.updo1 LDA #%00000000 \ Clear bits 6 and 7 of ditherObjectSights so the next STA ditherObjectSights \ call to DrawUpdatedObject will, by default, draw the \ object straight onto the screen without a dithered \ effect, and will not remove the sights first STA drawLandscape \ Clear bit 7 of drawLandscape so the next call to \ DrawUpdatedObject will, by default, draw the landscape \ behind the object STA doNotDitherObject \ Clear bit 7 of doNotDitherObject to enable objects to \ be updated on the screen with a dithered effect RTS \ Return from the subroutine .DrawUpdatedObject JSR GetObjVisibility \ Calculate whether any part of the current object is \ visible on-screen and set the C flag as follows: \ \ * Clear = at least some of the object is visible \ on-screen \ \ * Set = the object is not visible on-screen \ \ If the object is visible, also set the following: \ \ * bufferColumns = the number of character columns \ in the screen buffer that the object spans (capped \ to a maximum value of 20) \ \ * objYawOffset = the yaw offset of the left edge of \ the object from the left edge of the screen, in \ character columns BCS updo1 \ If the C flag is set then the object is not visible \ on-screen, so jump to updo1 to reset the various \ dither-related variables and return from the \ subroutine as there is nothing to update on the screen LDA #25 \ Set ditherOuterLoop = 25, so the dithered effect in STA ditherOuterLoop \ the DitherScreenBuffer routine implements 25 outer \ loops, where each inner loop dithers 255 pixels to the \ screen LDA ditherObjectSights \ If bit 7 of ditherObjectSights is clear then we are BPL updo2 \ not configured to remove the sights before updating \ the object on-screen, so jump to updo2 to skip the \ following SEI \ Disable interrupts to ensure that the screen doesn't \ scroll while we are removing the sights, as that could \ corrupt the screen JSR RemoveSights \ Bit 7 of ditherObjectSights is set, so we need to \ remove the sights from the screen before we update the \ object on-screen, so call the RemoveSights routine CLI \ Re-enable interrupts .updo2 LDX viewingObject \ Set X to the viewing object LDA objYawOffset \ Set yawAdjustment(Hi Lo) = (objYawOffset 0) / 2 STA yawAdjustmentHi \ LDA #0 \ This converts the yaw offset in objYawOffset from LSR yawAdjustmentHi \ character columns into a yaw angle and stores the ROR A \ result in yawAdjustment(Hi Lo) STA yawAdjustmentLo \ \ The custom screen mode 5 used by the game is 40 \ character columns wide, and the screen is 20 yaw \ angles wide in terms of the game's coordinate system \ \ So we can simply halve the character column count in \ objYawOffset to convert it into a yaw angle LDA yawAdjustmentHi \ Add yawAdjustmentHi to the yaw angle for the viewing ADC objectYawAngle,X \ object, so the viewing object is now looking directly STA objectYawAngle,X \ at the left edge of the object, so when we draw the \ object, we will draw it into the screen buffer with \ the object aligned with the left edge of the buffer \ \ The addition works because the ROR instruction clears \ the C flag, as A is zero before the rotation LDY #0 \ Zero lastPanKeyPressed so that when we draw the screen STY lastPanKeyPressed \ buffer onto the screen below, we draw the buffer onto \ the screen in columns, working from left to right, in \ the same way as for a pan to the right (which is what \ a zero value of lastPanKeyPressed usually represents) LDA bufferColumns \ Set up the screen buffer so it will hold an object JSR ConfigureObjBuffer \ with the number of character columns that the object \ spans, as calculated by the call to GetObjVisibility \ above \ \ The object might be too wide to fit into the screen \ buffer (as we configure it as a column buffer to make \ sure the entire visible part of the object fits into \ the buffer height-wise), but this ensures that the \ buffer contains as much of the visible part of the \ object as possible LDA #25 \ Set A = 25 to pass to FillScreen, so we fill the \ screen buffer (as opposed to screen memory) LDY #24 \ Set Y = 24 to pass to FillScreen, so we fill 24 \ character rows of the screen buffer LDX bufferColumns \ Set X = bufferColumns to pass to FillScreen, so we \ fill bufferColumns character columns in the screen \ buffer JSR FillScreen \ Call FillScreen to fill the screen buffer with the \ background specified in screenBackground \ \ If we are updating an object as part of the gameplay, \ then screenBackground variable will have been zeroed \ in DrawTitleView before the gameplay starts, so this \ call to FillScreen will fill the buffer with \ alternating colour 0/1 (blue/black) pixel rows, for \ the sky \ \ If we are drawing an object on the game over screen \ then screenBackground will be 3 and the background \ will be a black screen with stars BIT drawLandscape \ If bit 7 of drawLandscape is clear then we need to BPL updo3 \ draw both the object and the surrounding landscape, so \ jump to updo3 to do this \ If bit 7 of drawLandscape is set then we are showing \ the game over screen, so we just need to draw the \ object on its own and without the surrounding \ landscape LDY currentObject \ Draw the current object into the screen buffer JSR DrawObject JMP updo4 \ Jump to updo4 to skip the following instruction and \ move on to updating the screen .updo3 JSR DrawLandscapeView \ Draw the object into the screen buffer, as part of the \ landscape view .updo4 \ By this point we have drawn the updated object into \ the screen buffer, so we now need to copy the results \ onto the screen LDY #0 \ Set screenBufferAddr(1 0) to the address of the left JSR SetBufferAddress \ column of the screen buffer LDX viewingObject \ Set X to the viewing object LDA #0 \ Set yawAdjustmentLo = 0 so when we draw the landscape STA yawAdjustmentLo \ again, it doesn't include the yaw adjustment for \ drawing this object SEC \ Subtract yawAdjustmentHi from the yaw angle for the LDA objectYawAngle,X \ viewing, so the reverses the addition that we did SBC yawAdjustmentHi \ above so the yaw angle of the viewing object is back STA objectYawAngle,X \ to the value it has for the normal landscape view \ We now calculate the screen address of the left edge \ of the object in the landscape view, as that's where \ we want to copy the contents of the screen buffer \ \ The landscape view is at viewScreenAddr(1 0) in screen \ memory, and the number of character columns between \ the left edge of the screen and the left edge of the \ object is in objYawOffset, so now we convert the \ character columns into screen memory bytes and add \ that to viewScreenAddr(1 0), and that gives us the \ address in screen memory of the top-left corner of the \ object's position on-screen LDA #0 \ Set (U A) = objYawOffset STA U LDA objYawOffset ASL A \ Set (U A) = (U A) * 8 ASL A \ = objYawOffset * 8 ASL A \ ROL U \ This converts the yaw offset in objYawOffset from \ character columns into pixel bytes and stores the \ result in (U A), as each character block contains \ eight bytes \ \ We don't have to ROL the first two shifts into U as \ we know objYawOffset is less than 40, so the first two \ ASLs will only ever shift zeroes into the C flag CLC \ Set (A objScreenAddr) = viewScreenAddr(1 0) + (U A) ADC viewScreenAddr STA objScreenAddr LDA viewScreenAddr+1 ADC U CMP #&80 \ If the high byte in A >= &80 then the new address is BCC updo5 \ 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 .updo5 STA objScreenAddr+1 \ Store the high byte of the result, so we now have: \ \ objScreenAddr(1 0) = viewScreenAddr(1 0) + (U A) \ \ with the address wrapped around as required \ \ So this gives us the address in screen memory of the \ top-left corner of the object's position on-screen BIT ditherObjectSights \ If bit 6 of ditherObjectSights is clear then we do not BVC updo7 \ draw the object on-screen with a dithered effect, so \ jump to updo7 to skip the following and move straight \ on to drawing the whole object to the screen BIT sentinelHasWon \ If bit 7 of sentinelHasWon is clear then the Sentinel BPL updo6 \ has not won the game, so skip the following to leave \ ditherOuterLoop set to 25, which is the correct amount \ of time to spend dithering when updating objects on \ landscape during gameplay LDA #40 \ The Sentinel has won and we are therefore drawing the STA ditherOuterLoop \ game over screen rather than updating an object in the \ landscape, so set ditherOuterLoop = 40 to make the \ dithered effect in the DitherScreenBuffer routine last \ for 40 outer loops rather than 25, where each inner \ loop dithers 255 pixels to the screen .updo6 JSR DitherScreenBuffer \ Dither the contents of the screen buffer onto the \ screen \ \ Note that this process does not update the entire \ object on-screen, so we will need to do that at updo7 \ below once we have finished dithering LDA sentinelHasWon \ If bit 7 of sentinelHasWon is set then the Sentinel BMI updo11 \ has won the game and we are therefore drawing the game \ over screen and we don't want to draw the victorious \ enemy completely, so jump to updo11 to skip the part \ where the object is fully drawn on-screen .updo7 \ By this point we have either finished dithering the \ updated object onto the screen (if dithering was \ configured), or dithering was not configured \ \ In either case we now need to draw the whole object \ onto the screen SEI \ Disable interrupts to ensure that the screen doesn't \ scroll while we are removing the sights, as that could \ corrupt the screen JSR RemoveSights \ Remove the sights from the screen before we update the \ object on-screen by calling the RemoveSights routine SEC \ Set bit 7 of doNotDrawSights so the DrawSights routine ROR doNotDrawSights \ does not draw the sights on-screen going forward \ \ This prevents the interrupt handler from drawing the \ sights, in case it starts processing a sights-related \ key press while we still are doing the updating CLI \ Re-enable interrupts LDA objScreenAddr \ Set toAddr(1 0) to the address of the object in screen STA toAddr \ memory which we put into objScreenAddr(1 0) above, so LDA objScreenAddr+1 \ we copy the screen contents from the screen buffer to STA toAddr+1 \ the correct place on-screen LDA bufferColumns \ We are now going to loop through the character columns STA loopCounter \ that we want to copy to the screen, so set a loop \ counter to the number of columns in bufferColumns, \ which we set to the column count for covering the \ whole object (or as many as we can fit into the column \ buffer, at least) JMP updo10 \ We are now ready to copy the configured number of \ character columns from the screen buffer to the \ screen, so jump into the following loop at updo10 .updo8 \ We have updated an on-screen character column, so now \ we move to the next character column to the right LDA objScreenAddr \ Set (A objScreenAddr) = objScreenAddr(1 0) + 8 CLC \ ADC #8 \ and (A toAddr) = objScreenAddr(1 0) + 8 STA objScreenAddr \ STA toAddr \ So this moves both the from and to addresses to the LDA objScreenAddr+1 \ next character column along ADC #0 CMP #&80 \ If the high byte in A >= &80 then the new address is BCC updo9 \ 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 .updo9 STA objScreenAddr+1 \ Store the high byte of the result, so we now have: \ \ objScreenAddr(1 0) = objScreenAddr(1 0) + 8 \ \ with the address wrapped around as required STA toAddr+1 \ Store the high byte of the result, so we now have: \ \ toAddr(1 0) = toAddr(1 0) + 8 \ \ with the address wrapped around as required .updo10 \ This is where we enter the screen-updating loop LDY lastPanKeyPressed \ Set Y to the value of lastPanKeyPressed, which we set \ to zero above \ \ Zero is normally the value for when we are panning the \ screen to the right, but we use it here because when \ we pan to the right, we draw the screen buffer onto \ the screen in columns, working from left to right, and \ that's also what we want to do when updating the \ object JSR ShowScreenBuffer \ Update the player's scrolling landscape view by \ copying the relevant parts of the screen buffer into \ screen memory DEC loopCounter \ Decrement the loop counter to move on to the next \ character column in the updated object BNE updo8 \ Loop back to keep drawing character columns from the \ screen buffer until we have drawn them all .updo11 \ We now need to check whether we have updated the whole \ object on-screen \ \ The column buffer is 24 character rows tall and up to \ 20 character columns wide, so if the on-screen object \ wider than 20 character columns (i.e. wider than half \ a screen width), then we can't fit the whole object \ into the column buffer in one go, so we need to draw \ it in two stages LDA objYawOffset \ Set objYawOffset = objYawOffset + bufferColumns CLC \ ADC bufferColumns \ This sets the yaw offset to the character column to STA objYawOffset \ the right of the on-screen block that we just drew \ to update the object LDA objYawWidth \ Set A = objYawWidth - bufferColumns SEC \ SBC bufferColumns \ We just drew bufferColumns character columns of the \ object and objYawWidth contains the full width of the \ on-screen object in character columns, so A now \ contains the number of character columns that we still \ have to draw BEQ updo12 \ If A = 0 then we have drawn the whole object, so jump \ to updo12 to finish off \ If we get here then A is non-zero so we still have \ more of the object to update on-screen, so we update \ the variables and loop back to draw the next set of \ 20 character columns to the right, dithering them in \ the same way if required STA objYawWidth \ Set the width of the on-screen object to A so it \ contains the number of character columns we still have \ to draw STA bufferColumns \ Configure the buffer width to the correct size for the \ second batch of character columns STA ditherOuterLoop \ Set ditherOuterLoop to A, so the dithered effect in \ the DitherScreenBuffer routine implements A outer \ loops, where each inner loop dithers 255 pixels to the \ screen JMP updo2 \ Jump back to updo2 to update the second part of the \ on-screen object .updo12 LSR doNotDrawSights \ Clear bit 7 of doNotDrawSights so the DrawSights \ routine is enabled once again LDA sightsAreVisible \ If bit 7 of sightsAreVisible is clear then the sights BPL updo13 \ are not currently being shown on-screen, so jump to \ updo13 to skip the following SEI \ Disable interrupts to ensure that the screen doesn't \ scroll while we are drawing the sights, as that could \ corrupt the screen JSR DrawSights \ Bit 7 of sightsAreVisible is set, so we need to draw \ the sights that we removed when updating the object, \ so call DrawSights to put them back CLI \ Re-enable interrupts .updo13 JMP updo1 \ We have finished updating the object, so jump to updo1 \ to reset the various dither-related variables and \ return from the subroutineName: DrawUpdatedObject [Show more] Type: Subroutine Category: Drawing objects Summary: Draw an updated object on-screen, optionally with a dithered effect, and with or without the surrounding landscapeContext: See this subroutine in context in the source code References: This subroutine is called as follows: * ApplyTactics (Part 8 of 8) calls DrawUpdatedObject * ProcessGameplay calls DrawUpdatedObject * ShowGameOverScreen calls DrawUpdatedObject
Arguments: currentObject The number of the object to update on-screen viewingObject The number of the object that's viewing the object that we are updating drawLandscape Configure whether to draw the landscape behind the object: * Bit 7 clear = draw the landscape behind the object as well as the object * Bit 7 set = just draw the object, and configure the interrupt handler to draw random black dots on the screen if the Sentinel has won the game ditherObjectSights Configure how the update is drawn onto the screen: * Bit 6 set = dither the updated object onto the screen * Bit 7 set = remove the sights from the screen before drawing the object screenBackground The screen background to use when drawing the object: * 0 = fill with alternating colour 0/1 (blue/black) pixel rows, for the sky during gameplay * 3 = fill with solid colour 1 (black) and draw 240 randomly positioned stars on the background, for the game over screen
[X]
Subroutine ConfigureObjBuffer (category: Screen buffer)
Set up the variables required to configure the screen buffer for updating an object
[X]
Subroutine DitherScreenBuffer (category: Screen buffer)
Dither the contents of the screen buffer onto the screen
[X]
Subroutine DrawLandscapeView (Part 1 of 3) (category: Drawing the landscape)
Set up a number of variables for drawing the landscape view
[X]
Subroutine DrawObject (category: Drawing objects)
Draw a 3D object
[X]
Subroutine DrawSights (category: Sights)
Draw the sights on the screen, saving the existing screen contents in the sights pixel byte stash
[X]
Subroutine FillScreen (category: Graphics)
Fill a rectangular in screen memory or the screen buffer with the specified background
[X]
Subroutine GetObjVisibility (category: Gameplay)
Calculate whether any part of an object is visible on-screen, and if so, which character columns it spans on the screen
[X]
Subroutine RemoveSights (category: Sights)
Remove the sights from the screen
[X]
Subroutine SetBufferAddress (category: Screen buffer)
Set screenBufferAddr(1 0) to the address from which the interrupt handler should fetch new content to scroll onto the screen
[X]
Subroutine ShowScreenBuffer (category: Screen buffer)
Update the player's scrolling landscape view by copying the relevant parts of the screen buffer into screen memory
[X]
Variable bufferColumns in workspace Main variable workspace
The number of character columns in the current screen buffer
[X]
Variable currentObject in workspace Zero page
The number of the object we are currently processing
[X]
Variable ditherObjectSights in workspace Main variable workspace
Configures how an updated object is drawn onto the screen in the DrawUpdatedObject routine
[X]
Variable ditherOuterLoop (category: Screen buffer)
A counter for the inner loop when dithering objects to the screen in the DitherScreenBuffer routine
[X]
Variable doNotDitherObject in workspace Main variable workspace
Controls whether the DitherScreenBuffer routine can update an object on the screen using a dithered effect
[X]
Variable doNotDrawSights in workspace Main variable workspace
A flag that controls whether we draw the sights in the DrawSights routine
[X]
Variable drawLandscape in workspace Main variable workspace
Configures whether to draw the landscape behind the object in the DrawUpdatedObject routine
[X]
Variable lastPanKeyPressed in workspace Zero page
The direction of the last pan key that was pressed (which may not still be held down)
[X]
Variable loopCounter in workspace Zero page
A general-purpose loop counter
[X]
Variable objScreenAddr (category: Screen buffer)
The screen address of the object we are updating
[X]
Variable objYawOffset in workspace Main variable workspace
The yaw offset of the left edge of the object being analysed in GetObjVisibility, relative to the left edge of the screen, in on-screen character columns
[X]
Variable objYawWidth in workspace Main variable workspace
The width of the visible portion of the object being analysed in GetObjVisibility, in on-screen character columns
[X]
Variable objectYawAngle (category: 3D objects)
The yaw angle for each object (i.e. the horizontal direction in which they are facing)
[X]
Variable sentinelHasWon in workspace Main variable workspace
A flag to record when the player runs out of energy (i.e. the energy level goes negative), at which point the Sentinel wins
[X]
Variable sightsAreVisible in workspace Main variable workspace
Controls whether the sights are being shown
[X]
Label updo1 is local to this routine
[X]
Label updo10 is local to this routine
[X]
Label updo11 is local to this routine
[X]
Label updo12 is local to this routine
[X]
Label updo13 is local to this routine
[X]
Label updo2 is local to this routine
[X]
Label updo3 is local to this routine
[X]
Label updo4 is local to this routine
[X]
Label updo5 is local to this routine
[X]
Label updo6 is local to this routine
[X]
Label updo7 is local to this routine
[X]
Label updo8 is local to this routine
[X]
Label updo9 is local to this routine
[X]
Variable viewScreenAddr in workspace Main variable workspace
The screen address of the player's scrolling landscape view, which is just below the icon and scanner row at the top of the screen
[X]
Variable viewingObject in workspace Zero page
The number of the viewing object
[X]
Variable yawAdjustmentHi (category: Screen buffer)
The yaw offset of the left edge of the object being updated, relative to the left edge of the screen, in yaw angles
[X]
Variable yawAdjustmentLo in workspace Zero page
A yaw adjustment to apply to the landscape drawing process so that when we update objects on-screen, we can yaw the viewer's gaze to the right to move the left edge of the object to the left edge of the screen so the object will fit into the screen buffer as efficiently as possible (low byte)