Find a Pixel

z80 » Graphics

Find Pixel is a small routine that sets the basis for plotting pixels on the screen. It takes the x and y coordinates of where you want to put a pixel and transforms those coordinates into an address and a bit offset. The address it gives you is for the byte that that pixel coordinate is in the Video Memory.

Some routines give you this address relative to zero being the top left of the screen. You might have to add $fc00 to the address outputted in order to get the exact address in the Video Memory. The bit offset is stored as a bit set in the accumulator (a register). The bit cooresponds to the pixel you want. If you set the same bit in a as the address in the video memory given by hl, you will have a little black dot at the coordinates you want on the screen. Most routines accept the x coordinate in b and the y coordinate in c.

I've pasted Clem Vasseur's FindPixel.asm Routine here. You can download this along with many other Find Pixel routines that I've come across in the Download Section. Each line in the routine is represented by a number which corresponds to an explanation at the end of the routine. Follow each line and try to picture in your head what it is doing.

  1. FindPixel:
  2. ld h,63
  3. ld a,c
  4. add a,a
  5. add a,a
  6. ld l,a
  7. ld a,b
  8. rra
  9. add hl,hl
  10. rra
  11. add hl,hl
  12. rra
  13. or l
  14. ld l,a
  15. ld a,b
  16. and %00000111
  17. ld bc,FP_Bits
  18. add a,c
  19. ld c,a
  20. adc a,b
  21. sub c
  22. ld b,a
  23. ld a,(bc)
  24. ret
  25. FP_Bits:
  26. .db %10000000
  27. .db %01000000
  28. .db %00100000
  29. .db %00010000
  30. .db %00001000
  31. .db %00000100
  32. .db %00000010
  33. .db %00000001

Line(s) Explanation Bytes
2 This routine is great because it knows we are going to want the address relative to the Video Memory ($fc00) and not relative to zero ($0000). We are going to be multiplying hl times 4. We can think ahead and see that 63*4 converted to hexadecimal is $fc. That's the Most Significant Byte (MSB) of what we want. This will handle for us the adding of the final address to $fc00. The multiplying by 4 will come in lines 9 and 11. 2
3 We need to get the y coordinate and multiply it by 16 (or 24) so we move down 16 bytes (one row) until we're at the row designated by the y coordinate. We can't just do 16*c. We can square the y coordinate 4 times so we have it times 16. We are going to use 'add a,a' for the first 2 squares because we know we won't exceed 256 with just 2 multiplies since the maximum y value is 64 (64*2*2=256). 'Add a,a' is over two times faster than 'add hl,hl' which we will have to use later. 1
4-5 Now we've got to start multiplying the y coordinate (which was in c) by 16. It's nice that the screen is 16 bytes across because that way it's a power of 2 so we can figure this stuff out fast with just shifting bits over to the right once. 2
6 We put what we've just got into the Least Significant Byte (LSB) of our final address to be stored in hl. 1
7 Now's time to start working with the x coordinate stored in b. Since it's in terms of pixels (or bits in each pixel) we need to think of each byte containing 8 pixels. We need to divide the x coordinate by 8 to account for each of the 8 pixels in a byte. 1
8 If this is unfamiliar to you, refer to the Shift and Rotate Section to get a quick lesson. It rotates the bits one to the right with bit 0 being moved to bit 7 and also copied into the carry flag. This also acts as dividing by 2 since we shift over by a power of 2 (21=2). This is the same as lines 10 and 12. 1
9 We need to perform the third multiplication of the y coordinate by 2. This is the same as line 11. 1
10 See explanation for line 8. 1
11 See explanation for line 9. 1
12 See explanation for line 8. 1
13-14 This adds in the result we just found with whatever is currently in the LSB of hl (l). Then we put that result into the LSB. We now have the final address in the Video Memory in hl ready to be used. 2
15 Out next task is to figure out the bit offset needed in a. Let's get back out x coordinate from b and work with it again. 1
16 We have already figured out which byte the pixel is in. The remainder of after that calculation tells us where in that byte. The remainder would be the bits we shifted out the right, the last 3 bits (%00000111). If the remainder were 3, then we would know that the pixel is the 3rd from the left. We want to mask off (get rid of) all the bits except for the ones that would be our remainder. 2
17 Lines 25 and following are a set of precalculated bit offsets. We use that table and add the remainder to that to get the correct offset. If the remainder were 3, then we would need the 3rd element in the table which is %00100000. 3
18-22 This is Simulated 16-bit Addition which will be covered later in more detail. It's faster than just doing ld h,0 / ld l,a / add hl,bc to get the offset in the table. It adds a to the LSB of hl, stores the result in the LSB, adds the result to the MSB with the carry and subtracts the LSB. The MSB is either increased by one (if the carry is set) or remains the same (if the carry is reset). 5
23 Put that element of the table into a. 1
24 Done with the routine so return to caller. 1
25-33 Here begins a table with all possible bit offsets for a. They are in binary to make it easier for viewing. See lines 15 and following to see how this is used. 8

More from z80 » Graphics
Find a Pixel // Grayscale // Pixel Manipulation // The Screen // Sprites // SDR8 Routine // Tile Maps // TileGen Routine