.DrawSights LDA doNotDrawSights \ If bit 7 of doNotDrawSights is set then we do not draw BMI dras2 \ the sights, so jump to dras12 via dras2 to return from \ the subroutine JSR RemoveSights \ First we remove the sights from the screen (if there \ are any sights on-screen), so if we are drawing the \ sights to move them, this removes the old sights \ \ This also sets sightsByteCount = 0 to reset the sights \ pixel byte stash \ We now set the coordinates of the "brush" that we're \ going to use to draw the sights \ \ The coordinates of the brush are stored relative to \ the top-left corner of the character block containing \ the top of the sights \ \ The current coordinate of the brush is stored as \ follows: \ \ * sightsByteAddr(1 0) contains the address of the \ character block in which we are dabbing our brush \ \ * The x-coordinate is stored in xSightsBrush in the \ form of a pixel offset within the character block \ pointed to by sightsByteAddr(1 0), so this is \ always in the range 0 to 3 \ \ * The y-coordinate is stored in Y in the form of a \ pixel row offset within the character block \ pointed to by sightsByteAddr(1 0), so this is \ always in the range 0 to 7 LDA xSights \ Set xSightsBrush as follows: AND #%00000011 \ STA xSightsBrush \ xSightsBrush = xSights mod 4 \ \ Each pixel byte contains four pixels, so this sets \ xSightsBrush to the x-coordinate of the sights within \ the pixel byte in which the sights appear, in the \ range 0 to 3 \ \ This gives us the x-coordinate of the starting point \ for the brush at the top of the sights, relative to \ the top-left corner of the character block containing \ the top of the sights LDA sightsScreenAddr \ Set Y to bits 0-2 of the address of the sights in AND #%00000111 \ screen memory, so Y contains the number of the pixel TAY \ row within the character block containing the sights \ \ This gives us the y-coordinate of the starting point \ for the brush at the top of the sights, relative to \ the top-left corner of the character block containing \ the top of the sights LDA sightsScreenAddr \ Set sightsByteAddr(1 0) to bits 3-15 of the address AND #%11111000 \ of the sights in screen memory, so it contains the STA sightsByteAddr \ address of the top-left corner of the character block LDA sightsScreenAddr+1 \ containing the top of the sights STA sightsByteAddr+1 \ We now draw the sights one step at a time, dabbing the \ brush on the screen for each step \ \ There are 12 steps when drawing the sights, and we \ draw one screen byte (four pixels) in each step, \ storing the original screen contents in the sights \ pixel byte stash, so we can easily remove the sights \ later \ \ We start by placing our brush at the top of the sights \ and dabbing the screen to draw the first pixel byte \ \ The top of the sights are at a relative coordinate of \ (xSightsBrush, Y) from the start of the character \ block of the sights, so this is where we start \ \ We then apply the steps from the xSightsStep and \ ySightsStep tables to the brush coordinates, dabbing \ the screen at each step, starting from step 0 and \ moving as follows (where steps 10 and 11 are shown as \ A and B): \ \ 00 \ 11 \ 22 \ 334455667788 \ 99 \ AA \ BB \ \ The current step number is stored in sightsByteCount, \ which also tracks the size of the sights pixel byte \ stash .dras1 \ We start by adding the next step to the y-coordinate \ of the brush and updating sightsByteAddr(1 0) and Y \ accordingly LDX sightsByteCount \ Set X to the current step number, which is also the \ current size of the sights pixel byte stash TYA \ Set A = Y + the X-th entry in ySightsStep CLC \ ADC ySightsStep,X \ So this applies the next step from the ySightsStep \ table to the relative y-coordinate of the brush in Y BPL dras3 \ The table at ySightsStep is terminated by %10000000, \ so if bit 7 of the result is clear then we haven't yet \ reached the end of the table, so jump to dras3 to keep \ drawing \ Otherwise we just read the terminator at the end of \ the ySightsStep table, so fall through into dras2 to \ return from the subroutine .dras2 JMP dras12 \ Jump to dras12 to return from the subroutine .dras3 CMP #8 \ Compare A and 8 and set the status flags for us to \ check below TAY \ Set Y to the updated value of A, so Y now contains the \ updated y-coordinate of the brush after applying the \ next step from ySightsStep BCC dras5 \ If A < 8 then the y-coordinate has not moved past the \ bottom of the current character row, so jump to dras5 \ to move on to processing the x-coordinate \ We have just stepped down into the next character row, \ so we need to recalculate the values in Y and \ sightsByteAddr(1 0) to point to the correct address \ in the next character row SBC #8 \ Set Y = Y - 8 TAY \ \ This resets Y to the number of the pixel row in the \ next character row down, as each character row is \ eight bytes high \ \ This subtraction works as we passed through a BCC \ above, so we know the C flag is set LDA sightsByteAddr \ Set (A sightsByteAddr) = sightsByteAddr(1 0) + &140 CLC \ = sightsByteAddr(1 0) + 320 ADC #&40 \ STA sightsByteAddr \ Each character row in screen mode 5 takes up 320 bytes LDA sightsByteAddr+1 \ (40 character blocks of eight bytes each), so this ADC #&01 \ sets sightsByteAddr(1 0) to the address of the next \ row down in screen memory CMP #&80 \ If the high byte in A >= &80 then the new address is BCC dras4 \ past the end of screen memory, so subtract &20 from SBC #&20 \ the high byte so the address wraps around within the \ range of screen memory between &6000 and &8000 .dras4 STA sightsByteAddr+1 \ Store the high byte of the result, so we now have: \ \ sightsByteAddr(1 0) = sightsByteAddr(1 0) + 320 \ \ with the address wrapped around as required .dras5 \ We now need to add the next step to the x-coordinate \ of the brush and update sightsByteAddr(1 0) and \ xSightsBrush accordingly LDA xSightsStep,X \ Set A to the next step from the xSightsStep that we \ need to apply to the relative x-coordinate in \ xSightsBrush BEQ dras9 \ If the step is zero then we don't need to change the \ x-coordinate in xSightsBrush, so jump to dras9 to \ move on to the actual drawing step CLC \ Set xSightsBrush = xSightsBrush + A ADC xSightsBrush \ STA xSightsBrush \ So this applies the next step from the xSightsStep \ table to the relative x-coordinate of the brush in \ xSightsBrush AND #%11111100 \ Set A = (A * 2) div 8 ASL A \ \ All the step values in xSightsStep and ySightsStep \ and the brush coordinates in xSightsBrush and Y are \ in pixels, with each step in the x-coordinate drawing \ two pixels, so these steps: \ \ 00 \ 11 \ 22 \ 334455667788 \ 99 \ AA \ BB \ \ end up drawing pixels like this: \ \ x. \ x. \ x. \ x.x.x.x.x.x. \ x. \ x. \ x. \ \ Screen mode 5 contains four pixels in each byte, so to \ convert the x-coordinate of the brush offset from step \ pixels into screen pixels, we have to double it \ \ We then apply div 8 to the result to give us the \ offset of the start of the character block containing \ the new position of the brush, as each character block \ contains eight bytes BPL dras6 \ If A is a negative value then to make the following DEC sightsByteAddr+1 \ addition work we would have to add (&FF A) to make \ the 16-bit addition work in signed 16-bit arithmetic, \ but to save having to do this we can simply decrement \ the high byte of sightsByteAddr(1 0) in advance and \ add (0 A) instead .dras6 CLC \ Set (A sightsByteAddr) = sightsByteAddr(1 0) + A ADC sightsByteAddr \ STA sightsByteAddr \ So this applies the step in A to sightsByteAddr(1 0) LDA sightsByteAddr+1 ADC #0 CMP #&60 \ If the high byte in A < &60 then the new address is BCS dras7 \ before the start of screen memory, so add &20 to the ADC #&20 \ high byte so the address wraps around within the range \ of screen memory between &6000 and &8000 JMP dras8 \ Jump to dras8 to skip the next check, as we know it \ doesn't apply .dras7 CMP #&80 \ If the high byte in A >= &80 then the new address is BCC dras8 \ past the end of screen memory, so subtract &20 from SBC #&20 \ the high byte so the address wraps around within the \ range of screen memory between &6000 and &8000 .dras8 STA sightsByteAddr+1 \ Store the high byte of the result, so we now have: \ \ sightsByteAddr(1 0) = sightsByteAddr(1 0) + A \ \ with the address wrapped around as required \ \ So this applies the next step to sightsByteAddr(1 0) \ to give us the screen address of the character block \ that we need to update to make the next brush stroke LDA xSightsBrush \ Set xSightsBrush as follows: AND #%00000011 \ STA xSightsBrush \ xSightsBrush = xSightsBrush mod 4 \ \ Each pixel byte contains four pixels, so this sets \ xSightsBrush to the x-coordinate of the sights within \ the pixel byte in which the sights appear, in the \ range 0 to 3 .dras9 \ We have now set up sightsByteAddr(1 0), xSightsBrush \ and Y to the address of the next pixel byte to paint, \ so let's dab the brush for this step \ \ The address of the pixel byte we need to paint is \ sightsByteAddr(1 0) + Y \ \ The offset of the pixel within that pixel byte (in \ terms of step pixels) is in xSightsBrush \ First, we need to save the current contents of the \ screen in the sights pixel byte stash by storing the \ screen address of the pixel in the X-th entry of \ sightsByteAddr(Hi Lo), and then storing the existing \ contents of that address to the X-th entry of \ sightsByte TYA \ Set the X-th entry in the sights pixel byte stash ORA sightsByteAddr \ address list at sightsByteAddr(Hi Lo) to STA sightsByteAddrLo,X \ sightsByteAddr(1 0) + Y, which is the address of LDA sightsByteAddr+1 \ the pixel byte we are about to change STA sightsByteAddrHi,X LDA (sightsByteAddr),Y \ Set A to the current screen contents of the pixel byte \ we are updating STA sightsByte,X \ Store the current screen contents in the X-th entry in \ the sights pixel byte stash at sightsByte \ We now apply the brush to the pixel byte in screen \ memory at sightsByteAddr(1 0) + Y \ \ We do this by taking the existing screen pixel byte in \ A and setting the pixel number xSightsBrush to either \ colour 1 or colour 2, depending on the current colour \ in that pixel \ \ Specifically, we draw the sights in colour 2 when the \ background is colour 0 or 1, and we draw the sights in \ colour 1 when the background is colour 2 or 3 \ \ As an example of physical colours, in landscape 0000 \ we paint the sights pixels in white (colour 2) when \ the background is blue (colour 0) or black (colour 1), \ and we paint the sights pixels in black (colour 1) \ when the background is white (colour 2) or green \ (colour 3) \ \ This ensures that the sights are visible wherever they \ are on-screen, irrespective of the current screen \ contents LDX xSightsBrush \ Convert xSightsBrush from a number in the range 0 to 3 AND pixelBitMask,X \ into a bit mask with that pixel number set (where \ pixel 0 is on the left and pixel 3 is on the right) \ \ So if xSightsBrush is 2, for example, the resulting \ mask will be %00100010, in which pixel 2 is set \ At this point, A contains the existing contents of the \ screen byte into which we need to draw the sights, \ with all bits zeroed except for those for the pixel we \ want to colour in CMP #%00010000 \ Set the status flags on the comparison of A and PHP \ %00010000 and push the resulting flags on the stack \ so we can retrieve them below LDA (sightsByteAddr),Y \ Set A to the current screen contents of the pixel byte \ we are updating AND clearPixelMask,X \ Clear the pixel number in xSightsBrush by applying a \ pixel bit mask from clearPixelMask where the bits for \ every pixel are set except for pixel X PLP \ Using the status flags that we put on the stack above, BCS dras10 \ check whether A >= %00010000, where A contains the \ existing contents of the screen for pixel X \ \ If A >= %00010000 then A must have at least one bit \ set in the high nibble, which means the existing \ contents of pixel X on-screen has to have its top bit \ set, so it must either be colour %10 (2) or %11 (3) \ \ As the background is colour 2 or 3, jump to dras10 to \ paint the sights pixel in colour 1 \ If we get here then the existing screen contents are \ in colour 0 or 1, so we now paint the sights pixel in \ colour 2 ORA pixelByteColour2,X \ Set the colour of pixel X to colour 2 by OR'ing in a \ pixel mask where pixel X is set to %10 for colour 2 BCC dras11 \ Jump to dras11 to skip the following instruction (this \ BCC is effectively a JMP as we just passed through a \ BCS) .dras10 ORA pixelByteColour1,X \ Set the colour of pixel X to colour 1 by OR'ing in a \ pixel mask where pixel X is set to %01 for colour 1 .dras11 STA (sightsByteAddr),Y \ Store the updated pixel byte in screen memory to apply \ the brush to the canvas INC sightsByteCount \ Increment sightsByteCount to move on to the next step \ in the drawing process JMP dras1 \ Loop back to draw the next step of the sights .dras12 RTS \ Return from the subroutineName: DrawSights [Show more] Type: Subroutine Category: Sights Summary: Draw the sights on the screen, saving the existing screen contents in the sights pixel byte stashContext: See this subroutine in context in the source code References: This subroutine is called as follows: * CheckForKeyPresses calls DrawSights * DrawUpdatedObject calls DrawSights * MoveSights calls DrawSights
[X]
Subroutine RemoveSights (category: Sights)
Remove the sights from the screen
[X]
Variable clearPixelMask (category: Graphics)
A table for converting a pixel number in the range 0 to 3 into a screen mode 5 bit mask with that pixel's bits clear and others set
[X]
Variable doNotDrawSights in workspace Main variable workspace
A flag that controls whether we draw the sights in the DrawSights routine
[X]
Label dras1 is local to this routine
[X]
Label dras10 is local to this routine
[X]
Label dras11 is local to this routine
[X]
Label dras12 is local to this routine
[X]
Label dras2 is local to this routine
[X]
Label dras3 is local to this routine
[X]
Label dras4 is local to this routine
[X]
Label dras5 is local to this routine
[X]
Label dras6 is local to this routine
[X]
Label dras7 is local to this routine
[X]
Label dras8 is local to this routine
[X]
Label dras9 is local to this routine
[X]
Variable pixelBitMask (category: Graphics)
A table for converting a pixel number in the range 0 to 3 into a screen mode 5 bit mask with that pixel's bits set and others clear
[X]
Variable pixelByteColour1 (category: Sights)
A table for converting a pixel number in the range 0 to 3 into a screen mode 5 pixel byte with that pixel set to colour 1 (%01)
[X]
Variable pixelByteColour2 (category: Graphics)
A table for converting a pixel number in the range 0 to 3 into a screen mode 5 pixel byte with that pixel set to colour 2 (%10)
[X]
Variable sightsByte (category: Sights)
The sights pixel byte stash, which contains the screen pixel bytes behind the sights, so they can be restored to remove the sights
[X]
Variable sightsByteAddr in workspace Zero page
Storage for an address that loops through the pixel bytes in screen memory where the sights appear, so the sights can be drawn and removed using the contents of the sights pixel byte stash
[X]
Variable sightsByteAddrHi (category: Sights)
The screen addresses of the bytes in the sights pixel byte stash, to which they can be restored to remove the sights (high byte)
[X]
Variable sightsByteAddrLo (category: Sights)
The screen addresses of the bytes in the sights pixel byte stash, to which they can be restored to remove the sights (low byte)
[X]
Variable sightsByteCount in workspace Main variable workspace
The number of screen bytes in the sights pixel byte stash that contain the contents of the screen behind the sights (so they can be restored to remove the sights)
[X]
Variable sightsScreenAddr in workspace Main variable workspace
The screen address of the sights
[X]
Variable xSights in workspace Main variable workspace
The pixel x-coordinate of the sights on-screen
[X]
Variable xSightsBrush in workspace Main variable workspace
The x-coordinate of the "brush" that we use to draw the sights, relative to the top-left corner of the character block containing the top of the sights
[X]
Variable xSightsStep (category: Sights)
Steps to take along the x-axis when drawing the sights
[X]
Variable ySightsStep (category: Sights)
Steps to take along the y-axis when drawing the sights