Skip to navigation

Bank 7 (Part 2 of 4)

[NES version]

Name: ClearMemory [Show more] Type: Subroutine Category: Utility routines Summary: Clear a block of memory, split across multiple calls if required
This routine clears a block of memory, but only if there are enough cycles in the cycle count. If it runs out of cycles, it will pick up where it left off when called again.
Arguments: clearAddress The address of the block to clear clearBlockSize The size of the block to clear as a 16-bit number, must be a multiple of 8 bytes
Returns: clearAddress The address of the next byte to clear in the block, ready for the next call (if the whole block was not cleared) clearBlockSize The size of the block, reduced by the number of bytes cleared in the current call, so it's ready for the next call (this will be 0 if this call cleared the whole block)
.ClearMemory LDA clearBlockSize+1 ; If the high byte of the block size is zero, then jump BEQ cmem8 ; to cmem8 to clear a block of fewer than 256 bytes ; If we get here then the high byte of the block size is ; non-zero, so the block we need to clear consists of ; one or more page-sized blocks (i.e. 256-byte blocks), ; as well as one block with fewer than 256 bytes ; ; We now concentrate on clearing the page-sized blocks, ; leaving the block with fewer than 256 bytes for the ; next VBlank ; First we consider whether we can clear a block of 256 ; bytes SUBTRACT_CYCLES 2105 ; Subtract 2105 from the cycle count BMI cmem1 ; If the result is negative, jump to cmem1 to consider ; clearing a 32-byte block in this VBlank, as we don't ; have enough cycles for a 256-byte block JMP cmem2 ; The result is positive, so we have enough cycles to ; clear a 256-byte block in this VBlank, so jump to ; cmem2 to do just that .cmem1 ADD_CYCLES 2059 ; Add 2059 to the cycle count JMP cmem3 ; Jump to cmem3 to consider clearing the block with ; fewer than 256 bytes .cmem2 LDA #0 ; Set A = 0 so the call to FillMemory zeroes the memory ; block LDY #0 ; Set an index in Y to pass to FillMemory, so we start ; clearing memory from clearAddress(1 0) onwards JSR FillMemory ; Call FillMemory to clear a whole 256-byte block of ; memory at clearAddress(1 0) DEC clearBlockSize+1 ; Decrement the high byte of clearBlockSize(1 0), which ; is the same as subtracting 256, as we just cleared 256 ; bytes of memory INC clearAddress+1 ; Increment the high byte of clearAddress(1 0) to point ; at the next 256-byte block of memory after the block ; we just cleared, so we clear that next JMP ClearMemory ; Jump back to ClearMemory to consider clearing the next ; 256 bytes of memory .cmem3 ; If we get here then we did not have enough cycles to ; send a 256-byte block ; Now we consider whether we can clear a block of 32 ; bytes SUBTRACT_CYCLES 318 ; Subtract 318 from the cycle count BMI cmem4 ; If the result is negative, jump to cmem4 to skip ; clearing the next 32-byte block in this VBlank, as we ; have run out of cycles (we will pick up where we left ; off in the next VBlank) JMP cmem5 ; The result is positive, so we have enough cycles to ; clear the next 32-byte block in this VBlank, so jump ; to cmem5 to do just that .cmem4 ADD_CYCLES 277 ; Add 277 to the cycle count JMP cmem7 ; Jump to cmem7 to return from the subroutine .cmem5 LDA #0 ; Set A = 0 so the call to FillMemory zeroes the memory ; block LDY #0 ; Set an index in Y to pass to FillMemory, so we start ; clearing memory from clearAddress(1 0) onwards JSR FillMemory32Bytes ; Call FillMemory to clear 32 bytes of memory from ; clearAddress(1 0) to clearAddress(1 0) + 31 LDA clearAddress ; Set clearAddress(1 0) = clearAddress(1 0) + 32 CLC ; ADC #32 ; So it points at the next memory location to clear STA clearAddress ; after the block we just cleared LDA clearAddress+1 ADC #0 STA clearAddress+1 JMP cmem3 ; Jump back to cmem3 to consider clearing the next 32 ; bytes of memory, which we can keep doing until we run ; out of cycles because we only get here if we don't ; have enough cycles for a 256-byte block, so the cycles ; will run out before we manage to clear eight blocks of ; 32 bytes .cmem6 ADD_CYCLES_CLC 132 ; Add 132 to the cycle count .cmem7 RTS ; Return from the subroutine .cmem8 ; If we get here then we need to clear a block of fewer ; than 256 bytes SUBTRACT_CYCLES 186 ; Subtract 186 from the cycle count BMI cmem9 ; If the result is negative, jump to cmem9 to skip ; clearing the block in this VBlank, as we have run out ; of cycles (we will pick up where we left off in the ; next VBlank) JMP cmem10 ; The result is positive, so we have enough cycles to ; clear the block in this VBlank, so jump to cmem10 ; to do just that .cmem9 ADD_CYCLES 138 ; Add 138 to the cycle count JMP cmem7 ; Jump to cmem7 to return from the subroutine .cmem10 LDA clearBlockSize ; Set A to the size of the block we need to clear, which ; is in the low byte of clearBlockSize(1 0) (as we only ; get here when the high byte of clearBlockSize(1 0) is ; zero) BEQ cmem6 ; If the block size is zero, then there are no bytes to ; clear, so jump to cmem6 to return from the subroutine LSR A ; Set A = clearBlockSize / 16 LSR A LSR A LSR A CMP cycleCount+1 ; If A >= high byte of cycleCount(1 0), then: BCS cmem12 ; ; clearBlockSize / 16 >= cycleCount(1 0) / 256 ; ; so: ; ; clearBlockSize >= cycleCount(1 0) / 16 ; ; If clearing each byte takes up to 16 cycles, then this ; means we can't clear the whole block in this VBlank, ; as we don't have enough cycles, so jump to cmem12 to ; consider clearing it in blocks of 32 bytes rather than ; all at once ; ; (I don't know why this calculation counts 16 cycles ; per byte, as it only takes 8 cycles for FILL_MEMORY ; to clear a byte; perhaps it's an overestimation to be ; safe and cater for all this extra logic code?) ; If we get here then we can clear the block of memory ; in one go ; First we subtract the number of cycles that we need to ; clear the memory block from the cycle count ; ; Each call to the FILL_MEMORY macro takes 8 cycles (6 ; for the STA (clearAddress),Y instruction and 2 for the ; INY instruction), so the total number of cycles we ; will take will be clearBlockSize(1 0) * 8, so that's ; what we subtract from the cycle count LDA #0 ; Set the high byte of clearBlockSize(1 0) = 0 (though STA clearBlockSize+1 ; this should already be the case) LDA clearBlockSize ; Set (A clearBlockSize+1) = clearBlockSize(1 0) ASL A ; Set (A clearBlockSize+1) = (A clearBlockSize+1) * 8 ROL clearBlockSize+1 ; = clearBlockSize(1 0) * 8 ASL A ROL clearBlockSize+1 ASL A ROL clearBlockSize+1 EOR #$FF ; Set cycleCount(1 0) = cycleCount(1 0) SEC ; + ~(A clearBlockSize+1) + 1 ADC cycleCount ; STA cycleCount ; = cycleCount(1 0) - (A clearBlockSize+1) LDA clearBlockSize+1 ; = cycleCount(1 0) - clearBlockSize(1 0) * 8 EOR #$FF ADC cycleCount+1 STA cycleCount+1 ; Next we calculate the entry point into the FillMemory ; routine that will fill clearBlockSize(1 0) bytes of ; memory ; ; FillMemory consists of 256 sequential FILL_MEMORY ; macros, each of which fills one byte, as follows: ; ; STA (clearAddress),Y ; INY ; ; The first instruction takes up two bytes while the INY ; takes up one, so each byte that FillMemory fills takes ; up three bytes of instruction memory ; ; The FillMemory routine ends with an RTS, and is ; followed by the ClearMemory routine, so we can work ; out the entry point for filling clearBlockSize bytes ; as follows: ; ; ClearMemory - 1 - (3 * clearBlockSize) ; ; The 1 is for the RTS, and each of the byte fills has ; three instructions ; ; So this is what we calculate next LDY #0 ; Set an index in Y to pass to FillMemory (which we call ; via the JMP (clearBlockSize) instruction below, so we ; start clearing memory from clearAddress(1 0) onwards STY clearBlockSize+1 ; Set the high byte of clearBlockSize(1 0) = 0 LDA clearBlockSize ; Store the size of the memory block that we want to PHA ; clear on the stack, so we can retrieve it below ASL A ; Set clearBlockSize(1 0) ROL clearBlockSize+1 ; = clearBlockSize(1 0) * 2 + clearBlockSize(1 0) ADC clearBlockSize ; = clearBlockSize(1 0) * 3 STA clearBlockSize ; LDA clearBlockSize+1 ; So clearBlockSize(1 0) contains the block size * 3 ADC #0 STA clearBlockSize+1 ; At this point the C flag is clear, as the high byte ; addition will never overflow, so this means the SBC ; in the following will subtract an extra 1 LDA #LO(ClearMemory) ; Set clearBlockSize(1 0) SBC clearBlockSize ; = ClearMemory - clearBlockSize(1 0) - 1 STA clearBlockSize ; = ClearMemory - (block size * 3) - 1 LDA #HI(ClearMemory) ; SBC clearBlockSize+1 ; So clearBlockSize(1 0) is the address of the entry STA clearBlockSize+1 ; point in FillMemory that fills clearBlockSize(1 0) ; bytes with zero, and we can now call it with this ; instruction: ; ; JMP (clearBlockSize) ; ; So calling cmem11 below will fill memory with the ; value of A, for clearBlockSize(1 0) bytes from ; clearAddress(1 0) + Y onwards ; ; We already set Y to 0 above, so it will start filling ; from clearAddress(1 0) onwards LDA #0 ; Set A = 0 so the call to FillMemory via the ; JMP (clearBlockSize) instruction zeroes the memory ; block JSR cmem11 ; Jump to cmem11 to call the correct entry point in ; FillMemory to clear the memory block, returning here ; when it's done PLA ; Set A to the size of the memory block that we want to ; clear, which we stored on the stack above CLC ; Set clearAddress(1 0) = clearAddress(1 0) + A ADC clearAddress ; STA clearAddress ; So it points at the next memory location to clear LDA clearAddress+1 ; after the block we just cleared ADC #0 STA clearAddress+1 RTS ; Return from the subroutine .cmem11 JMP (clearBlockSize) ; We set up clearBlockSize(1 0) to point to the entry ; point in FillMemory that will fill the correct number ; of bytes with zero, so this clears our memory block ; and returns to the PLA above using a tail call .cmem12 ; If we get here then we need to consider clearing the ; memory in blocks of 32 bytes rather than all at once ADD_CYCLES_CLC 118 ; Add 118 to the cycle count .cmem13 SUBTRACT_CYCLES 321 ; Subtract 321 from the cycle count BMI cmem14 ; If the result is negative, jump to cmem14 to skip ; clearing the block in this VBlank, as we have run out ; of cycles (we will pick up where we left off in the ; next VBlank) JMP cmem15 ; The result is positive, so we have enough cycles to ; clear the block in this VBlank, so jump to cmem15 ; to do just that .cmem14 ADD_CYCLES 280 ; Add 280 to the cycle count JMP cmem16 ; Jump to cmem16 to return from the subroutine .cmem15 LDA clearBlockSize ; Set A = clearBlockSize - 32 SEC SBC #32 BCC cmem17 ; If the subtraction underflowed, then we need to clear ; fewer than 32 bytes (as clearBlockSize < 32), so jump ; to cmem17 to do just that STA clearBlockSize ; Set clearBlockSize - 32 = A ; = clearBlockSize - 32 ; ; So clearBlockSize(1 0) is updated with the new block ; size, as we are about to clear 32 bytes LDA #0 ; Set A = 0 so the call to FillMemory32Bytes zeroes the ; memory block LDY #0 ; Set an index in Y to pass to FillMemory32Bytes, so we ; start clearing memory from clearAddress(1 0) onwards JSR FillMemory32Bytes ; Call FillMemory32Bytes to clear a 32-byte block of ; memory at clearAddress(1 0) LDA clearAddress ; Set clearAddress(1 0) = clearAddress(1 0) + 32 CLC ; ADC #32 ; So it points at the next memory location to clear STA clearAddress ; after the block we just cleared BCC cmem13 INC clearAddress+1 JMP cmem13 ; Jump back to cmem13 to consider clearing the next 32 ; bytes of memory .cmem16 RTS ; Return from the subroutine .cmem17 ; If we get here then we need to clear fewer than 32 ; bytes of memory ADD_CYCLES_CLC 269 ; Add 269 to the cycle count .cmem18 SUBTRACT_CYCLES 119 ; Subtract 119 from the cycle count BMI cmem19 ; If the result is negative, jump to cmem19 to skip ; clearing the block in this VBlank, as we have run out ; of cycles (we will pick up where we left off in the ; next VBlank) JMP cmem20 ; The result is positive, so we have enough cycles to ; clear the block in this VBlank, so jump to cmem20 ; to do just that .cmem19 ADD_CYCLES 78 ; Add 78 to the cycle count JMP cmem16 ; Jump to cmem16 to return from the subroutine .cmem20 LDA clearBlockSize ; Set A = clearBlockSize - 8 SEC SBC #8 BCC cmem22 ; If the subtraction underflowed, then we need to clear ; fewer than 8 bytes (as clearBlockSize < 8), so jump ; to cmem22 to return from the subroutine, as this means ; we have filled the whole block (as we only clear ; memory blocks in multiples of 8 bytes) STA clearBlockSize ; Set clearBlockSize - 8 = A ; = clearBlockSize - 8 ; ; So clearBlockSize(1 0) is updated with the new block ; size, as we are about to clear 8 bytes LDA #0 ; Set A = 0 so the FILL_MEMORY macro zeroes the memory ; block LDY #0 ; Set an index in Y to pass to the FILL_MEMORY macro, so ; we start clearing memory from clearAddress(1 0) ; onwards FILL_MEMORY 8 ; Fill eight bytes at clearAddress(1 0) + Y with A, so ; this zeroes eight bytes at clearAddress(1 0) and ; increments the index counter in Y LDA clearAddress ; Set clearAddress(1 0) = clearAddress(1 0) + 8 CLC ; ADC #8 ; So it points at the next memory location to clear STA clearAddress ; after the block we just cleared BCC cmem21 INC clearAddress+1 .cmem21 JMP cmem18 ; Jump back to cmem18 to consider clearing the next 8 ; bytes of memory .cmem22 ADD_CYCLES_CLC 66 ; Add 66 to the cycle count RTS ; Return from the subroutine
Name: WaitForPPUToFinish [Show more] Type: Subroutine Category: PPU Summary: Wait until the NMI handler has finished updating both bitplanes, so the screen is no longer refreshing
.WaitForPPUToFinish SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA bitplaneFlags ; Keep looping back to the start of the routine until AND #%01000000 ; bit 6 of the bitplane flags for bitplane 0 is clear BNE WaitForPPUToFinish LDA bitplaneFlags+1 ; Do the same for bitplane 1 AND #%01000000 BNE WaitForPPUToFinish ; We get here when both bitplanes have bit 6 clear, ; which means neither bitplane is configured to send ; nametable data to the PPU ; ; This means the screen has finished refreshing and ; there is no longer any nametable data that needs ; sending to the PPU, so we can return from the ; subroutine RTS ; Return from the subroutine
Name: FlipDrawingPlane [Show more] Type: Subroutine Category: Drawing the screen Summary: Flip the drawing bitplane
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls FlipDrawingPlane * DrawScrollFrames calls FlipDrawingPlane * DrawShipInBitplane calls FlipDrawingPlane * LAUN calls FlipDrawingPlane * LL164 calls FlipDrawingPlane * Main flight loop (Part 1 of 16) calls FlipDrawingPlane * RunDemoFlightLoop calls FlipDrawingPlane
.FlipDrawingPlane LDA drawingBitplane ; Set X to the opposite bitplane to the current drawing EOR #1 ; bitplane TAX JSR SetDrawingBitplane ; Set X as the new drawing bitplane, so this effectively ; flips the drawing bitplane between 0 and 1 JMP ClearDrawingPlane ; Jump to ClearDrawingPlane to clear the buffers for the ; new drawing bitplane, returning from the subroutine ; using a tail call
Name: SetDrawingBitplane [Show more] Type: Subroutine Category: Drawing the screen Summary: Set the drawing bitplane to a specified value
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlipDrawingPlane calls SetDrawingBitplane * SendViewToPPU calls SetDrawingBitplane * SetDrawingPlaneTo0 calls SetDrawingBitplane * SetupViewInNMI calls SetDrawingBitplane * TT66 calls SetDrawingBitplane

Arguments: X The new value of the drawing bitplane
.SetDrawingBitplane STX drawingBitplane ; Set the drawing bitplane to X LDA lastPattern,X ; Set the next free pattern number in firstFreePattern STA firstFreePattern ; to the number of the last pattern that was sent to the ; PPU for the new bitplane LDA nameBufferHiAddr,X ; Set the high byte of the nametable buffer for the new STA nameBufferHi ; bitplane in nameBufferHiAddr LDA #0 ; Set the low byte of pattBufferAddr(1 0) to zero (we STA pattBufferAddr ; will set the high byte in SetPatternBuffer below STA drawingPlaneDebug ; Set drawingPlaneDebug = 0 (though this value is never ; read, so this has no effect) ; Fall through into SetPatternBuffer to set the high ; bytes of the patten buffer address variables
Name: SetPatternBuffer [Show more] Type: Subroutine Category: Drawing the screen Summary: Set the high byte of the pattern buffer address variables
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT128 calls SetPatternBuffer

Arguments: X The bitplane whose pattern address we should use
.SetPatternBuffer LDA pattBufferHiAddr,X ; Set the high byte of pattBufferAddr(1 0) to the STA pattBufferAddr+1 ; correct address for the pattern buffer for bitplane X LSR A ; Set pattBufferHiDiv8 to the high byte of the pattern LSR A ; buffer address, divided by 8 LSR A STA pattBufferHiDiv8 RTS ; Return from the subroutine
Name: CopySmallBlock [Show more] Type: Subroutine Category: Utility routines Summary: An unused routine that copies a small number of pages in memory
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Arguments: V(1 0) Source address SC(1 0) Destination address X Number of pages of memory to copy
.CopySmallBlock LDY #0 ; Set an index counter in Y .cops1 LDA (V),Y ; Copy the Y-th byte from V(1 0) to SC(1 0) STA (SC),Y DEY ; Decrement the index counter BNE cops1 ; Loop back until we have copied a whole page of bytes INC V+1 ; Increment the high bytes of V(1 0) and SC(1 0) to INC SC+1 ; point to the next page in memory DEX ; Decrement the page counter BNE cops1 ; Loop back until we have copied X pages of memory RTS ; Return from the subroutine
Name: CopyLargeBlock [Show more] Type: Subroutine Category: Utility routines Summary: An unused routine that copies a large number of pages in memory
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Arguments: SC2(1 0) Source address SC(1 0) Destination address V The number of pages top copy in each set V+1 The number of sets, so we copy V * V+1 pages X Number of pages of memory to copy
.CopyLargeBlock LDY #0 ; Set an index counter in Y INC V ; Increment the page counter in V so we can use a BNE ; below to copy V pages INC V+1 ; Increment the page counter in V+1 so we can use a BNE ; below to copy V+1 sets of V pages .copl1 LDA (SC2),Y ; Copy the Y-th byte from SC2(1 0) to SC(1 0) STA (SC),Y INY ; Increment the index counter BNE copl2 ; If we haven't reached the end of the page, jump to ; copl2 to skip the following INC SC+1 ; Increment the high bytes of SC(1 0) and SC2(1 0) to INC SC2+1 ; point to the next page in memory .copl2 DEC V ; Loop back to repeat the above until we have copied V BNE copl1 ; pages DEC V+1 ; Loop back to repeat the above until we have copied V+1 BNE copl1 ; sets of V pages RTS ; Return from the subroutine
Name: WaitFor3xVBlank [Show more] Type: Subroutine Category: Utility routines Summary: Wait for three VBlanks to pass
Context: See this subroutine on its own page References: This subroutine is called as follows: * ResetScreen calls WaitFor3xVBlank
.WaitFor3xVBlank LDA PPU_STATUS ; Read the PPU_STATUS register, which clears the VBlank ; latch in bit 7, so the following loops will wait for ; three VBlanks in total .wait1 LDA PPU_STATUS ; Wait for the first VBlank to pass, which will set bit BPL wait1 ; 7 of PPU_STATUS (and reading PPU_STATUS clears bit 7, ; ready for the next VBlank) .wait2 LDA PPU_STATUS ; Wait for the second VBlank to pass BPL wait2 ; Fall through into WaitForVBlank to wait for the third ; VBlank before returning from the subroutine
Name: WaitForVBlank [Show more] Type: Subroutine Category: Utility routines Summary: Wait for the next VBlank to pass
Context: See this subroutine on its own page References: This subroutine is called as follows: * MakeSoundsAtVBlank calls WaitForVBlank
.WaitForVBlank LDA PPU_STATUS ; Wait for the next VBlank to pass BPL WaitForVBlank RTS ; Return from the subroutine
Name: MakeSoundsAtVBlank [Show more] Type: Subroutine Category: Sound Summary: Wait for the next VBlank and make the current sounds (music and sound effects)
Context: See this subroutine on its own page References: This subroutine is called as follows: * SendDataNowToPPU calls MakeSoundsAtVBlank * SendViewToPPU calls MakeSoundsAtVBlank

Returns: X X is preserved
.MakeSoundsAtVBlank TXA ; Store X on the stack, so we can retrieve it below PHA JSR WaitForVBlank ; Wait for the next VBlank to pass JSR MakeSounds_b6 ; Call the MakeSounds routine to make the current sounds ; (music and sound effects) PLA ; Restore X from the stack so it is preserved TAX RTS ; Return from the subroutine
Name: DrawMessageInNMI [Show more] Type: Subroutine Category: Drawing the screen Summary: Configure the NMI to send the portion of the screen that contains the in-flight message to the PPU (i.e. tile rows 22 to 24)
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChangeCmdrName calls DrawMessageInNMI * ChangeLetter calls DrawMessageInNMI * FlightLoop4To16 calls DrawMessageInNMI * InputName calls DrawMessageInNMI * PrintFlightMessage calls DrawMessageInNMI * SetSelectedSystem calls DrawMessageInNMI * YESNO calls DrawMessageInNMI
.DrawMessageInNMI JSR WaitForPPUToFinish ; Wait until both bitplanes of the screen have been ; sent to the PPU, so the screen is fully updated and ; there is no more data waiting to be sent to the PPU LDA firstFreePattern ; Tell the NMI handler to send pattern entries up to the STA lastPattern ; first free pattern, for both bitplanes STA lastPattern+1 LDA #88 ; Tell the NMI handler to send nametable entries from STA firstNameTile ; tile 88 * 8 = 704 onwards (i.e. from the start of tile ; row 22) LDA #100 ; Tell the NMI handler to send nametable entries up to STA lastNameTile ; tile 100 * 8 = 800 (i.e. up to the end of tile row 24) STA lastNameTile+1 ; in both bitplanes LDA #%11000100 ; Set both bitplane flags as follows: STA bitplaneFlags ; STA bitplaneFlags+1 ; * Bit 2 set = send tiles up to end of the buffer ; * Bit 3 clear = don't clear buffers after sending ; * Bit 4 clear = we've not started sending data yet ; * Bit 5 clear = we have not yet sent all the data ; * Bit 6 set = send both pattern and nametable data ; * Bit 7 set = send data to the PPU ; ; Bits 0 and 1 are ignored and are always clear ; ; The NMI handler will now start sending data to the PPU ; according to the above configuration, splitting the ; process across multiple VBlanks if necessary JMP WaitForPPUToFinish ; Wait until both bitplanes of the screen have been ; sent to the PPU, so the screen is fully updated and ; there is no more data waiting to be sent to the PPU, ; and return from the subroutine using a tail call
Name: DrawShipInBitplane [Show more] Type: Subroutine Category: Drawing ships Summary: Flip the drawing bitplane and draw the current ship in the newly flipped bitplane
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF calls DrawShipInBitplane * ESCAPE calls DrawShipInBitplane * PAS1 calls DrawShipInBitplane * TITLE calls DrawShipInBitplane
.DrawShipInBitplane JSR FlipDrawingPlane ; Flip the drawing bitplane so we draw into the bitplane ; that isn't visible on-screen JSR LL9_b1 ; Draw the current ship into the newly flipped drawing ; bitplane ; Fall through into DrawBitplaneInNMI to configure the ; NMI to send the drawing bitplane to the PPU
Name: DrawBitplaneInNMI [Show more] Type: Subroutine Category: Drawing the screen Summary: Configure the NMI to send the drawing bitplane to the PPU after drawing the box edges and setting the next free tile number Deep dive: Views and view types in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawScrollFrames calls DrawBitplaneInNMI * DrawSpaceViewInNMI calls DrawBitplaneInNMI * LAUN calls DrawBitplaneInNMI * LL164 calls DrawBitplaneInNMI * RunDemoFlightLoop calls DrawBitplaneInNMI
.DrawBitplaneInNMI LDA #%11001000 ; Set A so we set the drawing bitplane flags in ; SetDrawPlaneFlags as follows: ; ; * Bit 2 clear = send tiles up to configured numbers ; * Bit 3 set = clear buffers after sending data ; * Bit 4 clear = we've not started sending data yet ; * Bit 5 clear = we have not yet sent all the data ; * Bit 6 set = send both pattern and nametable data ; * Bit 7 set = send data to the PPU ; ; Bits 0 and 1 are ignored and are always clear ; ; This configures the NMI to send nametable and pattern ; data for the drawing bitplane to the PPU during VBlank ; Fall through into SetDrawPlaneFlags to set the ; bitplane flags, draw the box edges and set the next ; free tile number
Name: SetDrawPlaneFlags [Show more] Type: Subroutine Category: Drawing the screen Summary: Set the drawing bitplane flags to the specified value, draw the box edges and set the next free tile number Deep dive: Drawing vector graphics using NES tiles
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls SetDrawPlaneFlags * DrawSpaceViewInNMI calls SetDrawPlaneFlags * SendBitplaneToPPU calls SetDrawPlaneFlags * SetupViewInNMI calls SetDrawPlaneFlags
.SetDrawPlaneFlags PHA ; Store A on the stack, so we can retrieve them below ; when setting the new drawing bitplane flags JSR DrawBoxEdges ; Draw the left and right edges of the box along the ; sides of the screen, drawing into the nametable buffer ; for the drawing bitplane LDX drawingBitplane ; Set X to the drawing bitplane LDA firstFreePattern ; Tell the NMI handler to send pattern entries up to the STA lastPattern,X ; first free pattern, for the drawing bitplane in X PLA ; Retrieve A from the stack and set it as the value of STA bitplaneFlags,X ; the drawing bitplane flags RTS ; Return from the subroutine
Name: SendInventoryToPPU [Show more] Type: Subroutine Category: PPU Summary: Send X batches of 16 bytes from SC(1 0) to the PPU, for sending the inventory icon bar image
Context: See this subroutine on its own page References: This subroutine is called as follows: * SendViewToPPU calls SendInventoryToPPU

Arguments: X The number of batches of 16 bytes to send to the PPU SC(1 0) The address of the data to send
.SendInventoryToPPU LDY #0 ; Set Y as an index counter for the following block, ; which sends 16 bytes of data from SC(1 0) to the PPU, ; using Y as an index that starts at 0 and increments ; after each byte ; ; We repeat this process for X iterations ; We repeat the following code 16 times, so it sends ; one whole pattern of 16 bytes to the PPU (eight bytes ; for each bitplane) LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA (SC),Y ; Send the Y-th byte of SC(1 0) to the PPU and increment STA PPU_DATA ; the index in Y INY LDA SC ; Set SC(1 0) = SC(1 0) + 16 CLC ; ADC #16 ; Starting with the low bytes STA SC BCC smis1 ; And then the high bytes INC SC+1 .smis1 DEX ; Decrement the block counter in X BNE SendInventoryToPPU ; Loop back to the start of the subroutine until we have ; sent X batches of 16 bytes RTS ; Return from the subroutine
Name: TWOS [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made single-pixel character row bytes for the space view Deep dive: Drawing pixels in the NES version
Context: See this variable on its own page References: This variable is used as follows: * DrawHangarWallLine uses TWOS * DrawVerticalLine (Part 2 of 3) uses TWOS * DrawVerticalLine (Part 3 of 3) uses TWOS * LOIN (Part 3 of 7) uses TWOS * LOIN (Part 4 of 7) uses TWOS * LOIN (Part 5 of 7) uses TWOS * PIXEL uses TWOS

Ready-made bytes for plotting one-pixel points in the space view. See the PIXEL routine for details.
.TWOS EQUB %10000000 EQUB %01000000 EQUB %00100000 EQUB %00010000 EQUB %00001000 EQUB %00000100 EQUB %00000010 EQUB %00000001 EQUB %10000000 EQUB %01000000
Name: TWOS2 [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made double-pixel character row bytes for the space view Deep dive: Drawing pixels in the NES version
Context: See this variable on its own page References: This variable is used as follows: * DrawDash uses TWOS2

Ready-made bytes for plotting two-pixel points in the space view. See the PIXEL routine for details.
.TWOS2 EQUB %11000000 EQUB %11000000 EQUB %01100000 EQUB %00110000 EQUB %00011000 EQUB %00001100 EQUB %00000110 EQUB %00000011
Name: TWFL [Show more] Type: Variable Category: Drawing lines Summary: Ready-made character rows for the left end of a horizontal line in the space view
Context: See this variable on its own page References: This variable is used as follows: * HLOIN (Part 4 of 5) uses TWFL * HLOIN (Part 5 of 5) uses TWFL

Ready-made bytes for plotting horizontal line end caps in the space view. This table provides a byte with pixels at the left end, which is used for the right end of the line. See the HLOIN routine for details.
.TWFL EQUB %10000000 EQUB %11000000 EQUB %11100000 EQUB %11110000 EQUB %11111000 EQUB %11111100 EQUB %11111110
Name: TWFR [Show more] Type: Variable Category: Drawing lines Summary: Ready-made character rows for the right end of a horizontal line in the space view
Context: See this variable on its own page References: This variable is used as follows: * HLOIN (Part 2 of 5) uses TWFR * HLOIN (Part 5 of 5) uses TWFR

Ready-made bytes for plotting horizontal line end caps in the space view. This table provides a byte with pixels at the right end, which is used for the left end of the line. See the HLOIN routine for details.
.TWFR EQUB %11111111 EQUB %01111111 EQUB %00111111 EQUB %00011111 EQUB %00001111 EQUB %00000111 EQUB %00000011 EQUB %00000001
Name: yLookupLo [Show more] Type: Variable Category: Drawing pixels Summary: Lookup table for converting pixel y-coordinate to tile number (low byte)
The NES screen mode is made up of 8x8-pixel tiles, with 32 tiles (256 pixels) across the screen, and either 30 tiles (240 pixels) or 28 tiles (224 pixels) vertically, for PAL or NTSC. This lookup table converts a pixel y-coordinate into the number of the first tile on the row containing the pixel. Pixel coordinate (0, 0) is mapped to the top-left pixel of the third row of tiles in the nametable, and the first column of tiles is at column 1 rather than 0 (as the screen is scrolled horizontally by 8 pixels via PPU_SCROLL), so pixel y-coordinates 0 to 7 are mapped to tile 65 (i.e. 2 * 32 + 1), pixel y-coordinates 8 to 15 are mapped to tile 97 (i.e. 3 * 32 + 1), and so on.
.yLookupLo FOR I%, 16, 239 EQUB LO((I% DIV 8) * 32 + 1) NEXT
Name: yLookupHi [Show more] Type: Variable Category: Drawing pixels Summary: Lookup table for converting pixel y-coordinate to tile number (high byte)
The NES screen mode is made up of 8x8-pixel tiles, with 32 tiles (256 pixels) across the screen, and either 30 tiles (240 pixels) or 28 tiles (224 pixels) vertically, for PAL or NTSC. This lookup table converts a pixel y-coordinate into the number of the first tile on the row containing the pixel. Pixel coordinate (0, 0) is mapped to the top-left pixel of the third row of tiles in the nametable, and the first column of tiles is at column 1 rather than 0 (as the screen is scrolled horizontally by 8 pixels via PPU_SCROLL), so pixel y-coordinates 0 to 7 are mapped to tile 65 (i.e. 2 * 32 + 1), pixel y-coordinates 8 to 15 are mapped to tile 97 (i.e. 3 * 32 + 1), and so on.
.yLookupHi FOR I%, 16, 239 EQUB HI((I% DIV 8) * 32 + 1) NEXT
Name: GetRowNameAddress [Show more] Type: Subroutine Category: Drawing the screen Summary: Get the addresses in the nametable buffers for the start of a given character row
This routine returns the index of the start of a text row in the nametable buffers. Character row 0 (i.e. YC = 0) is mapped to the second row on-screen, as the first row is taken up by the box edge. It's also worth noting that the first column in the nametable is column 1, not column 0, as the screen has a horizontal scroll of 8, so the leftmost tile on each row is scrolled around to the right side. This means that in terms of tiles, column 1 is the left edge of the screen, then columns 2 to 31 form the body of the screen, and column 0 is the right edge of the screen.
Arguments: YC The text row
Returns: SC(1 0) The address in nametable buffer 0 for the start of the row SC2(1 0) The address in nametable buffer 1 for the start of the row
.GetRowNameAddress LDA #0 ; Set SC+1 = 0, for use at the top byte of SC(1 0) in STA SC+1 ; the calculation below LDA YC ; If YC = 0, then we need to return the address of the BEQ grow1 ; start of the top character row (i.e. the second row ; on-screen), so jump to grow1 LDA YC ; Set A = YC + 1 CLC ; ADC #1 ; So this is the nametable row number for text row YC, ; as nametable row 0 is taken up by the top box edge ASL A ; Set SC(1 0) = (SC+1 A) << 5 + 1 ASL A ; = (0 A) << 5 + 1 ASL A ; = (YC + 1) * 32 + 1 ASL A ; ROL SC+1 ; This sets SC(1 0) to the offset within the nametable SEC ; of the start of the relevant row, as there are 32 ROL A ; tiles on each row ROL SC+1 ; STA SC ; The YC + 1 part skips the top on-screen row to start ; just below the top box edge, and the final + 1 takes ; care of the horizontal scrolling, which makes the ; first column number 1 rather than 0 ; ; The final ROL SC+1 also clears the C flag, as we know ; bits 1 to 7 of SC+1 were clear before the rotation STA SC2 ; Set the low byte of SC2(1 0) to the low byte of ; SC(1 0), as the addresses of the two nametable buffers ; only differ in the high bytes LDA SC+1 ; Set SC(1 0) = SC(1 0) + nameBuffer0 ADC #HI(nameBuffer0) ; STA SC+1 ; So SC(1 0) now points to the row's address in ; nametable buffer 0 (this addition works because we ; know that the C flag is clear and the low byte of ; nameBuffer0 is zero) ; ; This addition will never overflow, as we know SC+1 is ; in the range 0 to 3, so this also clears the C flag ; Each nametable buffer is 1024 bytes in size, which is ; four pages of 256 bytes, and nametable buffer 1 is ; straight after nametable buffer 0 in memory, so we can ; calculate the row's address in nametable buffer 1 in ; SC2(1 0) by simply adding 4 to the high byte ADC #4 ; Set SC2(1 0) = SC(1 0) + (4 0) STA SC2+1 ; ; So SC2(1 0) now points to the row's address in ; nametable buffer 1 (this addition works because we ; know that the C flag is clear RTS ; Return from the subroutine .grow1 ; If we get here then we want to return the address of ; the top character row (as YC = ), which is actually ; the second on-screen row (row 1), as the first row is ; taken up by the top of the box LDA #HI(nameBuffer0+1*32+1) ; Set SC(1 0) to the address of the tile in STA SC+1 ; column 1 on tile row 1 in nametable buffer 0 LDA #LO(nameBuffer0+1*32+1) STA SC LDA #HI(nameBuffer1+1*32+1) ; Set SC(1 0) to the address of the tile in STA SC2+1 ; column 1 on tile row 1 in nametable buffer 1 LDA #LO(nameBuffer1+1*32+1) STA SC2 RTS ; Return from the subroutine
Name: LOIN (Part 1 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Calculate the line gradient in the form of deltas Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * BLINE calls LOIN * CLIP_b1 calls LOIN * DrawLightning calls LOIN * LASLI calls LOIN * LL9 (Part 9 of 12) calls LOIN * LL9 (Part 10 of 12) calls LOIN * SHPPT calls LOIN * TT15 calls LOIN

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. This stage calculates the line deltas.
Arguments: X1 The screen x-coordinate of the start of the line Y1 The screen y-coordinate of the start of the line X2 The screen x-coordinate of the end of the line Y2 The screen y-coordinate of the end of the line
.LOIN STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA #128 ; Set S = 128, which is the starting point for the STA S ; slope error (representing half a pixel) ASL A ; Set SWAP = 0, as %10000000 << 1 = 0 STA SWAP LDA X2 ; Set A = X2 - X1 SBC X1 ; = delta_x ; ; This subtraction works as the ASL A above sets the C ; flag BCS LI1 ; If X2 > X1 then A is already positive and we can skip ; the next three instructions EOR #%11111111 ; Negate the result in A by flipping all the bits and ADC #1 ; adding 1, i.e. using two's complement to make it ; positive .LI1 STA P ; Store A in P, so P = |X2 - X1|, or |delta_x| SEC ; Set the C flag, ready for the subtraction below LDA Y2 ; Set A = Y2 - Y1 SBC Y1 ; = delta_y ; ; This subtraction works as we either set the C flag ; above, or we skipped that SEC instruction with a BCS BCS LI2 ; If Y2 > Y1 then A is already positive and we can skip ; the next two instructions EOR #%11111111 ; Negate the result in A by flipping all the bits and ADC #1 ; adding 1, i.e. using two's complement to make it ; positive .LI2 STA Q ; Store A in Q, so Q = |Y2 - Y1|, or |delta_y| CMP P ; If Q < P, jump to STPX to step along the x-axis, as BCC STPX ; the line is closer to being horizontal than vertical JMP STPY ; Otherwise Q >= P so jump to STPY to step along the ; y-axis, as the line is closer to being vertical than ; horizontal
Name: LOIN (Part 2 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a shallow gradient, step right along x-axis Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| < |delta_x| * The line is closer to being horizontal than vertical * We are going to step right along the x-axis * We potentially swap coordinates to make sure X1 < X2
.STPX LDX X1 ; Set X = X1 CPX X2 ; If X1 < X2, jump down to LI3, as the coordinates are BCC LI3 ; already in the order that we want DEC SWAP ; Otherwise decrement SWAP from 0 to $FF, to denote that ; we are swapping the coordinates around (though note ; that we don't use this value anywhere, as in the ; original versions of Elite it is used to omit the ; first pixel of each line, which we don't have to do ; in the NES version as it doesn't use EOR plotting) LDA X2 ; Swap the values of X1 and X2 STA X1 STX X2 TAX ; Set X = X1 LDA Y2 ; Swap the values of Y1 and Y2 LDY Y1 STA Y1 STY Y2 .LI3 ; By this point we know the line is horizontal-ish and ; X1 < X2, so we're going from left to right as we go ; from X1 to X2 ; The following section calculates: ; ; Q = Q / P ; = |delta_y| / |delta_x| ; ; using the log tables at logL and log to calculate: ; ; A = log(Q) - log(P) ; = log(|delta_y|) - log(|delta_x|) ; ; by first subtracting the low bytes of the logarithms ; from the table at LogL, and then subtracting the high ; bytes from the table at log, before applying the ; antilog to get the result of the division and putting ; it in Q LDX Q ; Set X = |delta_y| BEQ LIlog7 ; If |delta_y| = 0, jump to LIlog7 to return 0 as the ; result of the division LDA logL,X ; Set A = log(Q) - log(P) LDX P ; = log(|delta_y|) - log(|delta_x|) SEC ; SBC logL,X ; by first subtracting the low bytes of log(Q) - log(P) BMI LIlog4 ; If A > 127, jump to LIlog4 LDX Q ; And then subtracting the high bytes of log(Q) - log(P) LDA log,X ; so now A contains the high byte of log(Q) - log(P) LDX P SBC log,X BCS LIlog5 ; If the subtraction fitted into one byte and didn't ; underflow, then log(Q) - log(P) < 256, so we jump to ; LIlog5 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the antilog LDA antilog,X ; table so the result of the division is now in A JMP LIlog6 ; Jump to LIlog6 to return the result .LIlog5 LDA #255 ; The division is very close to 1, so set A to the BNE LIlog6 ; closest possible answer to 256, i.e. 255, and jump to ; LIlog6 to return the result (this BNE is effectively a ; JMP as A is never zero) .LIlog7 LDA #0 ; The numerator in the division is 0, so set A to 0 and BEQ LIlog6 ; jump to LIlog6 to return the result (this BEQ is ; effectively a JMP as A is always zero) .LIlog4 LDX Q ; Subtract the high bytes of log(Q) - log(P) so now A LDA log,X ; contains the high byte of log(Q) - log(P) LDX P SBC log,X BCS LIlog5 ; If the subtraction fitted into one byte and didn't ; underflow, then log(Q) - log(P) < 256, so we jump to ; LIlog5 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the LDA antilogODD,X ; antilogODD so the result of the division is now in A .LIlog6 STA Q ; Store the result of the division in Q, so we have: ; ; Q = |delta_y| / |delta_x| LDA P ; Set P = P + 1 CLC ; = |delta_x| + 1 ADC #1 ; STA P ; We will use P as the x-axis counter, and we add 1 to ; ensure we include the pixel at each end LDY Y1 ; If Y1 >= Y2, skip the following instruction CPY Y2 BCS P%+5 JMP DOWN ; Y1 < Y2, so jump to DOWN, as we need to draw the line ; to the right and down
Name: LOIN (Part 3 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a shallow line going right and up or left and down Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going right and up (no swap) or left and down (swap) * X1 < X2 and Y1 > Y2 * Draw from (X1, Y1) at bottom left to (X2, Y2) at top right
LDA X1 ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) LDA X1 ; Set X = X1 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWOS,X ; Fetch a one-pixel byte from TWOS where pixel X is set .loin1 STA R ; Store the pixel byte in R .loin2 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixel that we want to draw, then a BNE loin3 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ loin7 ; patterns to use for drawing lines and pixels, so jump ; to loin7 to move on to the next pixel in the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixel that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern .loin3 LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below .loin4 ; We now loop along the line from left to right, using P ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R ORA (SC),Y ; Store R into screen memory at SC(1 0), using OR logic STA (SC),Y ; so it merges with whatever is already on-screen DEC P ; Decrement the x-axis counter in P BEQ loin9 ; If we have just reached the end of the line along the ; x-axis, jump to loin9 to return from the subroutine LDA S ; Set S = S + Q to update the slope error ADC Q STA S BCC loin5 ; If the addition didn't overflow, jump to loin5 to skip ; the following DEY ; Otherwise we just overflowed, so decrement Y to move ; to the pixel line above BMI loin6 ; If Y is negative we need to move up into the character ; block above, so jump to loin6 to decrement the screen ; address accordingly (jumping back to loin1 afterwards) .loin5 LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin4 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin4 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE loin1 ; the right in the nametable buffer and jump up to loin1 INC SC2+1 ; to fetch the tile details for the new nametable entry BNE loin1 .loin6 LDA SC2 ; If we get here then we need to move up into the SBC #32 ; character block above, so we subtract 32 from SC2(1 0) STA SC2 ; to get the tile number on the row above (as there are BCS P%+4 ; 32 tiles on each row) DEC SC2+1 LDY #7 ; Set the pixel line in Y to the last line in the new ; character block LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin2 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin2 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE loin1 ; the right in the nametable buffer and jump up to loin1 INC SC2+1 ; to fetch the tile details for the new nametable entry BNE loin1 ; (this BNE is effectively a JMP as the high byte of ; SC2(1 0) will never be zero as the nametable buffers ; start at address $7000, so the high byte is always at ; least $70) .loin7 DEC P ; Decrement the x-axis counter in P BEQ loin9 ; If we have just reached the end of the line along the ; x-axis, jump to loin9 to return from the subroutine CLC ; Set S = S + Q to update the slope error LDA S ADC Q STA S BCC loin8 ; If the addition didn't overflow, jump to loin8 to skip ; the following DEY ; Otherwise we just overflowed, so decrement Y to move ; to the pixel line above BMI loin6 ; If Y is negative we need to move up into the character ; block above, so jump to loin6 to move to the previous ; row of nametable entries (jumping back to loin1 ; afterwards) .loin8 LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin7 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin7 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer and jump up to loin1 INC SC2+1 ; to fetch the tile details for the new nametable entry JMP loin1 .loin9 LDY YSAV ; Restore Y from YSAV, so that it's preserved SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine
Name: LOIN (Part 4 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a shallow line going right and down or left and up Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going right and down (no swap) or left and up (swap) * X1 < X2 and Y1 <= Y2 * Draw from (X1, Y1) at top left to (X2, Y2) at bottom right
.DOWN LDA X1 ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) LDA X1 ; Set X = X1 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWOS,X ; Fetch a one-pixel byte from TWOS where pixel X is set .loin10 STA R ; Store the pixel byte in R .loin11 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixel that we want to draw, then a BNE loin12 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ loin16 ; patterns to use for drawing lines and pixels, so jump ; to loin16 to move on to the next pixel in the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixel that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern .loin12 LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below .loin13 ; We now loop along the line from left to right, using P ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R ORA (SC),Y ; Store R into screen memory at SC(1 0), using OR logic STA (SC),Y ; so it merges with whatever is already on-screen DEC P ; Decrement the x-axis counter in P BEQ loin9 ; If we have just reached the end of the line along the ; x-axis, jump to loin9 to return from the subroutine LDA S ; Set S = S + Q to update the slope error ADC Q STA S BCC loin14 ; If the addition didn't overflow, jump to loin14 to ; skip the following INY ; Otherwise we just overflowed, so increment Y to move ; to the pixel line below CPY #8 ; If Y = 8 then we have just gone past the bottom of the BEQ loin15 ; character block, so jump to loin15 to move to the next ; row of nametable entries (jumping back to loin10 ; afterwards) .loin14 LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin13 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin13 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE loin10 ; the right in the nametable buffer and jump up to INC SC2+1 ; loin10 to fetch the tile details for the new nametable JMP loin10 ; entry .loin15 ; If we get here then we have just gone past the bottom ; of the character block ; ; At this point the C flag is set, as we jumped here ; using a BEQ, so the ADC #31 below actually adds 32 LDA SC2 ; If we get here then we need to move down into the ADC #31 ; character block above, so we add 32 to SC2(1 0) STA SC2 ; to get the tile number on the row above (as there are BCC P%+4 ; 32 tiles on each row) INC SC2+1 LDY #0 ; Set the pixel line in Y to the first line in the new ; character block LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin11 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin11 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE loin10 ; the right in the nametable buffer and jump up to INC SC2+1 ; loin10 to fetch the tile details for the new nametable JMP loin10 ; entry .loin16 ; If we get here then we have run out of tiles to ; allocate to the line drawing, so we continue with the ; same calculations, but don't actually draw anything in ; this character block DEC P ; Decrement the x-axis counter in P BEQ loin19 ; If we have just reached the end of the line along the ; x-axis, jump to loin19 to return from the subroutine CLC ; Set S = S + Q to update the slope error LDA S ADC Q STA S BCC loin17 ; If the addition didn't overflow, jump to loin17 to ; skip the following INY ; Otherwise we just overflowed, so increment Y to move ; to the pixel line below CPY #8 ; If Y = 8 then we have just gone past the bottom of the BEQ loin15 ; character block, so jump to loin15 to move to the next ; row of nametable entries (jumping back to loin10 ; afterwards) .loin17 LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BNE loin16 ; If the pixel didn't fall out of the right end of R, ; then the pixel byte is still non-zero, so loop back ; to loin16 LDA #%10000000 ; Set a pixel byte in A with the leftmost pixel set, as ; we need to move to the next character block along INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer and jump up to INC SC2+1 ; loin10 to fetch the tile details for the new nametable JMP loin10 ; entry .loin18 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 .loin19 LDY YSAV ; Restore Y from YSAV, so that it's preserved CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine
Name: LOIN (Part 5 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a steep gradient, step up along y-axis Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| >= |delta_x| * The line is closer to being vertical than horizontal * We are going to step up along the y-axis * We potentially swap coordinates to make sure Y1 >= Y2
.STPY LDY Y1 ; Set A = Y = Y1 TYA LDX X1 ; Set X = X1 CPY Y2 ; If Y1 = Y2, jump up to loin18 to return from the BEQ loin18 ; subroutine as there is no line to draw BCS LI15 ; If Y1 > Y2, jump down to LI15, as the coordinates are ; already in the order that we want DEC SWAP ; Otherwise decrement SWAP from 0 to $FF, to denote that ; we are swapping the coordinates around (though note ; that we don't use this value anywhere, as in the ; original versions of Elite it is used to omit the ; first pixel of each line, which we don't have to do ; in the NES version as it doesn't use EOR plotting) LDA X2 ; Swap the values of X1 and X2 STA X1 STX X2 TAX ; Set X = X1 LDA Y2 ; Swap the values of Y1 and Y2 STA Y1 STY Y2 TAY ; Set Y = A = Y1 .LI15 ; By this point we know the line is vertical-ish and ; Y1 >= Y2, so we're going from top to bottom as we go ; from Y1 to Y2 ; The following section calculates: ; ; P = P / Q ; = |delta_x| / |delta_y| ; ; using the log tables at logL and log to calculate: ; ; A = log(P) - log(Q) ; = log(|delta_x|) - log(|delta_y|) ; ; by first subtracting the low bytes of the logarithms ; from the table at LogL, and then subtracting the high ; bytes from the table at log, before applying the ; antilog to get the result of the division and putting ; it in P LDX P ; Set X = |delta_x| BEQ LIfudge ; If |delta_x| = 0, jump to LIfudge to return 0 as the ; result of the division LDA logL,X ; Set A = log(P) - log(Q) LDX Q ; = log(|delta_x|) - log(|delta_y|) SEC ; SBC logL,X ; by first subtracting the low bytes of log(P) - log(Q) BMI LIloG ; If A > 127, jump to LIloG LDX P ; And then subtracting the high bytes of log(P) - log(Q) LDA log,X ; so now A contains the high byte of log(P) - log(Q) LDX Q SBC log,X BCS LIlog3 ; If the subtraction fitted into one byte and didn't ; underflow, then log(P) - log(Q) < 256, so we jump to ; LIlog3 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the antilog LDA antilog,X ; table so the result of the division is now in A JMP LIlog2 ; Jump to LIlog2 to return the result .LIlog3 LDA #255 ; The division is very close to 1, so set A to the BNE LIlog2 ; closest possible answer to 256, i.e. 255, and jump to ; LIlog2 to return the result (this BNE is effectively a ; JMP as A is never zero) .LIfudge LDA #0 ; Set A = 0 and jump to LIlog2 to return 0 as the result BEQ LIlog2 ; (this BNE is effectively a JMP as A is always zero) .LIloG LDX P ; Subtract the high bytes of log(P) - log(Q) so now A LDA log,X ; contains the high byte of log(P) - log(Q) LDX Q SBC log,X BCS LIlog3 ; If the subtraction fitted into one byte and didn't ; underflow, then log(P) - log(Q) < 256, so we jump to ; LIlog3 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the LDA antilogODD,X ; antilogODD so the result of the division is now in A .LIlog2 STA P ; Store the result of the division in P, so we have: ; ; P = |delta_x| / |delta_y| LDA X1 ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) SEC ; Set A = X2 - X1 LDA X2 ; SBC X1 ; This sets the C flag when X1 <= X2 LDA X1 ; Set X = X1 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWOS,X ; Fetch a one-pixel byte from TWOS where pixel X is set STA R ; Store the pixel byte in R LDX Q ; Set X = Q + 1 INX ; = |delta_y| + 1 ; ; We will use Q as the y-axis counter, and we add 1 to ; ensure we include the pixel at each end BCS loin24 ; If X1 <= X2 (which we calculated above) then jump to ; loin24 to draw the line to the left and up JMP loin36 ; If we get here then X1 > X2, so jump to loin36, as we ; need to draw the line to the left and down
Name: LOIN (Part 6 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a steep line going up and left or down and right Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going up and left (no swap) or down and right (swap) * X1 < X2 and Y1 >= Y2 * Draw from (X1, Y1) at top left to (X2, Y2) at bottom right
.loin20 LDY YSAV ; Restore Y from YSAV, so that it's preserved CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine .loin21 ; If we get here then we are drawing our line in a new ; pattern, so it won't contain any pre-existing content LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below LDX Q ; Set X to the value of the x-axis counter .loin22 LDA R ; Fetch the pixel byte from R STA (SC),Y ; Store R into screen memory at SC(1 0) - we don't need ; to merge it with whatever is there, as we just started ; drawing in a new tile DEX ; Decrement the y-coordinate counter in X BEQ loin20 ; If we have just reached the end of the line along the ; y-axis, jump to loin20 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin23 ; If the addition didn't overflow, jump to loin23 to ; skip the following LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin28 ; If the pixel fell out of the right end of R into the ; C flag, then jump to loin28 to rotate it into the left ; end and move right by a character block .loin23 DEY ; Decrement Y to point to move to the pixel line above BPL loin22 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin22 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin23 using a BCC, or we passed through a ; BCS to get to loin23, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) STA SC2 BCS loin24 DEC SC2+1 ; Fall through into loin24 to fetch the correct tile ; number for the new character block and continue ; drawing .loin24 ; This is the entry point for this part (we jump here ; from part 5 when the line is steep and X1 <= X2) ; ; We jump here with X containing the y-axis counter, ; i.e. the number of steps we need to take along the ; y-axis when drawing the line STX Q ; Store the updated y-axis counter in Q SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixel that we want to draw, then a BNE loin25 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ loin29 ; patterns to use for drawing lines and pixels, so jump ; to loin29 to move on to the next pixel in the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixel that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern JMP loin21 ; Jump to loin21 to calculate the pattern buffer address ; for the new tile and continue drawing .loin25 ; If we get here then we are drawing our line in a ; pattern that was already in the nametable, so it might ; contain pre-existing content LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below LDX Q ; Set X to the value of the x-axis counter .loin26 ; We now loop along the line from left to right, using X ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R ORA (SC),Y ; Store R into screen memory at SC(1 0), using OR logic STA (SC),Y ; so it merges with whatever is already on-screen DEX ; Decrement the y-coordinate counter in X BEQ loin20 ; If we have just reached the end of the line along the ; y-axis, jump to loin20 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin27 ; If the addition didn't overflow, jump to loin27 to ; skip the following LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin28 ; If the pixel fell out of the right end of R into the ; C flag, then jump to loin28 to rotate it into the left ; end and move right by a character block .loin27 DEY ; Decrement Y to point to move to the pixel line above BPL loin26 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin26 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin27 using a BCC, or we passed through a ; BCS to get to loin27, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) and STA SC2 ; jump to loin24 to fetch the correct tile number for BCS loin24 ; the new character block and continue drawing (this DEC SC2+1 ; BNE is effectively a JMP as the high byte of SC2(1 0) BNE loin24 ; will never be zero (the nametable buffers start at ; address $7000, so the high byte is at least $70) .loin28 ; If we get here, then we just shifted the pixel out of ; the right end of R, so we now need to put it back into ; the left end of R and move to the right by one ; character block ROR R ; We only reach here via a BCS, so this rotates a 1 into ; the left end of R and clears the C flag INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer INC SC2+1 DEY ; Decrement Y to point to move to the pixel line above BPL loin24 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin24 to ; draw the next pixel LDY #7 ; Set Y to point to the bottom pixel row of the block ; above LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) and STA SC2 ; jump to loin24 to fetch the correct tile number for BCS loin24 ; the new character block and continue drawing DEC SC2+1 JMP loin24 .loin29 ; If we get here then we have run out of tiles to ; allocate to the line drawing, so we continue with the ; same calculations, but don't actually draw anything in ; this character block LDX Q ; Set X to the value of the x-axis counter .loin30 DEX ; Decrement the x-axis counter in X BEQ loin32 ; If we have just reached the end of the line along the ; x-axis, jump to loin32 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin31 ; If the addition didn't overflow, jump to loin31 to ; skip the following LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin28 ; If the pixel fell out of the right end of R into the ; C flag, then jump to loin28 to rotate it into the left ; end and move right by a character block .loin31 DEY ; Decrement Y to point to move to the pixel line above BPL loin30 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin30 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin31 using a BCC, or we passed through a ; BCS to get to loin31, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) STA SC2 BCS P%+4 DEC SC2+1 JMP loin24 ; Jump to loin24 to fetch the correct tile number for ; the new character block and continue drawing .loin32 LDY YSAV ; Restore Y from YSAV, so that it's preserved CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine
Name: LOIN (Part 7 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a steep line going up and right or down and left Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going up and right (no swap) or down and left (swap) * X1 >= X2 and Y1 >= Y2 * Draw from (X1, Y1) at bottom left to (X2, Y2) at top right
.loin33 LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below LDX Q ; Set X to the value of the x-axis counter .loin34 LDA R ; Fetch the pixel byte from R STA (SC),Y ; Store R into screen memory at SC(1 0) - we don't need ; to merge it with whatever is there, as we just started ; drawing in a new tile DEX ; Decrement the y-coordinate counter in X BEQ loin32 ; If we have just reached the end of the line along the ; y-axis, jump to loin32 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin35 ; If the addition didn't overflow, jump to loin35 to ; skip the following ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin40 ; If the pixel fell out of the left end of R into the ; C flag, then jump to loin40 to rotate it into the ; left end and move left by a character block .loin35 DEY ; Decrement Y to point to move to the pixel line above BPL loin34 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin34 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin35 using a BCC, or we passed through a ; BCS to get to loin35, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) STA SC2 BCS loin36 DEC SC2+1 ; Fall through into loin36 to fetch the correct tile ; number for the new character block and continue ; drawing .loin36 ; This is the entry point for this part (we jump here ; from part 5 when the line is steep and X1 > X2) ; ; We jump here with X containing the y-axis counter, ; i.e. the number of steps we need to take along the ; y-axis when drawing the line STX Q ; Store the updated y-axis counter in Q SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixel that we want to draw, then a BNE loin37 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ loin41 ; patterns to use for drawing lines and pixels, so jump ; to loin41 to keep going with the line-drawing ; calculations, but without drawing anything in this ; pattern STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixel that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern JMP loin33 ; Jump to loin33 to calculate the pattern buffer address ; for the new tile and continue drawing .loin37 ; If we get here then we are drawing our line in a ; pattern that was already in the nametable, so it might ; contain pre-existing content LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC CLC ; Clear the C flag for the additions below LDX Q ; Set X to the value of the x-axis counter .loin38 ; We now loop along the line from right to left, using X ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R ORA (SC),Y ; Store R into screen memory at SC(1 0), using OR logic STA (SC),Y ; so it merges with whatever is already on-screen DEX ; Decrement the y-coordinate counter in X BEQ loin45 ; If we have just reached the end of the line along the ; y-axis, jump to loin45 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin39 ; If the addition didn't overflow, jump to loin39 to ; skip the following ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin40 ; If the pixel fell out of the left end of R into the ; C flag, then jump to loin40 to rotate it into the ; left end and move left by a character block .loin39 DEY ; Decrement Y to point to move to the pixel line above BPL loin38 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin38 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin39 using a BCC, or we passed through a ; BCS to get to loin39, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) and STA SC2 ; jump to loin36 to fetch the correct tile number for BCS loin36 ; the new character block and continue drawing DEC SC2+1 JMP loin36 .loin40 ; If we get here, then we just shifted the pixel out of ; the left end of R, so we now need to put it back into ; the right end of R and move to the left by one ; character block ROL R ; We only reach here via a BCS, so this rotates a 1 into ; the right end of R and clears the C flag LDA SC2 ; Decrement SC2(1 0) to point to the next tile number to BNE P%+4 ; the left in the nametable buffer DEC SC2+1 DEC SC2 DEY ; Decrement Y to point to move to the pixel line above BPL loin36 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin36 to ; draw the next pixel LDY #7 ; Set Y to point to the bottom pixel row of the block ; above LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) and STA SC2 ; jump to loin36 to fetch the correct tile number for BCS loin36 ; the new character block and continue drawing DEC SC2+1 JMP loin36 .loin41 ; If we get here then we have run out of tiles to ; allocate to the line drawing, so we continue with the ; same calculations, but don't actually draw anything in ; this character block LDX Q ; Set X to the value of the x-axis counter .loin42 DEX ; Decrement the x-axis counter in X BEQ loin44 ; If we have just reached the end of the line along the ; x-axis, jump to loin44 to return from the subroutine LDA S ; Set S = S + P to update the slope error ADC P STA S BCC loin43 ; If the addition didn't overflow, jump to loin43 to ; skip the following ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along BCS loin40 ; If the pixel fell out of the left end of R into the ; C flag, then jump to loin40 to rotate it into the ; left end and move left by a character block .loin43 DEY ; Decrement Y to point to move to the pixel line above BPL loin42 ; If Y is still positive then we have not yet gone past ; the top of the character block, so jump to loin42 to ; draw the next pixel ; Otherwise we just gone past the top of the current ; character block, so we need to move up into the ; character block above by setting Y and SC2(1 0) LDY #7 ; Set Y to point to the bottom pixel row of the block ; above ; If we get here then the C flag is clear, as we either ; jumped to loin43 using a BCC, or we passed through a ; BCS to get to loin43, so the SBC #31 below actually ; subtracts 32 LDA SC2 ; Subtract 32 from SC2(1 0) to get the tile number on SBC #31 ; the row above (as there are 32 tiles on each row) STA SC2 BCS P%+4 DEC SC2+1 JMP loin36 ; Jump to loin36 to fetch the correct tile number for ; the new character block and continue drawing .loin44 LDY YSAV ; Restore Y from YSAV, so that it's preserved CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine .loin45 LDY YSAV ; Restore Y from YSAV, so that it's preserved CLC ; Clear the C flag for the routine to return RTS ; Return from the subroutine
Name: DrawSunRowOfBlocks [Show more] Type: Subroutine Category: Drawing suns Summary: Draw a row of character blocks that contain sunlight, silhouetting any existing content against the sun
Context: See this subroutine on its own page References: This subroutine is called as follows: * SUN (Part 2 of 2) calls DrawSunRowOfBlocks

This routine fills a row of whole character blocks with sunlight, turning any existing content into a black silhouette on the cyan sun. It effectively fills the character blocks containing the horizontal pixel line (P, Y) to (P+1, Y).
Arguments: P A pixel x-coordinate in the character block from which we start the fill P+1 A pixel x-coordinate in the character block where we finish the fill Y A pixel y-coordinate on the character row to fill
Returns: Y Y is preserved
.DrawSunRowOfBlocks SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 STY YSAV ; Store Y in YSAV so we can retrieve it below LDA P ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + P * 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and P * 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (P, Y) LDA P+1 ; Set Y = (P+1 - P) * 8 - 1 SEC ; SBC P ; So Y is the number of tiles we need to fill in the row LSR A LSR A LSR A TAY DEY .fill1 LDA (SC2),Y ; If the nametable entry for the Y-th tile is non-zero, BNE fill2 ; then there is already something there, so jump to ; fill2 to fill this tile using EOR logic (so the pixels ; that are already there are still visible against the ; sun, as black pixels on the sun's cyan background) LDA #51 ; Otherwise the nametable entry is zero, which is just STA (SC2),Y ; the background, so set this tile to pattern 51 DEY ; Decrement the tile counter in Y BPL fill1 ; Loop back until we have filled the entire row of tiles LDY YSAV ; Retrieve the value of Y we stored above RTS ; Return from the subroutine .fill2 ; If we get here then A contains the pattern number of ; the non-empty tile that we want to fill, so we now ; need to fill that pattern in the pattern buffer while ; keeping the existing content STY T ; Store Y in T so we can retrieve it below LDY pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STY SC+1 ; = (pattBufferHiAddr A*8) ASL A ; ROL SC+1 ; This is the address of pattern number A in the current ASL A ; pattern buffer, as each pattern in the buffer consists ROL SC+1 ; of eight bytes ASL A ; ROL SC+1 ; So this is the address of the pattern for the tile STA SC ; that we want to fill, so now to fill it SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDY #7 ; We now loop through each pixel row within this tile's ; pattern, filling the whole pattern with cyan, but ; EOR'ing with the pattern that is already there so it ; is still visible against the sun, as black pixels on ; the sun's cyan background .fill3 LDA #%11111111 ; Invert the Y-th pixel row by EOR'ing with %11111111 EOR (SC),Y STA (SC),Y DEY ; Decrement Y to point to the pixel line above BPL fill3 ; Loop back until we have filled all 8 pixel lines in ; the pattern LDY T ; Retrieve the value of Y we stored above, so it now ; contains the tile counter from the loop at fill1 DEY ; Decrement the tile counter in Y, as we just filled a ; tile BPL fill1 ; If there are still more tiles to fill on this row, ; loop back to fill1 to continue filling them LDY YSAV ; Retrieve the value of Y we stored above RTS ; Return from the subroutine
Name: HLOIN (Part 1 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line from (X1, Y) to (X2, Y) using EOR logic Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawLaunchBox calls HLOIN * DrawSunEdgeLeft calls HLOIN * DrawSunEdgeRight calls HLOIN * LL164 calls HLOIN * SUN (Part 2 of 2) calls HLOIN

Arguments: X1 The screen x-coordinate of the start of the line X2 The screen x-coordinate of the end of the line Y The screen y-coordinate of the line
Returns: Y Y is preserved X2 X2 is decremented
.hlin1 JMP hlin23 ; Jump to hlin23 to draw the line when it's all within ; one character block LDY YSAV ; Restore Y from YSAV, so that it's preserved .hlin2 RTS ; Return from the subroutine .HLOIN SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine LDX X1 ; Set X = X1 CPX X2 ; If X1 = X2 then the start and end points are the same, BEQ hlin2 ; so return from the subroutine (as hlin2 contains ; an RTS) BCC hlin3 ; If X1 < X2, jump to hlin3 to skip the following code, ; as (X1, Y) is already the left point LDA X2 ; Swap the values of X1 and X2, so we know that (X1, Y) STA X1 ; is on the left and (X2, Y) is on the right STX X2 TAX ; Set X = X1 once again .hlin3 DEC X2 ; Decrement X2 so we do not draw a pixel at the end ; point TXA ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) ; ; As we are drawing a horizontal line, we do not need to ; vary the value of Y, as we will always want to draw on ; the same pixel row within each character block TXA ; Set T = X1 with bits 0-2 cleared AND #%11111000 ; STA T ; Each character block contains 8 pixel rows, so to get ; the address of the first byte in the character block ; that we need to draw into, as an offset from the start ; of the row, we clear bits 0-2 ; ; T is therefore the offset within the row of the start ; of the line at x-coordinate X1 LDA X2 ; Set A = X2 with bits 0-2 cleared AND #%11111000 ; SEC ; A is therefore the offset within the row of the end ; of the line at x-coordinate X2 SBC T ; Set A = A - T ; ; So A contains the width of the line in terms of pixel ; bytes (which is the same as the number of character ; blocks that the line spans, less 1 and multiplied by ; 8) BEQ hlin1 ; If the line starts and ends in the same character ; block then A will be zero, so jump to hlin23 via hlin1 ; to draw the line when it's all within one character ; block LSR A ; Otherwise set R = A / 8 LSR A ; LSR A ; So R contains the number of character blocks that the STA R ; line spans, less 1 (so R = 0 means it spans one block, ; R = 1 means it spans two blocks, and so on)
Name: HLOIN (Part 2 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the left end of the line Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
; We now start the drawing process, beginning with the ; left end of the line, whose nametable entry is in ; SC2(1 0) SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BNE hlin5 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin4 ; patterns to use for drawing lines and pixels, so jump ; to hlin9 via hlin4 to move on to the next character ; block to the right, as we don't have enough patterns ; to draw the left end of the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern JMP hlin7 ; Jump to hlin7 to draw the line, starting by drawing ; the left end into the newly allocated pattern number ; in A .hlin4 JMP hlin9 ; Jump to hlin9 to move right by one character block ; without drawing anything .hlin5 ; If we get here then A contains the tile number that's ; already allocated to this part of the line in the ; nametable buffer CMP #60 ; If A >= 60, then the pattern that's already allocated BCS hlin7 ; is one of the patterns we have reserved for drawing, ; so jump to hlin7 to draw the line, starting by drawing ; the left end into the pattern number in A CMP #37 ; If A < 37, then the pattern that's already allocated BCC hlin4 ; is one of the icon bar tiles, so jump to hlin9 via ; hlin4 to move right by one character block without ; drawing anything, as we can't draw on the icon bar ; If we get here then 37 <= A <= 59, so the pattern ; that's already allocated is one of the pre-rendered ; patterns containing horizontal and vertical lines ; ; We don't want to draw over the top of the pre-rendered ; patterns as that will break them, so instead we make a ; copy of the pre-rendered pattern into a newly ; allocated pattern, and then draw our line into the ; this new pattern, thus preserving what's already shown ; on-screen while still drawing our new line LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC3+1 ; pattern that we want to copy STA SC3 LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin4 ; patterns for drawing lines and pixels, so jump to ; hlin9 via hlin4 to move right by one character block ; without drawing anything, as we don't have enough ; patterns to draw the left end of the line LDX #0 ; Otherwise firstFreePattern contains the number of the STA (SC2,X) ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to copy by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC+1 STA SC ; We now have a new pattern in SC(1 0) into which we can ; draw the left end of our line, so we now need to copy ; the pre-rendered pattern that we want to draw on top ; of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC3(1 0) to SC(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC3(1 0) to SC(1 0), so ; set a counter in Y .hlin6 LDA (SC3),Y ; Copy the Y-th byte of SC3(1 0) to the Y-th byte of STA (SC),Y ; SC(1 0) DEY ; Decrement the counter BPL hlin6 ; Loop back until we have copied all eight bytes LDY T ; Restore the value of Y from before the loop, so it ; once again contains the pixel row offset within the ; each character block for the line we are drawing JMP hlin8 ; Jump to hlin8 to draw the left end of the line into ; the tile that we just copied .hlin7 ; If we get here then we have either allocated a new ; pattern number for the line, or the pattern number ; already allocated to this part of the line is >= 60, ; which is a pattern into which we can draw ; ; In either case the pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC .hlin8 ; We now draw the left end of our horizontal line LDA X1 ; Set X = X1 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWFR,X ; Fetch a ready-made byte with X pixels filled in at the ; right end of the byte (so the filled pixels start at ; point X and go all the way to the end of the byte), ; which is the shape we want for the left end of the ; line EOR (SC),Y ; Store this into the pattern buffer at SC(1 0), using STA (SC),Y ; EOR logic so it merges with whatever is already ; on-screen, so we have now drawn the line's left cap .hlin9 INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer INC SC2+1 LDX R ; Fetch the number of character blocks in which we need ; to draw, which we stored in R above DEX ; If R = 1, then we only have the right cap to draw, so BNE hlin10 ; jump to hlin17 to draw the right end of the line JMP hlin17 .hlin10 STX R ; Otherwise we haven't reached the right end of the line ; yet, so decrement R as we have just drawn one block
Name: HLOIN (Part 3 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the middle part of the line Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
; We now draw the middle part of the line (i.e. the part ; between the left and right caps) .hlin11 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BEQ hlin13 ; pattern has not yet been allocated to this entry, so ; jump to hlin13 to allocate a new pattern ; If we get here then A contains the pattern number ; that's already allocated to this part of the line in ; the nametable buffer CMP #60 ; If A < 60, then the pattern that's already allocated BCC hlin15 ; is either an icon bar pattern, or one of the ; pre-rendered patterns containing horizontal and ; vertical lines, so jump to hlin15 to process drawing ; on top of the pre-rendered pattern ; If we get here then the pattern number already ; allocated to this part of the line is >= 60, which is ; a pattern into which we can draw ; ; The pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC LDA #%11111111 ; Set A to a pixel byte containing eight pixels in a row EOR (SC),Y ; Store this into the pattern buffer at SC(1 0), using STA (SC),Y ; EOR logic so it merges with whatever is already ; on-screen, so we have now drawn one character block ; of the middle portion of the line .hlin12 INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer INC SC2+1 DEC R ; Decrement the number of character blocks in which we ; need to draw, as we have just drawn one block BNE hlin11 ; If there are still more character blocks to draw, loop ; back to hlin11 to draw the next one JMP hlin17 ; Otherwise we have finished drawing the middle portion ; of the line, so jump to hlin17 to draw the right end ; of the line .hlin13 ; If we get here then there is no pattern allocated to ; the part of the line we want to draw, so we can use ; one of the pre-rendered patterns that contains an ; eight-pixel horizontal line on the correct pixel row ; ; We jump here with X = 0 TYA ; Set A = Y + 37 CLC ; ADC #37 ; Patterns 37 to 44 contain pre-rendered patterns as ; follows: ; ; * Pattern 37 has a horizontal line on pixel row 0 ; * Pattern 38 has a horizontal line on pixel row 1 ; ... ; * Pattern 43 has a horizontal line on pixel row 6 ; * Pattern 44 has a horizontal line on pixel row 7 ; ; So A contains the pre-rendered pattern number that ; contains an eight-pixel line on pixel row Y, and as Y ; contains the offset of the pixel row for the line we ; are drawing, this means A contains the correct pattern ; number for this part of the line STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A JMP hlin12 ; Jump up to hlin12 to move on to the next character ; block to the right .hlin14 ; If we get here then A + Y = 50, which means we can ; alter the current pre-rendered pattern to draw our ; line ; ; This is how it works. Patterns 44 to 51 contain ; pre-rendered patterns as follows: ; ; * Pattern 44 has a horizontal line on pixel row 7 ; * Pattern 45 is filled from pixel row 7 to row 6 ; * Pattern 46 is filled from pixel row 7 to row 5 ; ... ; * Pattern 50 is filled from pixel row 7 to row 1 ; * Pattern 51 is filled from pixel row 7 to row 0 ; ; Y contains the number of the pixel row for the line we ; are drawing, so if A + Y = 50, this means: ; ; * We want to draw pixel row 0 on top of pattern 50 ; * We want to draw pixel row 1 on top of pattern 49 ; ... ; * We want to draw pixel row 5 on top of pattern 45 ; * We want to draw pixel row 6 on top of pattern 44 ; ; In other words, if A + Y = 50, then we want to draw ; the pixel row just above the rows that are already ; filled in the pre-rendered pattern, which means we ; can simply swap the pre-rendered pattern to the next ; one in the list (e.g. going from four filled lines to ; five filled lines, for example) ; ; We jump here with a BEQ, so the C flag is set for the ; following addition, so the C flag can be used as the ; plus 1 in the two's complement calculation TYA ; Set A = 51 + C + ~Y EOR #$FF ; = 51 + (1 + ~Y) ADC #51 ; = 51 - Y ; ; So A contains the number of the pre-rendered pattern ; that has our horizontal line drawn on pixel row Y, and ; all the lines below that filled, which is what we want STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A INC SC2 ; Increment SC2(1 0) to point to the next tile number to BNE P%+4 ; the right in the nametable buffer INC SC2+1 DEC R ; Decrement the number of character blocks in which we ; need to draw, as we have just drawn one block BNE hlin11 ; If there are still more character blocks to draw, loop ; back to hlin11 to draw the next one JMP hlin17 ; Otherwise we have finished drawing the middle portion ; of the line, so jump to hlin17 to draw the right end ; of the line .hlin15 ; If we get here then A <= 59, so the pattern that's ; already allocated is either an icon bar pattern, or ; one of the pre-rendered patterns containing horizontal ; and vertical lines ; ; We jump here with the C flag clear, so the addition ; below will work correctly, and with X = 0, so the ; write to (SC2,X) will also work properly STA SC ; Set SC to the number of the tile that is already ; allocated to this part of the screen, so we can ; retrieve it later TYA ; If A + Y = 50, then we are drawing our line just ADC SC ; above the top line of a pre-rendered pattern that is CMP #50 ; filled from the bottom row to the row just below Y, BEQ hlin14 ; so jump to hlin14 to switch this tile to another ; pre-rendered pattern that contains the line we want to ; draw (see hlin14 for a full explanation of this logic) ; If we get here then 37 <= A <= 59, so the pattern ; that's already allocated is one of the pre-rendered ; patterns containing horizontal and vertical lines, but ; isn't one that we can simply replace with another ; pre-rendered pattern ; ; We don't want to draw over the top of the pre-rendered ; patterns as that will break them, so instead we make a ; copy of the pre-rendered pattern into a newly ; allocated pattern, and then draw our line into the ; this new pattern, thus preserving what's already shown ; on-screen while still drawing our new line LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin12 ; patterns to use for drawing lines and pixels, so jump ; to hlin12 to move right by one character block without ; drawing anything, as we don't have enough patterns to ; draw this part of the line INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; tile to contain the pre-rendered pattern that we want ; to copy by setting the nametable entry to the pattern ; number we just fetched LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC3+1 STA SC3 LDA SC ; Set A to the number of the pattern that is already ; allocated to this part of the screen, which we stored ; in SC above LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC+1 ; pattern that we want to copy STA SC ; We now have a new pattern in SC3(1 0) into which we ; can draw the left end of our line, so we now need to ; copy the pre-rendered pattern that we want to draw on ; top of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC(1 0) to SC3(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC(1 0) to SC3(1 0), so ; set a counter in Y .hlin16 LDA (SC),Y ; Copy the Y-th byte of SC(1 0) to the Y-th byte of STA (SC3),Y ; SC3(1 0) DEY ; Decrement the counter BPL hlin16 ; Loop back until we have copied all eight bytes LDY T ; Restore the value of Y from before the loop, so it ; once again contains the pixel row offset within the ; each character block for the line we are drawing LDA #%11111111 ; Set A to a pixel byte containing eight pixels in a row EOR (SC3),Y ; Store this into the pattern buffer at SC3(1 0), using STA (SC3),Y ; EOR logic so it merges with whatever is already ; on-screen, so we have now drawn one character block ; of the middle portion of the line JMP hlin12 ; Loop back to hlin12 to continue drawing the line in ; the next character block to the right
Name: HLOIN (Part 4 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the right end of the line Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.hlin17 ; We now finish off the drawing process with the right ; end of the line SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BNE hlin19 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin18 ; patterns to use for drawing lines and pixels, so jump ; to hlin30 via hlin18 to return from the subroutine, as ; we don't have enough patterns to draw the right end ; of the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; drawing JMP hlin21 ; Jump to hlin21 to draw the right end of the line into ; the newly allocated pattern number in A .hlin18 JMP hlin30 ; Jump to hlin30 to return from the subroutine .hlin19 ; If we get here then A contains the tile number that's ; already allocated to this part of the line in the ; nametable buffer CMP #60 ; If A >= 60, then the pattern that's already allocated BCS hlin21 ; is one of the patterns we have reserved for drawing, ; so jump to hlin21 to draw the right end of the line CMP #37 ; If A < 37, then the pattern that's already allocated BCC hlin18 ; is one of the icon bar tiles, so jump to hlin30 via ; hlin18 to return from the subroutine, as we can't draw ; on the icon bar ; If we get here then 37 <= A <= 59, so the pattern ; that's already allocated is one of the pre-rendered ; patterns containing horizontal and vertical lines ; ; We don't want to draw over the top of the pre-rendered ; patterns as that will break them, so instead we make a ; copy of the pre-rendered pattern into a newly ; allocated pattern, and then draw our line into the ; this new pattern, thus preserving what's already shown ; on-screen while still drawing our new line LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC3+1 ; pattern that we want to copy STA SC3 LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin18 ; patterns for drawing lines and pixels, so jump to ; hlin30 via hlin18 to return from the subroutine, as we ; don't have enough patterns to draw the right end ; of the line LDX #0 ; Otherwise firstFreePattern contains the number of the STA (SC2,X) ; next available pattern for drawing, so allocate this ; tile to contain the pre-rendered pattern that we want ; to copy by setting the nametable entry to the pattern ; number we just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC+1 STA SC ; We now have a new pattern in SC(1 0) into which we can ; draw the right end of our line, so we now need to copy ; the pre-rendered pattern that we want to draw on top ; of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC3(1 0) to SC(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC3(1 0) to SC(1 0), so ; set a counter in Y .hlin20 LDA (SC3),Y ; Copy the Y-th byte of SC3(1 0) to the Y-th byte of STA (SC),Y ; SC(1 0) DEY ; Decrement the counter BPL hlin20 ; Loop back until we have copied all eight bytes LDY T ; Restore the value of Y from before the loop, so it ; once again contains the pixel row offset within the ; each character block for the line we are drawing JMP hlin22 ; Jump to hlin22 to draw the right end of the line into ; the tile that we just copied .hlin21 ; If we get here then we have either allocated a new ; pattern number for the line, or the pattern number ; already allocated to this part of the line is >= 60, ; which is a pattern into which we can draw ; ; In either case the pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC .hlin22 ; We now draw the right end of our horizontal line LDA X2 ; Set X = X2 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line ends (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWFL,X ; Fetch a ready-made byte with X pixels filled in at the ; left end of the byte (so the filled pixels start at ; the left edge and go up to point X), which is the ; shape we want for the right end of the line JMP hlin29 ; Jump to hlin29 to poke the pixel byte into the pattern ; buffer
Name: HLOIN (Part 5 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the line when it's all within one character block Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.hlin23 ; If we get here then the line starts and ends in the ; same character block SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BNE hlin25 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin24 ; patterns to use for drawing lines and pixels, so jump ; to hlin30 via hlin24 to return from the subroutine, as ; we don't have enough patterns to draw the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern JMP hlin27 ; Jump to hlin27 to draw the line into the newly ; allocated tile number in A .hlin24 JMP hlin30 ; Jump to hlin30 to return from the subroutine .hlin25 ; If we get here then A contains the tile number that's ; already allocated to this part of the line in the ; nametable buffer CMP #60 ; If A >= 60, then the pattern that's already allocated BCS hlin27 ; is one of the patterns we have reserved for drawing, ; so jump to hlin27 to draw the line into the pattern ; number in A CMP #37 ; If A < 37, then the pattern that's already allocated BCC hlin24 ; is one of the icon bar patterns, so jump to hlin30 via ; hlin24 to return from the subroutine, as we can't draw ; on the icon bar ; If we get here then 37 <= A <= 59, so the pattern ; that's already allocated is one of the pre-rendered ; patterns containing horizontal and vertical lines ; ; We don't want to draw over the top of the pre-rendered ; patterns as that will break them, so instead we make a ; copy of the pre-rendered pattern into a newly ; allocated pattern, and then draw our line into the ; this new pattern, thus preserving what's already shown ; on-screen while still drawing our new line LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC3+1 ; pattern that we want to copy STA SC3 LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ hlin24 ; patterns for drawing lines and pixels, so jump to ; hlin30 via hlin24 to return from the subroutine, as we ; don't have enough patterns to draw the line LDX #0 ; Otherwise firstFreePattern contains the number of the STA (SC2,X) ; next available pattern for drawing, so allocate this ; tile to contain the pre-rendered pattern that we want ; to copy by setting the nametable entry to the pattern ; number we just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC+1 STA SC ; We now have a new pattern in SC(1 0) into which we can ; draw our line, so we now need to copy the pattern of ; the pre-rendered pattern that we want to draw on top ; of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC3(1 0) to SC(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC3(1 0) to SC(1 0), so ; set a counter in Y .hlin26 LDA (SC3),Y ; Copy the Y-th byte of SC3(1 0) to the Y-th byte of STA (SC),Y ; SC(1 0) DEY ; Decrement the counter BPL hlin26 ; Loop back until we have copied all eight bytes LDY T ; Restore the value of Y from before the loop, so it ; once again contains the pixel row offset within the ; each character block for the line we are drawing JMP hlin28 ; Jump to hlin28 to draw the line into the tile that ; we just copied .hlin27 ; If we get here then we have either allocated a new ; pattern number for the line, or the pattern number ; already allocated to this part of the line is >= 60, ; which is a pattern into which we can draw ; ; In either case the pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC .hlin28 ; We now draw our horizontal line into the relevant ; character block LDA X1 ; Set X = X1 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWFR,X ; Fetch a ready-made byte with X pixels filled in at the ; right end of the byte (so the filled pixels start at ; point X and go all the way to the end of the byte), ; which is the shape we want for the left end of the ; line STA T ; Store the pixel shape for the right end of the line in ; T LDA X2 ; Set X = X2 mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line ends (as TAX ; each pixel line in the character block is 8 pixels ; wide) LDA TWFL,X ; Fetch a ready-made byte with X pixels filled in at the ; left end of the byte (so the filled pixels start at ; the left edge and go up to point X), which is the ; shape we want for the right end of the line AND T ; Set A to the overlap of the pixel byte for the left ; end of the line (in T) and the right end of the line ; (in A) by AND'ing them together, which gives us the ; pixels that are in the horizontal line we want to draw .hlin29 EOR (SC),Y ; Store this into the pattern buffer at SC(1 0), using STA (SC),Y ; EOR logic so it merges with whatever is already ; on-screen, so we have now drawn our entire horizontal ; line within this one character block .hlin30 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine
Name: DrawVerticalLine (Part 1 of 3) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a vertical line from (X1, Y1) to (X1, Y2) Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawLaunchBox calls DrawVerticalLine

Arguments: X1 The screen x-coordinate of the line Y1 The screen y-coordinate of the start of the line Y2 The screen y-coordinate of the end of the line
Returns: Y Y is preserved
.DrawVerticalLine SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine LDY Y1 ; Set Y = Y1 CPY Y2 ; If Y1 = Y2 then the start and end points are the same, BEQ vlin3 ; so return from the subroutine (as vlin3 contains ; an RTS) BCC vlin1 ; If Y1 < Y2, jump to vlin1 to skip the following code, ; as (X1, Y1) is already the top point LDA Y2 ; Swap the values of Y1 and Y2, so we know that (X1, Y1) STA Y1 ; is at the top and (X1, Y2) is at the bottom STY Y2 TAY ; Set Y = Y1 once again .vlin1 LDA X1 ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing LDA X1 ; Set S = X1 mod 8, which is the pixel column within the AND #7 ; character block at which we want to draw the start of STA S ; our line (as each character block has 8 columns) ; ; As we are drawing a vertical line, we do not need to ; vary the value of S, as we will always want to draw on ; the same pixel column within each character block LDA Y2 ; Set R = Y2 - Y1 SEC ; SBC Y1 ; So R is the height of the line we want to draw, which STA R ; we will use as a counter as we work our way along the ; line from top to bottom - in other words, R will the ; height remaining that we have to draw TYA ; Set Y = Y1 mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) BNE vlin4 ; If Y is non-zero then our vertical line is starting ; inside a character block rather than from the very ; top, so jump to vlin4 to draw the top end of the line JMP vlin13 ; Otherwise jump to vlin13 to draw the middle part of ; the line from full-height line segments, as we don't ; need to draw a separate block for the top end
Name: DrawVerticalLine (Part 2 of 3) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the top end or bottom end of the line Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.vlin2 ; If we get here then we need to move down by one ; character block without drawing anything, and then ; move on to drawing the middle portion of the line STY T ; Set A = R + Y LDA R ; = pixels to left draw + current pixel row ADC T ; ; So A contains the total number of pixels left to draw ; in our line SBC #7 ; At this point the C flag is clear, as the above ; addition won't overflow, so this sets A = R + Y - 8 ; and sets the flags accordingly BCC vlin3 ; If the above subtraction didn't underflow then ; R + Y < 8, so there is less than one block height to ; draw, so there would be nothing more to draw after ; moving down one line, so jump to vlin3 to return ; from the subroutine JMP vlin12 ; Jump to vlin12 to move on drawing the middle ; portion of the line .vlin3 LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .vlin4 ; We now draw either the top end or the bottom end of ; the line into the nametable entry in SC2(1 0) STY Q ; Set Q to the pixel row of the top of the line that we ; want to draw (which will be Y1 mod 8 for the top end ; of the line, or 0 for the bottom end of the line) ; ; For the top end of the line, we draw down from row ; Y1 mod 8 to the bottom of the character block, which ; will correctly draw the top end of the line ; ; For the bottom end of the line, we draw down from row ; 0 until R runs down, which will correctly draw the ; bottom end of the line SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BNE vlin6 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ vlin5 ; patterns to use for drawing lines and pixels, so jump ; to vlin2 via vlin5 to move on to the next character ; block down, as we don't have enough patterns to draw ; the top block of the line STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern JMP vlin8 ; Jump to vlin8 to draw the line, starting by drawing ; the top end into the newly allocated pattern number ; in A .vlin5 JMP vlin2 ; Jump to vlin2 to move on to the next character block ; down .vlin6 CMP #60 ; If A >= 60, then the pattern that's already allocated BCS vlin8 ; is one of the patterns we have reserved for drawing, ; so jump to vlin8 to draw the line, starting by drawing ; the top end into the pattern number in A CMP #37 ; If A < 37, then the pattern that's already allocated BCC vlin5 ; is one of the icon bar pattern, so jump to vlin2 via ; vlin5 to move down by one character block without ; drawing anything, as we can't draw on the icon bar ; If we get here then 37 <= A <= 59, so the pattern ; that's already allocated is one of the pre-rendered ; patterns containing horizontal and vertical lines ; ; We don't want to draw over the top of the pre-rendered ; patterns as that will break them, so instead we make a ; copy of the pre-rendered pattern into a newly ; allocated pattern, and then draw our line into the ; this new pattern, thus preserving what's already shown ; on-screen while still drawing our new line LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC3+1 ; pattern that we want to copy STA SC3 LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ vlin5 ; patterns for drawing lines and pixels, so jump to ; vlin2 via vlin5 to move down by one character block ; without drawing anything, as we don't have enough ; patterns to draw the top end of the line LDX #0 ; Otherwise firstFreePattern contains the number of the STA (SC2,X) ; next available pattern for drawing, so allocate this ; pattern to cover the pixels that we want to copy by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC+1 STA SC ; We now have a new pattern in SC(1 0) into which we can ; draw the top end of our line, so we now need to copy ; the pre-rendered pattern that we want to draw on top ; of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC3(1 0) to SC(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC3(1 0) to SC(1 0), so ; set a counter in Y .vlin7 LDA (SC3),Y ; Copy the Y-th byte of SC3(1 0) to the Y-th byte of STA (SC),Y ; SC(1 0) DEY ; Decrement the counter BPL vlin7 ; Loop back until we have copied all eight bytes LDY T ; Restore the value of Y from before the loop, so it ; once again contains the pixel row offset within the ; each character block for the line we are drawing JMP vlin9 ; Jump to hlin8 to draw the top end of the line into ; the tile that we just copied .vlin8 ; If we get here then we have either allocated a new ; pattern number for the line, or the pattern number ; already allocated to this part of the line is >= 60, ; which is a pattern into which we can draw ; ; In either case the pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC .vlin9 LDX S ; Set X to the pixel column within the character block ; at which we want to draw our line, which we stored in ; S in part 1 LDY Q ; Set Y to y-coordinate of the start of the line, which ; we stored in Q above LDA R ; If the height remaining in R is 0 then we have no more BEQ vlin11 ; line to draw, so jump to vlin11 to return from the ; subroutine .vlin10 LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y DEC R ; Decrement the height remaining counter in R, as we ; just drew a pixel BEQ vlin11 ; If the height remaining in R is 0 then we have no more ; line to draw, so jump to vlin11 to return from the ; subroutine INY ; Increment the y-coordinate in Y so we move down the ; line by one pixel CPY #8 ; If Y < 8, loop back to vlin10 draw the next pixel as BCC vlin10 ; we haven't yet reached the bottom of the character ; block containing the line's top end BCS vlin12 ; If Y >= 8 then we have drawn our vertical line from ; the starting point to the bottom of the character ; block containing the line's top end, so jump to vlin12 ; to move down one row to draw the middle portion of the ; line .vlin11 LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine
Name: DrawVerticalLine (Part 3 of 3) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the middle portion of the line from full-height blocks Deep dive: Drawing lines in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.vlin12 ; We now draw the middle part of the line (i.e. the part ; between the top and bottom caps) SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDY #0 ; We want to start drawing the line from the top pixel ; line in the next character row, so set Y = 0 to use as ; the pixel row number ; Next, we update SC2(1 0) to the address of the next ; row down in the nametable buffer, which we can do by ; adding 32 as there are 32 tiles in each row LDA SC2 ; Set SC2(1 0) = SC2(1 0) + 32 CLC ; ADC #32 ; Starting with the low bytes STA SC2 BCC vlin13 ; And then the high bytes INC SC2+1 .vlin13 ; We jump here from part 2 if the line starts at the top ; of a character block LDA R ; If the height remaining in R is 0 then we have no more BEQ vlin11 ; line to draw, so jump to vlin11 to return from the ; subroutine SEC ; Set A = A - 8 SBC #8 ; = R - 8 ; ; So this subtracts 8 pixels (one block) from the number ; of pixels we still have to draw BCS vlin14 ; If the subtraction didn't underflow, then there are at ; least 8 more pixels to draw, so jump to vlin14 to draw ; another block's worth of pixels JMP vlin4 ; The subtraction underflowed, so R is less than 8 and ; we need to stop drawing full-height blocks and draw ; the bottom end of the line, so jump to vlin4 with ; Y = 0 to do just this .vlin14 STA R ; Store the updated number of pixels left to draw, which ; we calculated in the subtraction above LDX #0 ; If the nametable buffer entry is zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BEQ vlin15 ; pattern has not yet been allocated to this entry, so ; jump to vlin15 to place a pre-rendered pattern into ; the nametable entry CMP #60 ; If A < 60, then the pattern that's already allocated BCC vlin17 ; is either an icon bar pattern, or one of the ; pre-rendered patterns containing horizontal and ; vertical lines, so jump to vlin17 to process drawing ; on top of the pre-rendered pattern ; If we get here then the pattern number already ; allocated to this part of the line is >= 60, which is ; a pattern into which we can draw ; ; The pattern number is in A LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC LDX S ; Set X to the pixel column within the character block ; at which we want to draw our line, which we stored in ; S in part 1 LDY #0 ; We are going to draw a vertical line from pixel row 0 ; to row 7, so set an index in Y to count up ; We repeat the following code eight times, so it draws ; eight pixels from the top of the character block to ; the bottom LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the index in Y LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y JMP vlin12 ; Loop back to move down a row and draw the next block .vlin15 LDA S ; Set A to the pixel column within the character block ; at which we want to draw our line, which we stored in ; S in part 1 CLC ; Patterns 52 to 59 contain pre-rendered patterns as ADC #52 ; follows: ; ; * Pattern 52 has a vertical line in pixel column 0 ; * Pattern 53 has a vertical line in pixel column 1 ; ... ; * Pattern 58 has a vertical line in pixel column 6 ; * Pattern 59 has a vertical line in pixel column 7 ; ; So A contains the pre-rendered pattern number that ; contains an eight-pixel line in pixel column S, and as ; S contains the offset of the pixel column for the line ; we are drawing, this means A contains the correct ; pattern number for this part of the line STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A .vlin16 JMP vlin12 ; Loop back to move down a row and draw the next block .vlin17 ; If we get here then A <= 59, so the pattern that's ; already allocated is either an icon bar pattern, or ; one of the pre-rendered patterns containing horizontal ; and vertical lines ; ; We jump here with X = 0, so the write to (SC2,X) ; below will work properly STA SC ; Set SC to the number of the pattern that is already ; allocated to this part of the screen, so we can ; retrieve it later LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ vlin16 ; patterns to use for drawing lines and pixels, so jump ; to vlin16 to move down by one character block without ; drawing anything, as we don't have enough patterns to ; draw this part of the line INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be used the ; next time we need to draw lines or pixels into a ; pattern STA (SC2,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; tile to contain the pre-rendered pattern that we want ; to copy by setting the nametable entry to the pattern ; number we just fetched LDX pattBufferHiDiv8 ; Set SC3(1 0) = (pattBufferHiDiv8 A) * 8 STX SC3+1 ; = (pattBufferHi A) + A * 8 ASL A ; ROL SC3+1 ; So SC3(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC3+1 ; pattern data), which means SC3(1 0) points to the ASL A ; pattern data for the pattern we just fetched ROL SC3+1 STA SC3 LDA SC ; Set A to the number of the pattern that is already ; allocated to this part of the screen, which we stored ; in SC above LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the pre-rendered ROL SC+1 ; pattern that we want to copy STA SC ; We now have a new pattern in SC3(1 0) into which we ; can draw the middle part of our line, so we now need ; to copy the pattern of the pre-rendered pattern that ; we want to draw on top of ; ; Each pattern is made up of eight bytes, so we simply ; need to copy eight bytes from SC(1 0) to SC3(1 0) STY T ; Store Y in T so we can retrieve it after the following ; loop LDY #7 ; We now copy eight bytes from SC(1 0) to SC3(1 0), so ; set a counter in Y LDX S ; Set X to the pixel column within the character block ; at which we want to draw our line, which we stored in ; S in part 1 .vlin18 LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) and store the result in the Y-th byte of STA (SC3),Y ; SC3(1 0), so this copies the pre-rendered pattern, ; superimposes our vertical line on the result and ; stores it in the pattern buffer for the tile we just ; allocated DEY ; Decrement the counter BPL vlin18 ; Loop back until we have copied all eight bytes BMI vlin16 ; Jump to vlin12 via vlin16 to move down a row and draw ; the next block
Name: PIXEL [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw a one-pixel dot Deep dive: Drawing pixels in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOEXP calls PIXEL * DrawExplosionBurst calls PIXEL * DrawDash calls via pixl2

This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
Arguments: X The screen x-coordinate of the point to draw A The screen y-coordinate of the point to draw
Returns: Y Y is preserved
Other entry points: pixl2 Restore the value of Y and return from the subroutine
.PIXEL STX SC2 ; Set SC2 to the pixel's x-coordinate in X STY T1 ; Store Y in T1 so we can retrieve it at the end of the ; subroutine TAY ; Set Y to the pixel's y-coordinate TXA ; Set SC(1 0) = (nameBufferHi 0) + yLookup(Y) + X / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC ; Adding nameBufferHi and X / 8 therefore sets SC(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC+1 ; the pixel at (X, Y), i.e. the line we are drawing SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC,X) ; containing the pixel that we want to draw, then a BNE pixl1 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ pixl2 ; patterns to use for drawing lines and pixels, so jump ; to pixl2 to return from the subroutine, as we can't ; draw the pixel STA (SC,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the pixel that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern .pixl1 LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) LDA SC2 ; Set X = X mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide, and we set SC2 to the x-coordinate above) LDA TWOS,X ; Fetch a one-pixel byte from TWOS where pixel X is set ORA (SC),Y ; Store the pixel byte into screen memory at SC(1 0), STA (SC),Y ; using OR logic so it merges with whatever is already ; on-screen .pixl2 LDY T1 ; Restore the value of Y from T1 so it is preserved RTS ; Return from the subroutine
Name: DrawDash [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw a two-pixel dash Deep dive: Drawing pixels in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT22 calls DrawDash

Arguments: X The screen x-coordinate of the dash to draw A The screen y-coordinate of the dash to draw
Returns: Y Y is preserved
.DrawDash STX SC2 ; Set SC2 to the pixel's x-coordinate in X STY T1 ; Store Y in T1 so we can retrieve it at the end of the ; subroutine TAY ; Set Y to the pixel's y-coordinate TXA ; Set SC(1 0) = (nameBufferHi 0) + yLookup(Y) + X / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC ; Adding nameBufferHi and X / 8 therefore sets SC(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC+1 ; the pixel at (X, Y), i.e. the line we are drawing SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #0 ; If the nametable buffer entry is non-zero for the tile LDA (SC,X) ; containing the pixel that we want to draw, then a BNE dash1 ; pattern has already been allocated to this entry, so ; skip the following LDA firstFreePattern ; If firstFreePattern is zero then we have run out of BEQ pixl2 ; patterns to use for drawing lines and pixels, so jump ; to pixl2 to return from the subroutine, as we can't ; draw the dash STA (SC,X) ; Otherwise firstFreePattern contains the number of the ; next available pattern for drawing, so allocate this ; pattern to cover the dash that we want to draw by ; setting the nametable entry to the pattern number we ; just fetched INC firstFreePattern ; Increment firstFreePattern to point to the next ; available pattern for drawing, so it can be added to ; the nametable the next time we need to draw lines or ; pixels into a pattern .dash1 LDX #HI(pattBuffer0)/8 ; Set SC(1 0) = (pattBuffer0/8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) LDA SC2 ; Set X = X mod 8, which is the horizontal pixel number AND #7 ; within the character block where the line starts (as TAX ; each pixel line in the character block is 8 pixels ; wide, and we set SC2 to the x-coordinate above) LDA TWOS2,X ; Fetch a two-pixel byte from TWOS2 where pixels X and ; X+1 are set ORA (SC),Y ; Store the dash byte into screen memory at SC(1 0), STA (SC),Y ; using OR logic so it merges with whatever is already ; on-screen LDY T1 ; Restore the value of Y from T1 so it is preserved RTS ; Return from the subroutine
Name: ECBLB2 [Show more] Type: Subroutine Category: Dashboard Summary: Start up the E.C.M. (start the countdown and make the E.C.M. sound)
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls ECBLB2 * TACTICS (Part 1 of 7) calls ECBLB2
.ECBLB2 LDA #32 ; Set the E.C.M. countdown timer in ECMA to 32 STA ECMA LDY #2 ; Call the NOISE routine with Y = 2 to make the sound JMP NOISE ; of the E.C.M., returning from the subroutine using a ; tail call
Name: MSBAR [Show more] Type: Subroutine Category: Dashboard Summary: Draw a specific indicator in the dashboard's missile bar
Context: See this subroutine on its own page References: This subroutine is called as follows: * ABORT2 calls MSBAR * Main flight loop (Part 3 of 16) calls MSBAR * msblob calls MSBAR

Arguments: X The number of the missile indicator to update (counting from bottom-right to bottom-left, then top-left and top-right, so indicator NOMSL is the top-right indicator) Y The pattern number for the new missile indicator: * 133 = no missile indicator * 109 = red (armed and locked) * 108 = black (disarmed) The armed missile flashes black and red, so the tile is swapped between 108 and 109 in the main loop
Returns: X X is preserved Y Y is set to 0
.MSBAR TYA ; Store the pattern number on the stack so we can PHA ; retrieve it later LDY missileNames,X ; Set Y to the X-th entry from the missileNames table, ; so Y is the offset of missile X's indicator in the ; nametable buffer, from the start of row 22 PLA ; Set the nametable buffer entry to the pattern number STA nameBuffer0+22*32,Y LDY #0 ; Set Y = 0, so we can return it from the subroutine RTS ; Return from the subroutine
Name: missileNames [Show more] Type: Variable Category: Dashboard Summary: Tile numbers for the four missile indicators on the dashboard, as offsets from the start of tile row 22
Context: See this variable on its own page References: This variable is used as follows: * MSBAR uses missileNames

The active missile (i.e. the one that is armed and fired first) is the one with the highest number, so missile 4 (top-left) will be armed before missile 3 (top-right), and so on.
.missileNames EQUB 0 ; Missile numbers are from 1 to 4, so this value is ; never used EQUB 95 ; Missile 1 (bottom-right) EQUB 94 ; Missile 2 (bottom-left) EQUB 63 ; Missile 3 (top-right) EQUB 62 ; Missile 4 (top-left)
Name: autoPlayKeys1_EN [Show more] Type: Variable Category: Combat demo Summary: Auto-play commands for the first part of the auto-play combat demo (combat practice) when English is the chosen language Deep dive: Multi-language support in NES Elite Auto-playing the combat demo
Context: See this variable on its own page References: This variable is used as follows: * autoPlayKeys1Hi uses autoPlayKeys1_EN * autoPlayKeys1Lo uses autoPlayKeys1_EN
.autoPlayKeys1_EN ; At this point the we are at the title screen, which ; will show the rotating Cobra Mk III before starting ; the combat demo in auto-play mode EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks ; At this point the combat demo starts EQUB $C2 ; Do nothing (%00000000) while MANY+19 = 0 (i.e. wait EQUB %00000000 ; until the Sidewinder - ship type 19 - is spawned) EQUW MANY+19 EQUB $8A ; Do nothing for 10 * 4 = 40 VBlanks EQUB %01000000 ; Press the A button (%01000000) for 4 VBlanks to fire EQUB 4 ; the laser (this kills the first ship) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $C2 ; Do nothing (%00000000) while FRIN+4 = 0 (i.e. wait EQUB %00000000 ; until the third ship is spawned in ship slot 4) EQUW FRIN+4 EQUB $9C ; Do nothing for 12 * 4 = 48 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 20 VBlanks to EQUB 20 ; pull the nose up EQUB %01000100 ; Press the down and A buttons (%01000100) for 6 VBlanks EQUB 6 ; to pull the nose up and fire the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %00100001 ; Press the right and B buttons (%01000100) for 14 EQUB 14 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $8D ; Do nothing for 13 * 4 = 52 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 31 VBlanks to EQUB 31 ; roll to the right (clockwise) EQUB %00000001 ; EN/FR: Press the right button (%00000001) for 21 EQUB 21 ; VBlanks to roll to the right (clockwise) EQUB %00001000 ; Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB $8E ; Do nothing for 14 * 4 = 56 VBlanks EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; EN: Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB %00001000 ; EN: Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $C3 ; Press the up button (%00001000) while bit 7 of MSTG is EQUB %00001000 ; set (i.e. pull up until the missile has locked onto a EQUW MSTG ; target) EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. fire the missile), ; which sends a missile to kill the second ship EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; EN: Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; EN: Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 22 EQUB 22 ; VBlanks to move the icon bar pointer to the left ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. to change from front ; view to rear view) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; EN: Press the right button (%00000001) for 8 VBlanks EQUB 8 ; to roll to the right (clockwise) EQUB %00000100 ; EN: Press the down button (%00000100) for 31 VBlanks EQUB 31 ; to pull the nose up EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; EN: Press the down button (%00000100) for 19 VBlanks EQUB 19 ; to pull the nose up EQUB %00100100 ; EN: Press the down and B buttons (%00100100) for 17 EQUB 17 ; VBlanks to reduce our speed EQUB $C3 ; Do nothing (%00000000) while bit 7 of MSTG is set EQUB %00000000 ; (i.e. do nothing until the missile has locked onto a EQUW MSTG ; target) EQUB $C0 ; Switch to the autoPlayKeys2 table in the next VBlank ; to move on to the second part of the auto-play demo, ; which demonstrates the game itself
Name: autoPlayKeys1_DE [Show more] Type: Variable Category: Combat demo Summary: Auto-play commands for the first part of the auto-play combat demo (combat practice) when German is the chosen language Deep dive: Multi-language support in NES Elite Auto-playing the combat demo
Context: See this variable on its own page References: This variable is used as follows: * autoPlayKeys1Hi uses autoPlayKeys1_DE * autoPlayKeys1Lo uses autoPlayKeys1_DE
.autoPlayKeys1_DE ; At this point the we are at the title screen, which ; will show the rotating Cobra Mk III before starting ; the combat demo in auto-play mode EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks ; At this point the combat demo starts EQUB $C2 ; Do nothing (%00000000) while MANY+19 = 0 (i.e. wait EQUB %00000000 ; until the Sidewinder - ship type 19 - is spawned) EQUW MANY+19 EQUB $8A ; Do nothing for 10 * 4 = 40 VBlanks EQUB %01000000 ; Press the A button (%01000000) for 4 VBlanks to fire EQUB 4 ; the laser (this kills the first ship) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $C2 ; Do nothing (%00000000) while FRIN+4 = 0 (i.e. wait EQUB %00000000 ; until the third ship is spawned in ship slot 4) EQUW FRIN+4 EQUB $9C ; Do nothing for 12 * 4 = 48 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 20 VBlanks to EQUB 20 ; pull the nose up EQUB %01000100 ; Press the down and A buttons (%01000100) for 6 VBlanks EQUB 6 ; to pull the nose up and fire the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %00100001 ; Press the right and B buttons (%01000100) for 14 EQUB 14 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $8D ; Do nothing for 13 * 4 = 52 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 31 VBlanks to EQUB 31 ; roll to the right (clockwise) EQUB %00000001 ; DE: Press the right button (%00000001) for 19 VBlanks EQUB 19 ; to roll to the right (clockwise) EQUB %00001000 ; Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB $8E ; Do nothing for 14 * 4 = 56 VBlanks EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; DE: Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; DE: Press the up button (%00001000) for 22 VBlanks to EQUB 22 ; pitch down EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $C3 ; Press the up button (%00001000) while bit 7 of MSTG is EQUB %00001000 ; set (i.e. pull up until the missile has locked onto a EQUW MSTG ; target) EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. fire the missile), ; which sends a missile to kill the second ship EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; DE: Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 22 EQUB 22 ; VBlanks to move the icon bar pointer to the left ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. to change from front ; view to rear view) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; DE: Press the right button (%00000001) for 12 VBlanks EQUB 12 ; to roll to the right (clockwise) EQUB %00000100 ; DE: Press the down button (%00000100) for 31 VBlanks EQUB 31 ; to pull the nose up EQUB %00000100 ; DE: Press the down button (%00000100) for 30 VBlanks EQUB 30 ; to pull the nose up EQUB %00100100 ; DE: Press the down and B buttons (%00100100) for 22 EQUB 22 ; VBlanks to reduce our speed EQUB $C3 ; Do nothing (%00000000) while bit 7 of MSTG is set EQUB %00000000 ; (i.e. do nothing until the missile has locked onto a EQUW MSTG ; target) EQUB $C0 ; Switch to the autoPlayKeys2 table in the next VBlank ; to move on to the second part of the auto-play demo, ; which demonstrates the game itself
Name: autoPlayKeys1_FR [Show more] Type: Variable Category: Combat demo Summary: Auto-play commands for the first part of the auto-play combat demo (combat practice) when French is the chosen language Deep dive: Multi-language support in NES Elite Auto-playing the combat demo
Context: See this variable on its own page References: This variable is used as follows: * autoPlayKeys1Hi uses autoPlayKeys1_FR * autoPlayKeys1Lo uses autoPlayKeys1_FR
.autoPlayKeys1_FR ; At this point the we are at the title screen, which ; will show the rotating Cobra Mk III before starting ; the combat demo in auto-play mode EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks ; At this point the combat demo starts EQUB $C2 ; Do nothing (%00000000) while MANY+19 = 0 (i.e. wait EQUB %00000000 ; until the Sidewinder - ship type 19 - is spawned) EQUW MANY+19 EQUB $8A ; Do nothing for 10 * 4 = 40 VBlanks EQUB %01000000 ; Press the A button (%01000000) for 4 VBlanks to fire EQUB 4 ; the laser (this kills the first ship) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $C2 ; Do nothing (%00000000) while FRIN+4 = 0 (i.e. wait EQUB %00000000 ; until the third ship is spawned in ship slot 4) EQUW FRIN+4 EQUB $9C ; Do nothing for 12 * 4 = 48 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 20 VBlanks to EQUB 20 ; pull the nose up EQUB %01000100 ; Press the down and A buttons (%01000100) for 6 VBlanks EQUB 6 ; to pull the nose up and fire the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to fire EQUB 31 ; the lasers EQUB %00100001 ; Press the right and B buttons (%01000100) for 14 EQUB 14 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $8D ; Do nothing for 13 * 4 = 52 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 31 VBlanks to EQUB 31 ; roll to the right (clockwise) EQUB %00000001 ; FR/EN: Press the right button (%00000001) for 21 EQUB 21 ; VBlanks to roll to the right (clockwise) EQUB %00001000 ; Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB $8E ; Do nothing for 14 * 4 = 56 VBlanks EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; FR: Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; FR: Press the up button (%00001000) for 20 VBlanks to EQUB 20 ; pitch down EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks ; and onto the Fire Missile button EQUB $C3 ; Press the up button (%00001000) while bit 7 of MSTG is EQUB %00001000 ; set (i.e. pull up until the missile has locked onto a EQUW MSTG ; target) EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. fire the missile), ; which sends a missile to kill the second ship EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; FR: Do nothing for 31 * 4 = 124 VBlanks EQUB $98 ; FR: Do nothing for 24 * 4 = 96 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 22 EQUB 22 ; VBlanks to move the icon bar pointer to the left ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. to change from front ; view to rear view) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; FR: Press the right button (%00000001) for 14 VBlanks EQUB 14 ; to roll to the right (clockwise) EQUB %00000100 ; FR: Press the down button (%00000100) for 31 VBlanks EQUB 31 ; to pull the nose up EQUB %00100100 ; FR: Press the down and B buttons (%00100100) for 17 EQUB 17 ; VBlanks to reduce our speed EQUB %00000100 ; FR: Press the down button (%00000100) for 28 VBlanks EQUB 28 ; to pull the nose up EQUB $C3 ; Do nothing (%00000000) while bit 7 of MSTG is set EQUB %00000000 ; (i.e. do nothing until the missile has locked onto a EQUW MSTG ; target) ; Fall through into the autoPlayKeys2 table to move on ; to the second part of the auto-play demo, which ; demonstrates the game itself
Name: autoPlayKeys2 [Show more] Type: Variable Category: Combat demo Summary: Auto-play commands for the second part of the auto-play demo (demonstrating the game itself) Deep dive: Auto-playing the combat demo
Context: See this variable on its own page References: This variable is used as follows: * AutoPlayDemo uses autoPlayKeys2
.autoPlayKeys2 EQUB $89 ; Do nothing for 9 * 4 = 36 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. fire the missile), ; which sends a missile to kill the third ship EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00101000 ; Press the up and B buttons (%00101000) for 25 EQUB 25 ; VBlanks to increase our speed EQUB $C2 ; Do nothing (%00000000) while QQ12 = 0 (i.e. wait until EQUB %00000000 ; we are docked, which will happen when the combat demo EQUW QQ12 ; finishes after killing the third ship and showing the ; scroll text with the results of combat practice ; At this point the combat demo has finished and we are ; back at the title screen EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 22 EQUB 22 ; VBlanks to move the icon bar pointer to the left ; and onto the Equip Ship button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Equip Ship) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00000100 ; FR: Press the down button (%00000100) for 4 VBlanks EQUB 4 ; to move the cursor down the list of equipment by one ; row onto the missile entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %01000000 ; Press the A button (%01000000) for 4 VBlanks to buy a EQUB 4 ; missile EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the left ; and onto the Market Price button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Market Price) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have two) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have three) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have four) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have five) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have six) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have seven) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Food (so we now have eight) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Textiles ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one tonne of Textiles EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Radioactives ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Robot Slaves ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Beverages ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Luxuries ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Rare Species ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Computers ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Machinery ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Alloys ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Firearms ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Furs ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Minerals ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Gold ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Platinum ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Gem-stones ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 4 VBlanks to EQUB 4 ; buy one gram of Gem-stones EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Inventory) to show the ; cargo that we just bought EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the left ; and onto the Launch button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Launch) to launch from ; the space station ; At this point we are back in space, outside the space ; station at Lave EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 22 EQUB 22 ; VBlanks to move the icon bar pointer to the right ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. to change from front ; view to rear view) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00001000 ; Press the up button (%00001000) for 30 VBlanks to EQUB 30 ; pitch down EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the left ; and onto the Charts button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the Short-range Chart) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. System Data) to show ; the system data for Lave EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the Short-range Chart) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00000001 ; Press the right button (%00000001) for 31 VBlanks to EQUB 31 ; move the crosshairs to the right EQUB %00000101 ; Press the right and down buttons (%00000101) for 31 EQUB 31 ; VBlanks to move the crosshairs down and to the right EQUB %00000001 ; Press the right button (%00000001) for 5 VBlanks to EQUB 5 ; move the crosshairs to the right, so Zaonce gets ; selected EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. System Data) to show ; the system data for Zaonce EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the Short-range Chart) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the left ; and onto the Charts icon EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the Long-range Chart) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the Short-range Chart) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 26 EQUB 26 ; VBlanks to move the icon bar pointer to the right ; and onto the Hyperspace button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. hyperspace) to start ; the hyperspace countdown EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $96 ; Do nothing for 22 * 4 = 88 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the left ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the front view) so we ; can see Lave in front of us EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $C4 ; Do nothing (%00000000) while bit 7 of FRIN+1 is clear EQUB %00000000 ; (i.e. do nothing until the sun has been spawned in EQUW FRIN+1 ; the second ship slot, at which point we know we have ; arrived in Zaonce) EQUB %00000010 ; Press the left button (%00000010) for 22 VBlanks to EQUB 22 ; roll to the left (anti-clockwise) EQUB %00000100 ; Press the down button (%00000100) for 30 VBlanks to EQUB 30 ; pull the nose up EQUB %00100001 ; Press the right and B buttons (%01000100) for 14 EQUB 34 ; VBlanks to move the icon bar pointer to the right ; and onto the Fast-forward button (for an in-system ; jump) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. do an in-system jump) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. do a second in-system ; jump) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. do a third in-system ; jump) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. do a fourth in-system ; jump) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $C2 ; Do nothing (%00000000) while MANY+2 = 0 (i.e. wait EQUB %00000000 ; until the space station - ship type 2 - is spawned) EQUW MANY+2 EQUB %00100010 ; Press the left and B buttons (%00100010) for 22 EQUB 58 ; VBlanks to move the icon bar pointer to the left ; and onto the Docking Computer button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. engage the docking ; computer) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $C2 ; Do nothing (%00000000) while QQ12 = 0 (i.e. wait until EQUB %00000000 ; we are docked) EQUW QQ12 ; At this point we are docked in Zaonce EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%01000100) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Market Price button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Market Price) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have seven tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have six tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have five tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have four tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have three tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have two tonnes ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we now have one tonne ; left) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Food (so we have sold them all) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000100 ; Press the down button (%00000100) for 4 VBlanks to EQUB 4 ; move the highlight down one row onto the Textiles ; entry EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00000010 ; Press the left button (%00000010) for 4 VBlanks to EQUB 4 ; sell one tonne of Textiles (so we have sold them all) EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the right ; and onto the Equip Ship button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Equip Ship) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to buy EQUB 31 ; fuel EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to buy EQUB 31 ; fuel EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to buy EQUB 31 ; fuel EQUB %01000000 ; Press the A button (%01000000) for 31 VBlanks to buy EQUB 31 ; fuel EQUB %00100010 ; Press the left and B buttons (%00100010) for 54 EQUB 54 ; VBlanks to move the icon bar pointer to the left ; and onto the Launch button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Launch) to launch from ; the space station ; At this point we are back in space, outside the space ; station at Zaonce EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00101000 ; Press the up and B buttons (%00101000) for 10 EQUB 10 ; VBlanks to increase our speed EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 14 EQUB 14 ; VBlanks to move the icon bar pointer to the right ; and onto the Status Mode button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Status Mode) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 14 EQUB 14 ; VBlanks to move the icon bar pointer to the right ; and onto the Front View button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. the front view) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 18 EQUB 18 ; VBlanks to move the icon bar pointer to the right ; and onto the Target Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00100100 ; Press the down and B buttons (%00100100) for 31 EQUB 31 ; VBlanks to reduce our speed EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB %00001000 ; Press the up button (%00001000) for 31 VBlanks to EQUB 31 ; pitch down EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. target the missile) EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $C3 ; Press the up button (%00001000) while bit 7 of MSTG is EQUB %00001000 ; set (i.e. pull up until the missile has locked onto EQUW MSTG ; the space station) EQUB $9F ; Do nothing for 31 * 4 = 124 VBlanks EQUB %00100001 ; Press the right and B buttons (%00100001) for 2 EQUB 2 ; VBlanks to move the icon bar pointer to the right ; and onto the Fire Missile button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. fire the missile), ; which fires a missile at the space station, triggering ; the station's E.C.M. EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB %00100010 ; Press the left and B buttons (%00100010) for 30 EQUB 30 ; VBlanks to move the icon bar pointer to the left ; and onto the Status Mode button EQUB $83 ; Do nothing for 3 * 4 = 12 VBlanks EQUB %00101000 ; Press the up and B buttons (%00101000) for 10 EQUB 10 ; VBlanks to increase our speed EQUB $C3 ; Do nothing (%00000000) while bit 7 of ENERGY is set EQUB %00000000 ; (i.e. do nothing until our energy levels start to EQUW ENERGY ; deplete as the station's Vipers attack us and blast ; away our shields) EQUB %00010000 ; Press the Select button (%00010000) for 3 VBlanks to EQUB 3 ; choose the selected icon (i.e. Status Mode) so we can ; see the commander image flashing with a red background ; until we finally reach the Game Over screen EQUB $88 ; Do nothing for 8 * 4 = 32 VBlanks EQUB $80 ; Quit auto-play and return to the title screen
Name: AutoPlayDemo [Show more] Type: Subroutine Category: Combat demo Summary: Automatically play the demo using the auto-play commands from the autoplayKeys tables Deep dive: Auto-playing the combat demo
Context: See this subroutine on its own page References: This subroutine is called as follows: * NMI calls AutoPlayDemo
.AutoPlayDemo LDA controller1A ; If no buttons are being pressed on controller 1, jump ORA controller1B ; to auto1 to continue with the auto-playing of the demo ORA controller1Left ORA controller1Right ORA controller1Up ORA controller1Down ORA controller1Start ORA controller1Select BPL auto1 LDA #0 ; Otherwise a button has been pressed, so we disable STA autoPlayDemo ; auto-play by setting autoPlayDemo to zero RTS ; Return from the subroutine .auto1 LDX autoPlayRepeat ; If autoPlayRepeat is non-zero then this means a BNE auto4 ; previous auto-play step has set a repeat action and ; we still have some repeats to go, so jump to auto4 ; to decrement the repeat counter and press the buttons ; in autoPlayKey for this VBlank LDY #0 ; Set Y = 0 to use as an index when fetching auto-play ; bytes from the relevant autoPlayKeys table LDA (autoPlayKeys),Y ; Set A to byte #1 of this auto-play command BMI auto5 ; If bit 7 of byte #1 is set, jump to auto5 ; If we get here then bit 7 of byte #1 is clear and A ; contains byte #1 STA autoPlayKey ; Set autoPlayKey to byte #1 so we perform the button ; presses in byte #1 INY ; Set A to byte #2 of this auto-play command LDA (autoPlayKeys),Y SEC ; Set the C flag so the addition below adds an extra 1, ; so autoPlayKeys(1 0) gets incremented by 2 (as we have ; just processed two bytes) TAX ; Set X to byte #2, so this gets set as the number of ; repeats in autoPlayRepeat .auto2 LDA #1 ; Set A = 1 so the following adds 1 + C to the address ; in autoPlayKeys(1 0), so we move the pointer to the ; byte we are processing next on by 1 + C bytes .auto3 ADC autoPlayKeys ; Set autoPlayKeys(1 0) = autoPlayKeys(1 0) + 1 + C STA autoPlayKeys BCC auto4 INC autoPlayKeys+1 .auto4 DEX ; Decrement the repeat counter in autoPlayRepeat, as we STX autoPlayRepeat ; are about to press the buttons LDA autoPlayKey ; Set A to the buttons to be pressed in autoPlayKey, ; which has the following format: ; ; * Bit 0 = right button ; * Bit 1 = left button ; * Bit 2 = down button ; * Bit 3 = up button ; * Bit 4 = Select button ; * Bit 5 = B button ; * Bit 6 = A button ; ; Bit 7 is always clear ASL controller1Right ; Set bit 7 of controller1Right to bit 0 of autoPlayKey LSR A ; to "press" the right button ROR controller1Right ASL controller1Left ; Set bit 7 of controller1Left to bit 0 of autoPlayKey LSR A ; to "press" the left button ROR controller1Left ASL controller1Down ; Set bit 7 of controller1Down to bit 0 of autoPlayKey LSR A ; to "press" the down button ROR controller1Down ASL controller1Up ; Set bit 7 of controller1Up to bit 0 of autoPlayKey LSR A ; to "press" the up button ROR controller1Up ASL controller1Select ; Set bit 7 of controller1Select to bit 0 of autoPlayKey LSR A ; to "press" the Select button ROR controller1Select ASL controller1B ; Set bit 7 of controller1B to bit 0 of autoPlayKey LSR A ; to "press" the B button ROR controller1B ASL controller1A ; Set bit 7 of controller1A to bit 0 of autoPlayKey LSR A ; to "press" the A button ROR controller1A RTS ; We have now pressed the correct buttons for this ; VBlank, so return from the subroutine .auto5 ; If we get here then bit 7 of byte #1 is set and A ; contains byte #1 ASL A ; Set A = A << 1, so A contains byte #1 << 1 BEQ auto14 ; If the result is zero then byte #1 must be $80, so ; jump to auto14 with A = 0 to terminate auto-play BMI auto7 ; If bit 6 of byte #1 is set, jump to auto7 ; If we get here then bit 7 of byte #1 is set, bit 6 of ; byte #1 is clear, and A contains byte #1 << 1 ; ; So byte #1 = $C0, which means we do nothing for ; 4 * byte #1 (ignoring bit 7 of byte #1) ASL A ; Set A = A << 1, so A contains byte #1 << 2 TAX ; Set X to byte #1 << 2, so this gets set as the number ; of repeats in autoPlayRepeat when we jump up to auto2 ; below (so this sets the number of repetitions to ; byte #1 << 2, which is 4 * byte #1 (if we ignore bit 7 ; of byte #1) .auto6 LDA #0 ; Set autoPlayKey = 0 so no buttons are pressed in the STA autoPlayKey ; next VBlank BEQ auto2 ; Jump to auto2 to process the button-pressing in this ; VBlank (this BEQ is effectively a JMP as A is always ; zero) .auto7 ; If we get here then bits 6 and 7 of byte #1 are set ; and A contains byte #1 << 1, so byte #1 is of the form ; $Cx, where x is any value ASL A ; Set A = A << 1, so A contains byte #1 << 2 BEQ auto13 ; If the result is zero then byte #1 must be $C0, so ; jump to auto13 to switch to the auto-play commands in ; the autoPlayKeys2 table, which we will start ; processing in the next NMI ; If we get here then bits 6 and 7 of byte #1 are set ; and A contains byte #1 << 1, so byte #1 is of the form ; $Cx where x is non-zero PHA ; Store byte #1 << 2 on the stack INY ; Set A to byte #2 of this auto-play command LDA (autoPlayKeys),Y STA autoPlayKey ; Set autoPlayKey to byte #2 so we perform the button ; presses in byte #2 INY ; Set A to byte #3 of this auto-play command LDA (autoPlayKeys),Y STA addr ; Set the low byte of addr(1 0) to byte #3 INY ; Set A to byte #4 of this auto-play command LDA (autoPlayKeys),Y STA addr+1 ; Set the high byte of addr(1 0) to byte #3, so we now ; have addr(1 0) = (byte #3 byte #4) ; We now process the auto-play commands for when byte #1 ; is $C1 through $C5 LDY #0 ; Set Y = 0 so we can use indirect addressing below (we ; do not change the value of Y, this is just so we can ; implement the non-existent LDA (addr) instruction by ; using LDA (addr),Y instead) LDX #1 ; Set X = 1 this gets set as the number of repeats in ; autoPlayRepeat when we jump up to auto2 below, so the ; command will do each button press just once before ; re-checking the criteria in the next VBlank PLA ; Set A = byte #1 << 2 ; ; In other words A is the low nibble of byte #1 ; multiplied by 4, so we can check this value to ; determine the command in byte #1, as follows: ; ; * If byte #1 = $C1, A = 1 * 4 = 4 ; ; * If byte #1 = $C2, A = 2 * 4 = 8 ; ; * If byte #1 = $C3, A = 3 * 4 = 12 ; ; * If byte #1 = $C4, A = 4 * 4 = 16 ; ; * If byte #1 = $C5, A = 5 * 4 = 20 CMP #8 ; If A >= 8 then byte #1 is not $C1, so jump to auto9 BCS auto9 ; If we get here then byte #1 is $C1, so we repeat the ; button presses in byte #2 while addr(1 0) <> 0 LDA (addr),Y ; Set A = addr(1 0) BNE auto4 ; If addr(1 0) <> 0, jump to auto4 to do the button ; presses in byte #2 (which we put into autoPlayKey ; above), and because we have not updated the pointer ; in autoPlayKeys(1 0), we will come back to this exact ; same check in the next VBlank, and so on until the ; condition changes and addr(1 0) = 0 ; If addr(1 0) = 0 then fall through into auto8 to ; advance the pointer in autoPlayKeys(1 0) by 4, so in ; the next VBlank, we move on to the next command after ; byte #3 .auto8 LDA #4 ; Set A = 4 and clear the C flag, so in the jump to CLC ; auto3, we advance the pointer in autoPlayKeys(1 0) by ; 4 and return from the subroutine BCC auto3 ; Jump to auto3 to advance the pointer and return from ; the subroutine (this BCC is effectively a JMP as we ; just cleared the C flag) .auto9 ; If we get here then byte #1 is $C2 to $C5, we just ; performed a CMP #8, and A = byte #1 << 2 BNE auto10 ; If A <> 8 then byte #1 is not $C2, so jump to auto10 ; If we get here then byte #1 is $C2, so we repeat the ; button presses in byte #2 while addr(1 0) = 0 LDA (addr),Y ; Set A = addr(1 0) BEQ auto4 ; If addr(1 0) = 0, jump to auto4 to do the button ; presses in byte #2 (which we put into autoPlayKey ; above), and because we have not updated the pointer ; in autoPlayKeys(1 0), we will come back to this exact ; same check in the next VBlank, and so on until the ; condition changes and addr(1 0) <> 0 BNE auto8 ; If addr(1 0) <> 0 then jump to auto8 to advance the ; pointer in autoPlayKeys(1 0) by 4, so in the next ; VBlank, we move on to the next command after byte #3 ; (this BNE is effectively a JMP as we just passed ; through a BEQ) .auto10 ; If we get here then byte #1 is $C3 to $C5, and ; A = byte #1 << 2 CMP #16 ; if A >= 16 then byte #1 is not $C3, so jump to auto11 BCS auto11 ; If we get here then byte #1 is $C3, so we repeat the ; button presses in byte #2 while bit 7 of addr(1 0) is ; set LDA (addr),Y ; Set A = addr(1 0) BMI auto4 ; If bit 7 of addr(1 0) is set, jump to auto4 to do the ; button presses in byte #2 (which we put into ; autoPlayKey above), and because we have not updated ; the pointer in autoPlayKeys(1 0), we will come back to ; this exact same check in the next VBlank, and so on ; until the condition changes and bit 7 of addr(1 0) is ; clear BPL auto8 ; If bit 7 of addr(1 0) is clear then jump to auto8 to ; advance the pointer in autoPlayKeys(1 0) by 4, so in ; the next VBlank, we move on to the next command after ; byte #3 (this BPL is effectively a JMP as we just ; passed through a BMI) .auto11 ; If we get here then byte #1 is $C4 to $C5, we just ; performed a CMP #16, and A = byte #1 << 2 BNE auto12 ; If A <> 16 then byte #1 is not $C4, so jump to auto12 ; If we get here then byte #1 is $C4, so we repeat the ; button presses in byte #2 while bit 7 of addr(1 0) is ; clear LDA (addr),Y ; Set A = addr(1 0) BMI auto8 ; If bit 7 of addr(1 0) is set then jump to auto8 to ; advance the pointer in autoPlayKeys(1 0) by 4, so in ; the next VBlank, we move on to the next command after ; byte #3 (this BPL is effectively a JMP as we just ; passed through a BMI) JMP auto4 ; Otherwise bit 7 of addr(1 0) is clear, so jump to ; auto4 to do the button presses in byte #2 (which we ; put into autoPlayKey above), and because we have not ; updated the pointer in autoPlayKeys(1 0), we will come ; back to this exact same check in the next VBlank, and ; so on until the condition changes and bit 7 of ; addr(1 0) is set .auto12 ; If we get here then byte #1 is $C5, so we terminate ; auto-play with the Start button being held down LDA #%11000000 ; Set bits 6 and 7 of controller1Start to simulate the STA controller1Start ; Start button being held down for two VBlanks LDX #22 ; Set X = 22, so this gets set as the number of repeats ; autoPlayRepeat when we jump to auto2 via auto6 below ; (so this ensures we do nothing for 22 VBlanks after ; pressing the Start button) CLC ; Clear the C flag so the jump to auto2 via auto6 only ; adds one to the pointer in autoPlayKeys(1 0), so we ; move on to the command after byte #1 when we have ; completed the 22 VBlanks of inactivity BCC auto6 ; Jump to auto6 to set autoPlayKey = 0 so no buttons are ; pressed in the following VBlanks, and move on to auto2 ; to process the button-pressing in this VBlank (this ; BCC is effectively a JMP as we just cleared the C ; flag) .auto13 ; If we get here then byte #1 is $C0 and we need to ; switch to the auto-play commands in the autoPlayKeys2 ; table, which we will start processing in the next NMI LDA #HI(autoPlayKeys2) ; Set autoPlayKeys(1 0) = autoPlayKeys2 STA autoPlayKeys+1 ; LDA #LO(autoPlayKeys2) ; So the next time we call AutoPlayDemo, in the next STA autoPlayKeys ; call to the NMI handler at the next VBlank, we will ; start pulling auto-play commands from autoPlayKeys2 ; instead of the language-specific table we've been ; using up to this point RTS ; Return from the subroutine .auto14 ; If we get here then byte #1 is $80 and we need to ; terminate auto-play STA autoPlayDemo ; We jump here with A = 0, so this disables auto-play ; by setting autoPlayDemo to zero RTS ; Return from the subroutine
Name: HideIconBarPointer [Show more] Type: Subroutine Category: Icon bar Summary: Clear the icon bar choice and hide the icon bar pointer
Context: See this subroutine on its own page References: This subroutine is called as follows: * MoveIconBarPointer calls HideIconBarPointer * MoveIconBarPointer calls via hipo2

Other entry points: hipo2 Clear the icon button choice and hide the icon bar pointer
.HideIconBarPointer LDA controller1Start ; If the Start button on controller 1 was being held AND #%11000000 ; down (bit 6 is set) but is no longer being held down CMP #%01000000 ; (bit 7 is clear) then keep going, otherwise jump to BNE hipo1 ; hipo1 LDA #80 ; The Start button has been pressed and released, so STA iconBarChoice ; set iconBarChoice to 80 to record this BNE hipo3 ; Jump to hipo3 to hide the icon bar pointer and return ; from the subroutine .hipo1 LDA iconBarChoice ; If iconBarChoice = 80 then we have already recorded CMP #80 ; that the Start button has been pressed but this has BEQ hipo3 ; not yet been processed (as otherwise it would have ; been zeroed), so jump to hipo3 to hide the icon bar ; pointer and return from the subroutine .hipo2 LDA #0 ; Set iconBarChoice = 0 to clear the icon button choice STA iconBarChoice ; so we don't process it again .hipo3 LDA #240 ; Set A to the y-coordinate that's just below the bottom ; of the screen, so we can hide the icon bar pointer ; sprites by moving them off-screen STA ySprite1 ; Set the y-coordinates for the four icon bar pointer STA ySprite2 ; sprites to 240, to move them off-screen STA ySprite3 STA ySprite4 RTS ; Return from the subroutine
Name: SetIconBarPointer [Show more] Type: Subroutine Category: Icon bar Summary: Set the icon bar pointer to a specific position
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH2 calls SetIconBarPointer * SetDemoAutoPlay calls SetIconBarPointer

Arguments: A The button number on which to position the pointer
.SetIconBarPointer ASL A ; Set xIconBarPointer = A * 4 ASL A ; STA xIconBarPointer ; As xIconBarPointer contains the x-coordinate of the ; icon bar pointer, incrementing by 4 for each button LDX #0 ; Zero all the pointer timer and movement variables so STX pointerMoveCounter ; the pointer is not moving and the MoveIconBarPointer STX xPointerDelta ; routine does not start looking for double-taps of STX pointerPressedB ; the B button STX pointerTimer IF _PAL STX pointerTimerB ; Reset the PAL-specific timer that controls whether a ; tap on the B button is the second tap of a double-tap ENDIF RTS ; Return from the subroutine
Name: MoveIconBarPointer [Show more] Type: Subroutine Category: Icon bar Summary: Move the sprites that make up the icon bar pointer and record any choices Deep dive: Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * NMI calls MoveIconBarPointer
.MoveIconBarPointer ; This routine is called every VBlank and manages the ; movement of the icon bar pointer and choosing icon bar ; buttons ; ; We start by updating a couple of counters that are ; only used in the PAL version, and which are ignored in ; the NTSC version DEC pointerTimer ; Decrement the pointer timer ; ; This timer is used in the PAL version to detect the B ; button being pressed twice in quick succession ; ; The pointer timer is updated in the NTSC version but ; is otherwise ignored IF _PAL BNE mbar1 ; If the pointer timer has not reached zero, jump to ; mbar1 to skip the following instruction LSR pointerTimerB ; Zero pointerTimerB (this works because pointerTimerB ; is only ever 0 or 1) ; ; The pointerTimerB timer is used in the PAL version to ; detect the B button being pressed twice in quick ; succession, so zeroing it indicates that the timer has ; run down before the B button was pressed for the ; second time, so this can't be a double-tap ; ; The NTSC version does away with this variable ; altogether, as well as ignoring pointerTimer .mbar1 ENDIF BPL mbar2 ; If pointerTimer is positive, jump to mbar2 to skip ; the following instruction INC pointerTimer ; Increment pointerTimer so it doesn't decrement past ; zero .mbar2 DEC pointerMoveCounter ; Decrement the pointer move counter, which is used to ; keep track of whether the icon bar pointer is moving ; between two buttons (if the counter is non-zero, then ; the pointer is currently moving between two buttons) BPL mbar3 ; If pointerMoveCounter is positive, jump to mbar3 to ; skip the following instruction INC pointerMoveCounter ; Increment pointerMoveCounter so it doesn't decrement ; past zero .mbar3 ; We now confirm that there is an icon bar for us to ; manage (if not, we leave the subroutine at this point) LDA screenFadedToBlack ; If bit 7 of screenFadedToBlack is set then we have BMI hipo2 ; already faded the screen to black, so jump to hipo2 ; to clear the icon button choice and hide the icon bar ; pointer LDA showIconBarPointer ; If showIconBarPointer = 0 then the icon bar pointer BEQ HideIconBarPointer ; should be hidden, so jump to HideIconBarPointer to do ; just that ; If we get here then the icon bar pointer is visible, ; so we now need to process any movement before drawing ; the pointer in its new location ; ; The pointer coordinates are stored in xIconBarPointer ; and yIconBarPointer, though note that the x-coordinate ; in xIconBarPointer is multiplied by 5 to get the final ; pixel x-coordinate (the yIconBarPointer value, on the ; other hand, contains a pixel coordinate from the off) ; ; The movement of the pointer is stored in xPointerDelta ; as a delta, which is set later in the routine ; according to the buttons being pressed on the ; controller ; ; The xPointerDelta variable is set to zero by default, ; and is only non-zero if the previous call to this ; routine detected the correct movement buttons LDA xPointerDelta ; Set xIconBarPointer = xIconBarPointer + xPointerDelta CLC ; ADC xIconBarPointer ; So this updates the x-coordinate of the icon bar STA xIconBarPointer ; to move it in the direction of xPointerDelta (which ; was set to -1, 0 or +1 depending on which directional ; keys were being pressed the last time we were here) AND #3 ; If xIconBarPointer mod 4 is non-zero, jump to mbar9 to BNE mbar9 ; skip updating the movement delta in xPointerDelta, so ; we only scan for movement keys every four movements of ; the pointer (this ensures that when a movement starts, ; it runs for four VBlanks without being interrupted, so ; the pointer keeps moving towards the next button, one ; step for each VBlank) ; ; In other words, when xIconBarPointer mod 4 is 0, the ; pointer is on a button, while other values mean it is ; between buttons ; If we get here then the movement has been applied for ; four VBlanks, so the pointer has now moved onto the ; next button LDA #0 ; Set xPointerDelta = 0 so the pointer is not moving by STA xPointerDelta ; default, though we now change that if the movement ; buttons are being pressed LDA pointerMoveCounter ; If pointerMoveCounter is non-zero then we are already BNE mbar9 ; moving the pointer between two buttons, so jump to ; mbar9 to leave xPointerDelta at zero and ignore any ; button presses, as we need to finish the jump from one ; button to another before we can move it again ; ; This ensures that once we start a movement between ; icon bar buttons, we wait until pointerMoveCounter ; VBlanks have passed before listening for the next move ; ; pointerMoveCounter is set to 12 at the start of each ; move, so we spend the first four VBlanks moving the ; pointer, then xPointerDelta is zeroed above, and then ; we wait for another eight VBlanks before listening for ; button presses again ; ; This gives the icon bar pointer a stepped movement ; that jumps from button to button if the left or right ; buttons are held down LDA controller1B ; If the B button is not being pressed on controller 1 ORA numberOfPilots ; and the game is configured for one pilot, jump to BPL mbar9 ; mbar9 to skip updating the movement delta in ; xPointerDelta, as in one-pilot mode we can only move ; the icon bar pointer when the B button is held down ; ; If the game is configured for two pilots, we always ; pass through this branch as numberOfPilots = 1, so ; we don't need the B button to be held down when there ; are two pilots ; We now process the left button, which moves the icon ; bar pointer to the left LDX controller1Left ; If the left button on controller 1 is being pressed, BMI mbar4 ; jump to mbar4 to set xPointerDelta to -1 LDA #0 ; Otherwise reset controller1Left to 0 to clear out the STA controller1Left ; left button history in the controller variable, to ; make the logic below slightly simpler (see mbar13) JMP mbar6 ; Jump to mbar6 to check for the right button .mbar4 ; If we get here then the left button is being pressed ; and X contains the value of controller1Left LDA #$FF ; Set A = -1 to set as the value of xPointerDelta CPX #%10000000 ; If the left button has just been pressed but wasn't BNE mbar5 ; being pressed before, keep going, otherwise jump to ; mbar5 to skip the following ; The following is therefore only run when we first ; press the left button LDX #12 ; The left button was just pressed but wasn't being STX pointerMoveCounter ; pressed before, so set pointerMoveCounter to 12 so it ; can count down to zero, during which time we don't ; check for any more directional button presses (as the ; pointer will be moving between icon bar buttons) .mbar5 STA xPointerDelta ; Set xPointerDelta = -1, so the pointer moves to the ; left .mbar6 ; We now process the right button, which moves the icon ; bar pointer to the right LDX controller1Right ; If the right button on controller 1 is being pressed, BMI mbar7 ; jump to mbar7 to set xPointerDelta to 1 LDA #0 ; Reset controller1Right to 0 to clear out the right STA controller1Right ; button history in the controller variable, to ; make the logic below slightly simpler (see mbar13) JMP mbar9 ; Jump to mbar9 to move on to clipping the pointer's ; x-coordinate .mbar7 LDA #1 ; Set A = 1 to set as the value of xPointerDelta CPX #%10000000 ; If the right button has just been pressed but wasn't BNE mbar8 ; being pressed before, keep going, otherwise jump to ; mbar8 to skip the following ; The following is therefore only run when we first ; press the right button LDX #12 ; The right button was just pressed but wasn't being STX pointerMoveCounter ; pressed before, so set pointerMoveCounter to 12 so it ; can count down to zero, during which time we don't ; check for any more directional button presses (as the ; pointer will be moving between icon bar buttons) .mbar8 STA xPointerDelta ; Set xPointerDelta = 1, so the pointer moves to the ; left .mbar9 ; We now clip the x-coordinate of the pointer to ensure ; it is in the range 0 to 44 (which equates to a pixel ; range of 0 to 44 * 5 = 220) LDA xIconBarPointer ; If xIconBarPointer < 128, jump to mbar10 to skip the BPL mbar10 ; following LDA #0 ; If we get here then xIconBarPointer >= 128, so set STA xPointerDelta ; xPointerDelta = 0 to stop the pointer from moving BEQ mbar11 ; Jump to mbar11 with A = 0 to set xIconBarPointer = 0, ; so the value of xIconBarPointer wraps around to zero ; if it goes above 127 (this BEQ is effectively a JMP ; as A is always zero) .mbar10 CMP #45 ; If xIconBarPointer < 45, jump to mbar11 to move on to BCC mbar11 ; the next set of checks LDA #0 ; If we get here then 45 <= xIconBarPointer < 127, so STA xPointerDelta ; set xPointerDelta = 0 to stop the pointer from moving LDA #44 ; Set A = 44 to store in xIconBarPointer, so the value ; of xIconBarPointer never gets above 44 .mbar11 STA xIconBarPointer ; Set xIconBarPointer to the clipped value in A, so ; xIconBarPointer is in the range 0 to 44 ; We now draw the icon bar pointer in either the up or ; down position ; ; We draw it in the up position if any of the following ; are true: ; ; * xIconBarPointer mod 4 is non-zero (in which case ; we know that the pointer is moving between ; buttons) ; ; * xPointerDelta is non-zero (so the pointer only ; ever moves when it is in the up position) ; ; * The B button is being pressed (which is the button ; we press to lift the pointer up) ; ; * The Select button is being pressed (so the pointer ; jumps up when we choose an icon from the icon bar ; with Select) ; ; Otherwise we draw it in the down position LDA xIconBarPointer ; If xIconBarPointer mod 4 is non-zero or xPointerDelta AND #3 ; is non-zero, then as noted above, this means that the ORA xPointerDelta ; pointer is between buttons, so jump to mbar12 to draw BNE mbar12 ; the icon bar pointer in the up position LDA controller1B ; If the B button is being pressed, jump to mbar12 to BMI mbar12 ; draw the icon bar pointer in the up position (this LDA controller1B ; comparison is repeated, but that doesn't seem to have BMI mbar12 ; any effect) LDA controller1Select ; If the Select button is being pressed, jump to mbar12 BNE mbar12 ; to draw the icon bar pointer in the up position ; If we get here then the B button is not being pressed, ; so we draw the icon bar pointer in the down position, ; so it looks as if it goes around the bottom of the ; button ; ; The pointer is made up of the following sprites, which ; are ordered in a clockwise fashion: ; ; * Sprite 1 in the top-left ; * Sprite 2 in the top-right ; * Sprite 3 in the bottom-right ; * Sprite 4 in the bottom-left ; ; The value of yIconBarPointer contains the y-coordinate ; of the icon bar, which is 148 when there is a ; dashboard or this is the Game Over screen, or 204 ; otherwise ; ; The value of xIconBarPointer is in the range 0 to 44, ; which represents the icon bar with buttons on each ; multiple of 4 LDA #251 ; Set the pattern number for the sprites 1 and 2 to STA pattSprite1 ; pattern 251, so the top part of the pointer appears to STA pattSprite2 ; go behind the button LDA yIconBarPointer ; Set the y-coordinate of the top of the pointer in CLC ; sprites 1 and 2 to yIconBarPointer + 11, so the ADC #11+YPAL ; pointer is drawn in the down position (three pixels STA ySprite1 ; lower down the screen than the up position) STA ySprite2 LDA xIconBarPointer ; Set A = 6 + xIconBarPointer * 4 + xIconBarPointer ASL A ; = 6 + 5 * xIconBarPointer ASL A ; ADC xIconBarPointer ; As noted above, xIconBarPointer is in the range 0 to ADC #6 ; 44, so the pixel x-coordinate of the pointer is in ; the range 6 to 226 ; We now use A as the x-coordinate of the bottom-left ; corner of the four-sprite pointer by setting the ; sprite's coordinates as follows (with the bottom ; sprites being spread out slightly more than the top ; sprites) STA xSprite4 ; Set the x-coordinate of sprite 4 in the bottom-left ; of the pointer to A ADC #1 ; Set the x-coordinate of sprite 1 in the top-left of STA xSprite1 ; the pointer to A + 1 ADC #13 ; Set the x-coordinate of sprite 2 in the top-right of STA xSprite2 ; the pointer to A + 14 ADC #1 ; Set the x-coordinate of sprite 3 in the bottom-right STA xSprite3 ; of the pointer to A + 15 LDA yIconBarPointer ; Set the y-coordinate of the bottom of the pointer in CLC ; sprites 3 and 4 to yIconBarPointer + 19, so the ADC #19+YPAL ; pointer is drawn in the down position (three pixels STA ySprite4 ; lower down the screen than the up position) STA ySprite3 LDA xIconBarPointer ; If xIconBarPointer is non-zero then jump to mbar13 BNE mbar13 ; (though this has no effect as that's what we're about ; to do anyway) JMP mbar13 ; Jump to mbar13 to continue checking for button presses .mbar12 ; If we get here then the B button is being pressed, so ; we draw the icon bar pointer in the up position, so it ; looks as if can be moved left or right without being ; blocked by the buttons ; ; The pointer is made up of the following sprites, which ; are ordered in a clockwise fashion: ; ; * Sprite 1 in the top-left ; * Sprite 2 in the top-right ; * Sprite 3 in the bottom-right ; * Sprite 4 in the bottom-left ; ; The value of yIconBarPointer contains the y-coordinate ; of the icon bar, which is 148 when there is a ; dashboard or this is the Game Over screen, or 204 ; otherwise ; ; The value of xIconBarPointer is in the range 0 to 44, ; which represents the icon bar with buttons on each ; multiple of 4 LDA #252 ; Set the pattern number for the sprites 1 and 2 to STA pattSprite1 ; pattern 252, so the top part of the pointer appears to STA pattSprite2 ; pop up from behind the top of the button LDA yIconBarPointer ; Set the y-coordinate of the top of the pointer in CLC ; sprites 1 and 2 to yIconBarPointer + 8, so the ADC #8+YPAL ; pointer is drawn in the up position (three pixels STA ySprite1 ; higher up the screen than the down position) STA ySprite2 LDA xIconBarPointer ; Set A = 6 + xIconBarPointer * 4 + xIconBarPointer ASL A ; = 6 + 5 * xIconBarPointer ASL A ; ADC xIconBarPointer ; As noted above, xIconBarPointer is in the range 0 to ADC #6 ; 44, so the pixel x-coordinate of the pointer is in ; the range 6 to 226 ; We now use A as the x-coordinate of the bottom-left ; corner of the four-sprite pointer by setting the ; sprite's coordinates as follows (with the bottom ; sprites being spread out slightly more than the top ; sprites) STA xSprite4 ; Set the x-coordinate of sprite 4 in the bottom-left ; of the pointer to A ADC #1 ; Set the x-coordinate of sprite 1 in the top-left of STA xSprite1 ; the pointer to A + 1 ADC #13 ; Set the x-coordinate of sprite 2 in the top-right of STA xSprite2 ; the pointer to A + 14 ADC #1 ; Set the x-coordinate of sprite 3 in the bottom-right STA xSprite3 ; of the pointer to A + 15 LDA yIconBarPointer ; Set the y-coordinate of the bottom of the pointer in CLC ; sprites 3 and 4 to yIconBarPointer + 16, so the ADC #16+YPAL ; pointer is drawn in the up position (three pixels STA ySprite4 ; higher up the screen than the down position) STA ySprite3 .mbar13 ; We now check the controller buttons to see if an icon ; bar button has been chosen ; ; The logic for the PAL version is rather more ; convoluted than the NTSC version LDA controller1Left ; If none of the directional buttons are being pressed, ORA controller1Right ; jump to mbar14 to skip the following ORA controller1Up ORA controller1Down BPL mbar14 LDA #0 ; At least one of the directional buttons is being STA pointerPressedB ; pressed, so set pointerPressedB = 0 so we don't look ; for a double-tap of the B button .mbar14 LDA controller1Select ; If the Select button has just been pressed but wasn't AND #%11110000 ; being pressed before, jump to mbar17 to choose the CMP #%10000000 ; button under the pointer BEQ mbar17 LDA controller1B ; If the B button has just been pressed but wasn't being AND #%11000000 ; pressed before, keep going, otherwise jump to mbar15 CMP #%10000000 ; to skip the following BNE mbar15 LDA #30 ; The B button has just been pressed but wasn't being STA pointerPressedB ; pressed before, so set pointerPressedB to a non-zero ; value so we start looking for a double-tap of the B ; button ; ; This value of A also ensures that we jump to mbar18 ; in the next comparison, as %01000000 does not match ; 30, so this essentially sets pointerPressedB to a ; non-zero value and then moves on to the next VBlank, ; leaving the non-zero value to be picked up in a future ; VBlank below .mbar15 CMP #%01000000 ; If the B button was being pressed but has just been BNE mbar18 ; released, keep going, otherwise jump to mbar18 to move ; onto the next set of checks IF _NTSC LDA pointerPressedB ; If pointerPressedB = 0 then jump to mbar18 to move BEQ mbar18 ; onto the next set of checks ; If we get here then pointerPressedB is non-zero, so we ; know the B button was pressed and released in a ; previous VBlank, and we know that in this VBlank, the ; B button was being pressed but has just been released, ; so that's a double-tap on the B button ; ; This is one of the ways of choosing an icon bar icon, ; so fall through into mbar17 to choose the button under ; the pointer ELIF _PAL LDA pointerPressedB ; If pointerPressedB is non-zero then the B button was BNE mbar16 ; tapped and released in a previous VBlank, so jump to ; mbar16 to potentially process a double-tap on the B ; button STA pointerTimerB ; Otherwise zero pointerTimerB, so pointerTimerB is zero ; if we are not looking for a double-tap BEQ mbar18 ; Jump to mbar18 to move on to the next set of checks ; (this BEQ is effectively a JMP as A is always zero) .mbar16 ; If we get here then the B button was tapped and ; released in a previous VBlank LDA #40 ; Set pointerTimer = 40 so it counts down over the next STA pointerTimer ; 40 VBlanks, zeroing pointerTimerB if it runs out ; before the second tap on the B button is detected LDA pointerTimerB ; If pointerTimerB = 1 then we are already looking for BNE mbar17 ; the second tap of a double-tap on the B button, which ; we just found, so jump to mbar17 to choose the button ; under the pointer INC pointerTimerB ; Otherwise increment pointerTimerB to 1 and skip the BNE mbar18 ; following instruction, so we will be on the lookout ; for the second tap of the B button in future VBlanks ENDIF .mbar17 IF _PAL LSR pointerTimerB ; Set pointerTimerB = 0 as we have now chosen an icon ; bar button, so we don't need to check for a double-tap ; on the B button any more ENDIF ; If we get here then we have chosen a button on the ; icon bar, so we update iconBarChoice accordingly LDA xIconBarPointer ; Set Y to the button number that the icon bar pointer LSR A ; is over LSR A TAY LDA (barButtons),Y ; Set iconBarChoice to the Y-th entry from the button STA iconBarChoice ; table for this icon bar to indicate that this icon bar ; button has been selected .mbar18 ; Finally, we check to see if the Start button has been ; tapped, and if so, we record that as an iconBarChoice ; of 80 LDA controller1Start ; If the Start button on controller 1 was being held AND #%11000000 ; down (bit 6 is set) but is no longer being held down CMP #%01000000 ; (bit 7 is clear) then keep going, otherwise jump to BNE mbar19 ; mbar19 LDA #80 ; Set iconBarChoice to indicate that the Start button STA iconBarChoice ; has been pressed .mbar19 RTS ; Return from the subroutine
Name: SetControllerPast [Show more] Type: Subroutine Category: Controllers Summary: Set the controller history variables to the values from four VBlanks ago Deep dive: Bolting NES controllers onto the key logger
Context: See this subroutine on its own page References: This subroutine is called as follows: * UpdateJoystick calls SetControllerPast
.SetControllerPast LDA controller1B ; If the B button is being held down, jump to past1 to BNE past1 ; zero the controller history variables, as we don't ; need the controller history for the icon bar movement ; (which is done by holding down the B button while ; using the left and right buttons) LDA controller1Left ; Set the high nibble of the left button history ASL A ; variable to bits 0 to 3 of controller1Left, so it ASL A ; contains the controller values from four VBlanks ago ASL A ASL A STA controller1Left03 LDA controller1Right ; Set the high nibble of the right button history ASL A ; variable to bits 0 to 3 of controller1Right, so it ASL A ; contains the controller values from four VBlanks ago ASL A ASL A STA controller1Right03 RTS ; Return from the subroutine .past1 LDA #0 ; Zero the controller history variables as we don't need STA controller1Left03 ; them for moving the icon bar pointer STA controller1Right03 RTS ; Return from the subroutine
Name: UpdateJoystick [Show more] Type: Subroutine Category: Controllers Summary: Update the values of JSTX and JSTY with the values from the controller Deep dive: Bolting NES controllers onto the key logger
Context: See this subroutine on its own page References: This subroutine is called as follows: * NMI calls UpdateJoystick
.UpdateJoystick LDA QQ11a ; If the old view in QQ11a is not the space view, then BNE SetControllerPast ; jump to SetControllerPast to set the controller ; history variables to the values from four VBlanks ago ; and return from the subroutien using a tail call LDX JSTX ; Set X to the current roll rate in JSTX LDA #8 ; Set joystickDelta = 8, to use as the amount by which STA joystickDelta ; we change the roll rate by for each button press LDY numberOfPilots ; Set Y to numberOfPilots, which will be 0 if only one ; pilot is configured, or 1 if two pilots are configured ; ; As the rest of this routine updates the joystick based ; on the values in controller1Right + Y etc., this means ; that when the game is configured for two pilots, the ; routine updates the joystick variables based on the ; buttons being pressed on controller 2 ; ; In other words, when two pilots are configured, ; controller 2 steers the ship while controller 1 looks ; after the weaponry BNE joys1 ; If numberOfPilots = 1 then the game is configured for ; two pilots, so skip the following so that holding down ; the B button on controller 1 doesn't stop controller 2 ; from updating the flight controls LDA controller1B ; If the B button is being pressed on controller 1 then BMI joys10 ; the arrow should be used to control the icon bar and ; ship speed, rather than the ship's steering, so jump ; to joys10 to return from the subroutine .joys1 LDA controller1Right,Y ; If the right button is not being pressed, jump to BPL joys2 ; joys2 to skip the following instruction JSR DecreaseJoystick ; The right button is being held down, so decrease the ; current roll rate in X by joystickDelta .joys2 LDA controller1Left,Y ; If the left button is not being pressed, jump to joys3 BPL joys3 ; to skip the following instruction JSR IncreaseJoystick ; The left button is being held down, so increase the ; current roll rate in X by joystickDelta .joys3 STX JSTX ; Store the updated roll rate in JSTX TYA ; If Y is non-zero then the game is configured for two BNE joys4 ; pilots, so jump to joys4... though as this is the very ; next line, this has no effect .joys4 LDA #4 ; Set joystickDelta = 4, to use as the amount by which STA joystickDelta ; we change the pitch rate by for each button press LDX JSTY ; Set X to the current pitch rate in JSTY LDA JSTGY ; If JSTGY is $FF then the game is configured to reverse BMI joys8 ; the controller y-axis, so jump to joys8 to change the ; pitch value in the opposite direction LDA controller1Down,Y ; If the down button is not being pressed, jump to joys5 BPL joys5 ; to skip the following instruction JSR DecreaseJoystick ; The down button is being held down, so decrease the ; current pitch rate in X by joystickDelta .joys5 LDA controller1Up,Y ; If the up button is not being pressed, jump to joys7 BPL joys7 ; to skip the following instruction .joys6 JSR IncreaseJoystick ; The up button is being held down, so increase the ; current pitch rate in X by joystickDelta .joys7 STX JSTY ; Store the updated pitch rate in JSTY RTS ; Return from the subroutine .joys8 LDA controller1Up,Y ; If the up button is not being pressed, jump to joys9 BPL joys9 ; to skip the following instruction JSR DecreaseJoystick ; The up button is being held down, so decrease the ; current pitch rate in X by joystickDelta (as the game ; is configured to reverse the joystick Y channel) .joys9 LDA controller1Down,Y ; If the down button is being pressed, jump to joys6 to BMI joys6 ; increase the current pitch rate in X by joystickDelta ; (as the game is configured to reverse the joystick Y ; channel) STX JSTY ; Store the updated pitch rate in JSTY RTS ; Return from the subroutine .joys10 RTS ; Return from the subroutine
Name: IncreaseJoystick [Show more] Type: Subroutine Category: Controllers Summary: Increase a joystick value by a specific amount, jumping straight to the indicator centre if we increase from the left-hand side Deep dive: Bolting NES controllers onto the key logger
Context: See this subroutine on its own page References: This subroutine is called as follows: * UpdateJoystick calls IncreaseJoystick

Arguments: X The value (pitch or roll rate) to decrease joystickDelta The amount to decrease the value in X by
.IncreaseJoystick TXA ; Set X = X + joystickDelta CLC ADC joystickDelta TAX BCC incj1 ; If the addition didn't overflow, jump to incj1 to skip ; the following instruction LDX #255 ; Set X = 255 so X doesn't get larger than 255 (so we ; can't go past the right end of the indicator) .incj1 BPL decj2 ; If X < 127 then the increased value is still in the ; left-hand side of the indicator, so jump to decj2 to ; return a value of 128, for the centre of the indicator RTS ; Return from the subroutine
Name: DecreaseJoystick [Show more] Type: Subroutine Category: Controllers Summary: Decrease a joystick value by a specific amount, jumping straight to the indicator centre if we decrease from the right-hand side Deep dive: Bolting NES controllers onto the key logger
Context: See this subroutine on its own page References: This subroutine is called as follows: * UpdateJoystick calls DecreaseJoystick * IncreaseJoystick calls via decj2

Arguments: X The value (pitch or roll rate) to decrease joystickDelta The amount to decrease the value in X by
Other entry points: decj2 Return a value of X = 128, for the centre of the indicator
.DecreaseJoystick TXA ; Set X = X - joystickDelta SEC SBC joystickDelta TAX BCS decj1 ; If the subtraction didn't underflow, jump to decj1 to ; skip the following instruction LDX #1 ; Set X = 1 so X doesn't get smaller than 1 (so we can't ; go past the left end of the indicator) .decj1 BPL decj3 ; If X < 127 then the decreased value is in the ; left-hand side of the indicator, so jump to decj3 to ; return from the subroutine ; If we get here then the decreased value is still in ; the right-hand side of the indicator, so we return a ; value of 128, for the centre of the indicator .decj2 LDX #128 ; Set X = 128 to jump to indicator to the centre of the ; indicator, so increasing or decreasing a value towards ; the centre of the indicator immediately jumps to the ; middle point of the indicator .decj3 RTS ; Return from the subroutine
Name: iconBarButtons [Show more] Type: Variable Category: Icon bar Summary: A list of button numbers for each icon bar type
Context: See this variable on its own page References: This variable is used as follows: * SetIconBarButtons uses iconBarButtons
.iconBarButtons ; Icon bar 0 (Docked) EQUB 1 ; Launch EQUB 2 ; Market Price EQUB 3 ; Status Mode EQUB 4 ; Charts EQUB 5 ; Equip Ship EQUB 6 ; Save and Load EQUB 7 ; Change Commander Name (only on save screen) EQUB 35 ; Data on System EQUB 8 ; Inventory EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 12 ; Fast-forward EQUD 0 ; Icon bar 1 (Flight) EQUB 17 ; Docking Computer EQUB 2 ; Market Price EQUB 3 ; Status Mode EQUB 4 ; Charts EQUB 21 ; Front Space View (and rear, left, right) EQUB 22 ; Hyperspace (only when system is selected) EQUB 23 ; E.C.M. (if fitted) EQUB 24 ; Target Missile EQUB 25 ; Fire Targeted Missile EQUB 26 ; Energy Bomb (if fitted) EQUB 27 ; Escape Pod (if fitted) EQUB 12 ; Fast-forward EQUD 0 ; Icon bar 2 (Charts) EQUB 1 ; Launch EQUB 2 ; Market Price EQUB 36 ; Switch Chart Range (long, short) EQUB 35 ; Data on System EQUB 21 ; Front Space View (only in flight) EQUB 38 ; Return Pointer to Current System EQUB 39 ; Search for System EQUB 22 ; Hyperspace (only when system is selected) EQUB 41 ; Galactic Hyperspace (if fitted) EQUB 23 ; E.C.M. (if fitted) EQUB 27 ; Escape Pod (if fitted) EQUB 12 ; Fast-forward EQUD 0 ; Icon bar 3 (Pause) EQUB 49 ; Direction of y-axis EQUB 50 ; Damping toggle EQUB 51 ; Music toggle EQUB 52 ; Sound toggle EQUB 53 ; Number of Pilots EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 0 ; (blank) EQUB 60 ; Restart EQUD 0
Name: HideStardust [Show more] Type: Subroutine Category: Stardust Summary: Hide the stardust sprites
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL164 calls HideStardust
.HideStardust SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX NOSTM ; Set X = NOSTM so we hide NOSTM+1 sprites LDY #152 ; Set Y so we start hiding from sprite 152 / 4 = 38 ; Fall through into HideMoreSprites to hide NOSTM+1 ; sprites from sprite 38 onwards (i.e. 38 to 58 in ; normal space when NOSTM is 20, or 38 to 41 in ; witchspace when NOSTM is 3)
Name: HideMoreSprites [Show more] Type: Subroutine Category: Drawing sprites Summary: Hide X + 1 sprites from sprite Y / 4 onwards
Context: See this subroutine on its own page References: This subroutine is called as follows: * HideMostSprites calls HideMoreSprites

This routine is similar to HideSprites, except it hides X + 1 sprites rather than X sprites.
Arguments: X The number of sprites to hide (we hide X + 1) Y The number of the first sprite to hide * 4
.HideMoreSprites LDA #240 ; Set A to the y-coordinate that's just below the bottom ; of the screen, so we can hide the required sprites by ; moving them off-screen .hisp1 STA ySprite0,Y ; Set the y-coordinate for sprite Y / 4 to 240 to hide ; it (the division by four is because each sprite in the ; sprite buffer has four bytes of data) INY ; Add 4 to Y so it points to the next sprite's data in INY ; the sprite buffer INY INY DEX ; Decrement the loop counter in X BPL hisp1 ; Loop back until we have hidden X + 1 sprites RTS ; Return from the subroutine
Name: SetScreenForUpdate [Show more] Type: Subroutine Category: Drawing sprites Summary: Get the screen ready for updating by hiding all sprites, after fading the screen to black if we are changing view
Context: See this subroutine on its own page References: This subroutine is called as follows: * BuyAndSellCargo calls SetScreenForUpdate * DEATH calls SetScreenForUpdate * EQSHP calls SetScreenForUpdate * SVE calls SetScreenForUpdate * TT23 calls SetScreenForUpdate * TT66 calls SetScreenForUpdate * UpdateViewWithFade calls SetScreenForUpdate
.SetScreenForUpdate LDA QQ11a ; If QQ11 = QQ11a, then we are not currently changing CMP QQ11 ; view, so jump to HideMostSprites to hide all sprites BEQ HideMostSprites ; except for sprite 0 and the icon bar pointer ; Otherwise fall through into FadeAndHideSprites to fade ; the screen to black and hide all the sprites
Name: FadeAndHideSprites [Show more] Type: Subroutine Category: Drawing sprites Summary: Fade the screen to black and hide all sprites
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF calls FadeAndHideSprites * STATUS calls FadeAndHideSprites * TT22 calls FadeAndHideSprites * TT25 calls FadeAndHideSprites
.FadeAndHideSprites JSR FadeToBlack_b3 ; Fade the screen to black over the next four VBlanks
Name: HideMostSprites [Show more] Type: Subroutine Category: Drawing sprites Summary: Hide all sprites except for sprite 0 and the icon bar pointer
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls HideMostSprites * DrawTitleScreen calls HideMostSprites * LAUN calls HideMostSprites * SetScreenForUpdate calls HideMostSprites
.HideMostSprites SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX #58 ; Set X = 58 so we hide 59 sprites LDY #20 ; Set Y so we start hiding from sprite 20 / 4 = 5 BNE HideMoreSprites ; Jump to HideMoreSprites to hide 59 sprites from ; sprite 5 onwards (i.e. sprites 5 to 63, which only ; leaves sprite 0 and the icon bar pointer sprites 1 to ; 4) ; ; We return from the subroutine using a tail call (this ; BNE is effectively a JMP as Y is never zero)
Name: DELAY [Show more] Type: Subroutine Category: Utility routines Summary: Wait until a specified number of NMI interrupts have passed (i.e. a specified number of VBlanks)
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIS calls DELAY * BRIS_b0 calls DELAY * ChangeLetter calls DELAY * DOENTRY calls DELAY * eq calls DELAY * EQSHP calls DELAY * LAUN calls DELAY * Main game loop (Part 5 of 6) calls DELAY * PauseGame calls DELAY * SetupDemoUniverse calls DELAY * YESNO calls DELAY


Arguments: Y The number of NMI interrupts to wait for
.DELAY JSR WaitForNMI ; Wait until the next NMI interrupt has passed (i.e. the ; next VBlank) DEY ; Decrement the counter in Y BNE DELAY ; If Y isn't yet at zero, jump back to DELAY to wait ; for another NMI interrupt RTS ; Return from the subroutine
Name: BEEP [Show more] Type: Subroutine Category: Sound Summary: Make a short, high beep
Context: See this subroutine on its own page References: This subroutine is called as follows: * BEEP_b7 calls BEEP
.BEEP LDY #3 ; Call the NOISE routine with Y = 3 to make a short, BNE NOISE ; high beep, returning from the subroutine using a tail ; call (this BNE is effectively a JMP as Y will never be ; zero)
Name: EXNO3 [Show more] Type: Subroutine Category: Sound Summary: Make an explosion sound
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls EXNO3 * Main flight loop (Part 10 of 16) calls EXNO3 * OOPS calls EXNO3 * TACTICS (Part 1 of 7) calls EXNO3

Make the sound of death in the cold, hard vacuum of space. Apparently, in Elite space, everyone can hear you scream. This routine also makes the sound of a destroyed cargo canister if we don't get scooping right, the sound of us colliding with another ship, and the sound of us being hit with depleted shields. It is not a good sound to hear.
.EXNO3 LDY #13 ; Call the NOISE routine with Y = 13 to make the sound BNE NOISE ; of an explosion, returning from the subroutine using ; a tail call (this BNE is effectively a JMP as Y will ; never be zero)
Name: FlushSoundChannels [Show more] Type: Subroutine Category: Sound Summary: Flush the SQ1, SQ2 and NOISE sound channels
Context: See this subroutine on its own page References: This subroutine is called as follows: * MakeHyperSound calls FlushSoundChannels
.FlushSoundChannels LDX #0 ; Flush the SQ1 sound channel JSR FlushSoundChannel ; Fall through into FlushSQ2AndNOISE to flush the SQ2 ; and NOISE channels
Name: FlushSQ2AndNOISE [Show more] Type: Subroutine Category: Sound Summary: Flush the SQ2 and NOISE sound channels
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlushSpecificSound calls FlushSQ2AndNOISE
.FlushSQ2AndNOISE LDX #1 ; Flush the SQ2 sound channel JSR FlushSoundChannel LDX #2 ; Flush the NOISE sound channel, returning from the BNE FlushSoundChannel ; subroutine using a tail call
Name: FlushSpecificSound [Show more] Type: Subroutine Category: Sound Summary: Flush the channels used by a specific sound
Context: See this subroutine on its own page References: This subroutine is called as follows: * ECMOF calls FlushSpecificSound

The sound channels are flushed according to the specific sound's value in the soundChannel table: * If soundChannel = 0, flush the SQ1 sound channel * If soundChannel = 1, flush the SQ2 sound channel * If soundChannel = 2, flush the NOISE sound channel * If soundChannel = 3, flush the SQ1 and NOISE sound channels * If soundChannel = 4, flush the SQ2 and NOISE sound channels
Arguments: Y The number of the sound to flush
.FlushSpecificSound LDX soundChannel,Y ; Set X to the sound channel for sound Y CPX #3 ; If X < 3 then jump to FlushSoundChannel to flush the BCC FlushSoundChannel ; SQ1, SQ2 or NOISE sound channel, as specified in X, ; returning from the subroutine using a tail call BNE FlushSQ2AndNOISE ; If X <> 3, i.e. X = 4, then jump to FlushSQ2AndNOISE ; to flush sound channels 1 and 2, returning from the ; subroutine using a tail call ; If we get here then we know X = 3, so now we flush the ; SQ1 and NOISE sound channels LDX #0 ; Flush the SQ1 sound channel JSR FlushSoundChannel LDX #2 ; Set X = 2 and fall through into FlushSoundChannel to ; flush the NOISE sound channel
Name: FlushSoundChannel [Show more] Type: Subroutine Category: Sound Summary: Flush a specific sound channel Deep dive: Sound effects in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlushSoundChannels calls FlushSoundChannel * FlushSpecificSound calls FlushSoundChannel * FlushSQ2AndNOISE calls FlushSoundChannel

Arguments: X The sound channel to flush: * 0 = flush the SQ1 sound channel * 1 = flush the SQ2 sound channel * 2 = flush the NOISE sound channel
.FlushSoundChannel SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA #0 ; Set the priority for channel X to zero to stop the STA channelPriority,X ; channel from making any more sounds LDA #26 ; Set A = 26 to pass to StartEffect below (sound effect ; 26 is the sound of silence, so this flushes the sound ; channel) BNE StartEffect_b7 ; Jump to StartEffect to start making sound effect 26 ; on channel X (this BNE is effectively a JMP as A is ; never zero)
Name: BOOP [Show more] Type: Subroutine Category: Sound Summary: Make a long, low beep
Context: See this subroutine on its own page References: This subroutine is called as follows: * eq calls BOOP * EQSHP calls BOOP * HME2 calls BOOP * TT102 calls BOOP
.BOOP LDY #4 ; Call the NOISE routine with Y = 4 to make a long, low BNE NOISE ; beep, returning from the subroutine using a tail call ; (this BNE is effectively a JMP as Y will never be ; zero)
Name: MakeScoopSound [Show more] Type: Subroutine Category: Sound Summary: Make the sound of the fuel scoops working
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 8 of 16) calls MakeScoopSound * Main flight loop (Part 15 of 16) calls MakeScoopSound
.MakeScoopSound LDY #1 ; Call the NOISE routine with Y = 1 to make the sound of BNE NOISE ; the fuel scoops working, returning from the subroutine ; using a tail call (this BNE is effectively a JMP as Y ; will never be zero)
Name: MakeHyperSound [Show more] Type: Subroutine Category: Sound Summary: Make the hyperspace sound
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL164 calls MakeHyperSound * TT18 calls MakeHyperSound
.MakeHyperSound JSR FlushSoundChannels ; Flush the SQ1, SQ2 and NOISE sound channels LDY #21 ; Set Y = 21 and fall through into the NOISE routine to ; make the hyperspace sound
Name: NOISE [Show more] Type: Subroutine Category: Sound Summary: Make the sound effect whose number is in Y Deep dive: Sound effects in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * BEEP calls NOISE * BOOP calls NOISE * BuyAndSellCargo calls NOISE * ECBLB2 calls NOISE * ESCAPE calls NOISE * EXNO calls NOISE * EXNO2 calls NOISE * EXNO3 calls NOISE * FRMIS calls NOISE * Ghy calls NOISE * LAUN calls NOISE * Main flight loop (Part 3 of 16) calls NOISE * Main flight loop (Part 15 of 16) calls NOISE * Main game loop (Part 5 of 6) calls NOISE * MakeScoopSound calls NOISE * MJP calls NOISE * SFRMIS calls NOISE * TACTICS (Part 6 of 7) calls NOISE

Arguments: Y The number of the sound effect to be made
.NOISE LDA DNOIZ ; If DNOIZ is zero then sound is disabled, so jump to BPL RTS8 ; RTS8 to return from the subroutine without making a ; sound LDX soundChannel,Y ; Set X to the channel number for sound effect Y CPX #3 ; If X < 3 then this sound effect uses just the channel BCC nois1 ; given in X, so jump to nois1 to make the sound effect ; on that channel alone ; If we get here then X = 3 or 4, so we need to make the ; sound effect on two channels: ; ; * If X = 3, use sound channels 0 and 2 ; ; * If X = 4, use sound channels 1 and 2 TYA ; Store the sound effect number on the stack, so we can PHA ; restore it after the call to nois1 below DEX ; Set X = X - 3, so X is now 0 or 1, which is the number DEX ; of the first channel we need to make the sound on DEX ; (i.e. the SQ1 or SQ2 channel) JSR nois1 ; Call nois1 to make the sound effect on channel X, so ; that's the SQ1 or SQ2 channel PLA ; Restore the sound effect number from the stack into Y TAY LDX #2 ; Set X = 2 and fall through into nois1 to make the ; sound effect on the NOISE channel, which is the number ; of the second channel we need to make the sound on .nois1 LDA effectOnSQ1,X ; If the status flag for channel X is zero, then there BEQ nois2 ; is no sound being made on this channel at the moment, ; so jump to nois2 to make the sound LDA soundPriority,Y ; Otherwise set A to the priority of the sound effect we ; want to make CMP channelPriority,X ; If A is less than the priority of the sound currently BCC RTS8 ; being made on channel X, then we mustn't interrupt it ; with our lower-priority sound, so return from the ; subroutine without making the new sound .nois2 ; If we get here then we are cleared to make our new ; sound Y on channel X LDA soundPriority,Y ; Set the priority of the sound on channel X to that of STA channelPriority,X ; our new sound, as we are about to make it SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 TYA ; Set A to the sound number in Y so we can pass it to ; the StartEffect routine ; Fall through into StartEffect_b7 to start making sound ; effect A on channel X
Name: StartEffect_b7 [Show more] Type: Subroutine Category: Sound Summary: Call the StartEffect routine Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlushSoundChannel calls StartEffect_b7 * NOISE calls via RTS8

Arguments: A The number of the sound effect to make X The number of the channel on which to make the sound effect
Other entry points: RTS8 Contains an RTS
.StartEffect_b7 JSR StartEffect_b6 ; Call StartEffect to start making sound effect A on ; channel A .RTS8 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 RTS ; Return from the subroutine
Name: soundChannel [Show more] Type: Variable Category: Sound Summary: The sound channels used by each sound effect Deep dive: Sound effects in NES Elite
Context: See this variable on its own page References: This variable is used as follows: * FlushSpecificSound uses soundChannel * NOISE uses soundChannel

The sound channels used by each sound are defined as follows: * If soundChannel = 0, use the SQ1 sound channel * If soundChannel = 1, use the SQ2 sound channel * If soundChannel = 2, use the NOISE sound channel * If soundChannel = 3, use the SQ1 and NOISE sound channels * If soundChannel = 4, use the SQ2 and NOISE sound channels
.soundChannel EQUB 2 ; Sound 0 EQUB 1 ; Sound 1 EQUB 1 ; Sound 2 EQUB 1 ; Sound 3 EQUB 1 ; Sound 4 EQUB 0 ; Sound 5 EQUB 0 ; Sound 6 EQUB 1 ; Sound 7 EQUB 2 ; Sound 8 EQUB 2 ; Sound 9 EQUB 2 ; Sound 10 EQUB 2 ; Sound 11 EQUB 3 ; Sound 12 EQUB 2 ; Sound 13 EQUB 2 ; Sound 14 EQUB 0 ; Sound 15 EQUB 0 ; Sound 16 EQUB 0 ; Sound 17 EQUB 0 ; Sound 18 EQUB 0 ; Sound 19 EQUB 2 ; Sound 20 EQUB 3 ; Sound 21 EQUB 3 ; Sound 22 EQUB 2 ; Sound 23 EQUB 1 ; Sound 24 EQUB 2 ; Sound 25 EQUB 0 ; Sound 26 EQUB 2 ; Sound 27 EQUB 0 ; Sound 28 EQUB 1 ; Sound 29 EQUB 0 ; Sound 30 EQUB 0 ; Sound 31
Name: soundPriority [Show more] Type: Variable Category: Sound Summary: The default priority for each sound effect Deep dive: Sound effects in NES Elite
Context: See this variable on its own page References: This variable is used as follows: * NOISE uses soundPriority
.soundPriority EQUB 128 ; Sound 0 EQUB 130 ; Sound 1 EQUB 192 ; Sound 2 EQUB 33 ; Sound 3 EQUB 33 ; Sound 4 EQUB 16 ; Sound 5 EQUB 16 ; Sound 6 EQUB 65 ; Sound 7 EQUB 130 ; Sound 8 EQUB 50 ; Sound 9 EQUB 132 ; Sound 10 EQUB 32 ; Sound 11 EQUB 192 ; Sound 12 EQUB 96 ; Sound 13 EQUB 64 ; Sound 14 EQUB 128 ; Sound 15 EQUB 128 ; Sound 16 EQUB 128 ; Sound 17 EQUB 128 ; Sound 18 EQUB 144 ; Sound 19 EQUB 132 ; Sound 20 EQUB 51 ; Sound 21 EQUB 51 ; Sound 22 EQUB 32 ; Sound 23 EQUB 192 ; Sound 24 EQUB 24 ; Sound 25 EQUB 16 ; Sound 26 EQUB 16 ; Sound 27 EQUB 16 ; Sound 28 EQUB 16 ; Sound 29 EQUB 16 ; Sound 30 EQUB 96 ; Sound 31 EQUB 96 ; Sound 32 .SetupPPUForIconBar PHA ; Store the value of A on the stack so we can retrieve ; it below SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 PLA ; Retrieve the value of A from the stack so it is ; unchanged RTS ; Return from the subroutine
Name: GetShipBlueprint [Show more] Type: Subroutine Category: Drawing ships Summary: Fetch a specified byte from the current ship blueprint
Arguments: Y The offset of the byte to return from the blueprint
Returns: A The Y-th byte of the current ship blueprint
.GetShipBlueprint LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank LDA (XX0),Y ; Set A to the Y-th byte of the current ship blueprint ; Fall through into ResetBankA to retrieve the bank ; number we stored above and page it back into memory
Name: ResetBankA [Show more] Type: Subroutine Category: Utility routines Summary: Page a specified bank into memory at $8000 while preserving the value of A Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * GetDefaultNEWB calls ResetBankA

Arguments: Stack The number of the bank to page into memory at $8000
.ResetBankA STA storeA ; Store the value of A so we can retrieve it below PLA ; Fetch the ROM bank number from the stack JSR SetBank ; Page bank A into memory at $8000 LDA storeA ; Restore the value of A that we stored above RTS ; Return from the subroutine
Name: GetDefaultNEWB [Show more] Type: Subroutine Category: Drawing ships Summary: Fetch the default NEWB flags for a specified ship type
Context: See this subroutine on its own page References: This subroutine is called as follows: * NWSHP calls GetDefaultNEWB * TACTICS (Part 4 of 7) calls GetDefaultNEWB

Arguments: Y The ship type
Returns: A The default NEWB flags for ship type Y
.GetDefaultNEWB LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank LDA E%-1,Y ; Set A to the default NEWB flags for ship type Y JMP ResetBankA ; Jump to ResetBankA to retrieve the bank number we ; stored above and page it back into memory, returning ; from the subroutine using a tail call
Name: IncreaseTally [Show more] Type: Subroutine Category: Status Summary: Add the kill count to the fractional and low bytes of our combat rank tally following a kill
Context: See this subroutine on its own page References: This subroutine is called as follows: * EXNO2 calls IncreaseTally

Arguments: X The type of the ship that was killed
Returns: C flag If set, the addition overflowed
.IncreaseTally SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank ; The fractional kill count is taken from the KWL% ; table, according to the ship's type (we look up the ; X-1-th value from KWL% because ship types start at 1 ; rather than 0) LDA KWL%-1,X ; Double the fractional kill count and push the low byte ASL A ; onto the stack PHA LDA KWH%-1,X ; Double the integer kill count and put the high byte ROL A ; in Y TAY PLA ; Add the doubled fractional kill count to our tally, ADC TALLYL ; starting by adding the fractional bytes: STA TALLYL ; ; TALLYL = TALLYL + fractional kill count TYA ; And then we add the low byte of TALLY(1 0): ADC TALLY ; STA TALLY ; TALLY = TALLY + carry + integer kill count ; Fall through into ResetBankP to reset the ROM bank to ; the value we stored on the stack
Name: ResetBankP [Show more] Type: Subroutine Category: Utility routines Summary: Page a specified bank into memory at $8000 while preserving the value of A and the processor flags Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckForPause_b0 calls ResetBankP * CheckPauseButton calls via RTS4 * ChooseMusic_b6 calls via RTS4

Arguments: Stack The number of the bank to page into memory at $8000
Other entry points: RTS4 Contains an RTS
.ResetBankP PLA ; Fetch the ROM bank number from the stack PHP ; Store the processor flags on the stack so we can ; retrieve them below JSR SetBank ; Page bank A into memory at $8000 PLP ; Restore the processor flags, so we return the correct ; Z and N flags for the value of A .RTS4 RTS ; Return from the subroutine
Name: CheckPauseButton [Show more] Type: Subroutine Category: Icon bar Summary: Check whether the pause button has been pressed or an icon bar button has been chosen, and process pause/unpause if required
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL164 calls CheckPauseButton
.CheckPauseButton LDA iconBarChoice ; If iconBarChoice = 0 then the icon bar pointer is over BEQ RTS4 ; a blank button, so jump to RTS4 to return from the ; subroutine ; Otherwise fall through into CheckForPause_b0 to pause ; the game if the pause button is pressed
Name: CheckForPause_b0 [Show more] Type: Subroutine Category: Icon bar Summary: Call the CheckForPause routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckSaveLoadBar calls CheckForPause_b0 * DrawScrollFrames calls CheckForPause_b0

Returns: N, Z flags Set according to the value of A passed to the routine
.CheckForPause_b0 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR CheckForPause ; Call CheckForPause, now that it is paged into memory JMP ResetBankP ; Jump to ResetBankP to retrieve the bank number we ; stored above, page it back into memory and set the ; processor flags according to the value of A, returning ; from the subroutine using a tail call
Name: DrawInventoryIcon [Show more] Type: Subroutine Category: Icon bar Summary: Draw the inventory icon on top of the second button in the icon bar
Context: See this subroutine on its own page References: This subroutine is called as follows: * BuyAndSellCargo calls DrawInventoryIcon * SetIconBarRow calls DrawInventoryIcon
.DrawInventoryIcon ; We draw the inventory icon image from sprites with ; sequential patterns, so first we configure the ; variables to pass to the DrawSpriteImage routine LDA #2 ; Set K = 2, to pass as the number of columns in the STA K ; image to DrawSpriteImage below STA K+1 ; Set K+1 = 2, to pass as the number of rows in the ; image to DrawSpriteImage below LDA #69 ; Set K+2 = 69, so we draw the inventory icon image STA K+2 ; using pattern 69 onwards LDA #8 ; Set K+3 = 8, so we build the image from sprite 8 STA K+3 ; onwards LDA #3 ; Set XC = 3 so we draw the image with the top-left STA XC ; corner in tile column 3 LDA #25 ; Set YC = 25 so we draw the image with the top-left STA YC ; corner on tile row 25 LDX #7 ; Set X = 7 so we draw the image seven pixels into the ; (XC, YC) character block along the x-axis LDY #7 ; Set Y = 7 so we draw the image seven pixels into the ; (XC, YC) character block along the y-axis JMP DrawSpriteImage_b6 ; Draw the inventory icon from sprites, using pattern ; #69 onwards, returning from the subroutine using a ; tail call
Name: MakeSounds_b6 [Show more] Type: Subroutine Category: Sound Summary: Call the MakeSounds routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * MakeSoundsAtVBlank calls MakeSounds_b6 * NMI calls MakeSounds_b6 * SetBank calls MakeSounds_b6
.MakeSounds_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR MakeSounds ; Call MakeSounds, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ChooseMusic_b6 [Show more] Type: Subroutine Category: Sound Summary: Call the ChooseMusic routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH2 calls ChooseMusic_b6 * DrawTitleScreen calls ChooseMusic_b6 * PauseGame calls ChooseMusic_b6 * TT102 calls ChooseMusic_b6

Arguments: A The number of the tune to choose
.ChooseMusic_b6 PHA ; Wait until the next NMI interrupt has passed (i.e. the JSR WaitForNMI ; next VBlank), preserving the value in A via the stack PLA ORA #%10000000 ; Set bit 7 of the tune number and store in newTune to STA newTune ; indicate that we are now in the process of changing to ; this tune AND #%01111111 ; Clear bit 7 to set A to the tune number once again LDX disableMusic ; If music is disabled then bit 7 of disableMusic will BMI RTS4 ; be set, so jump to RTS4 to return from the subroutine ; as we can't choose a new tune if music is disabled STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 6 is already paged into memory, jump to CMP #6 ; bank1 BEQ bank1 PHA ; Otherwise store the current bank number on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR ChooseMusic ; Call ChooseMusic, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank1 LDA storeA ; Restore the value of A that we stored above JMP ChooseMusic ; Call ChooseMusic, which is already paged into memory, ; and return from the subroutine using a tail call
Name: StartEffect_b6 [Show more] Type: Subroutine Category: Sound Summary: Call the StartEffect routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * StartEffect_b7 calls StartEffect_b6
.StartEffect_b6 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 6 is already paged into memory, jump to CMP #6 ; bank2 BEQ bank2 PHA ; Otherwise store the current bank number on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR StartEffect ; Call StartEffect, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank2 LDA storeA ; Restore the value of A that we stored above JMP StartEffect ; Call StartEffect, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: ResetMusicAfterNMI [Show more] Type: Subroutine Category: Sound Summary: Wait for the next NMI before resetting the current tune to 0 and stopping the music
Context: See this subroutine on its own page References: This subroutine is called as follows: * BR1 calls ResetMusicAfterNMI * DEATH calls ResetMusicAfterNMI * DOKEY calls ResetMusicAfterNMI * DrawTitleScreen calls ResetMusicAfterNMI * Main flight loop (Part 9 of 16) calls ResetMusicAfterNMI * TT102 calls ResetMusicAfterNMI * TT18 calls ResetMusicAfterNMI
.ResetMusicAfterNMI JSR WaitForNMI ; Wait until the next NMI interrupt has passed (i.e. the ; next VBlank) ; Fall through into ResetMusic to reset the current tune ; to 0 and stop the music
Name: ResetMusic [Show more] Type: Subroutine Category: Sound Summary: Reset the current tune to the default and stop all sounds (music and sound effects)
Context: See this subroutine on its own page References: This subroutine is called as follows: * ResetVariables calls ResetMusic * ShowStartScreen calls ResetMusic
.ResetMusic LDA #0 ; Set newTune to select the default tune (tune 0, the STA newTune ; "Elite Theme") and clear bit 7 to indicate we are not ; in the process of changing tunes ; Fall through into StopSounds_b6 to stop all sounds ; (music and sound effects)
Name: StopSounds_b6 [Show more] Type: Subroutine Category: Sound Summary: Call the StopSounds routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PauseGame calls StopSounds_b6
.StopSounds_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR StopSoundsS ; Call StopSounds via StopSoundsS, now that it is paged ; into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetDemoAutoPlay_b5 [Show more] Type: Subroutine Category: Combat demo Summary: Call the SetDemoAutoPlay routine in ROM bank 5 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChooseLanguage calls SetDemoAutoPlay_b5
.SetDemoAutoPlay_b5 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #5 ; Page ROM bank 5 into memory at $8000 JSR SetBank JSR SetDemoAutoPlay ; Call SetDemoAutoPlay, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawSmallLogo_b4 [Show more] Type: Subroutine Category: Save and load Summary: Call the DrawSmallLogo routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SVE calls DrawSmallLogo_b4
.DrawSmallLogo_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR DrawSmallLogo ; Call DrawSmallLogo, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawBigLogo_b4 [Show more] Type: Subroutine Category: Start and end Summary: Call the DrawBigLogo routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChooseLanguage calls DrawBigLogo_b4
.DrawBigLogo_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR DrawBigLogo ; Call DrawBigLogo, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: FadeToBlack_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the FadeToBlack routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRP calls FadeToBlack_b3 * DEATH2 calls FadeToBlack_b3 * DrawTitleScreen calls FadeToBlack_b3 * FadeAndHideSprites calls FadeToBlack_b3 * ShowScrollText calls FadeToBlack_b3
.FadeToBlack_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR FadeToBlack ; Call FadeToBlack, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: CheckSaveSlots_b6 [Show more] Type: Subroutine Category: Save and load Summary: Call the CheckSaveSlots routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChooseLanguage calls CheckSaveSlots_b6
.CheckSaveSlots_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR CheckSaveSlots ; Call CheckSaveSlots, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: LL9_b1 [Show more] Type: Subroutine Category: Drawing ships Summary: Call the LL9 routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawShipInBitplane calls LL9_b1 * Main flight loop (Part 12 of 16) calls LL9_b1
.LL9_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank3 BEQ bank3 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR LL9 ; Call LL9, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank3 JMP LL9 ; Call LL9, which is already paged into memory, and ; return from the subroutine using a tail call
Name: SIGHT_b3 [Show more] Type: Subroutine Category: Flight Summary: Call the SIGHT routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PlayDemo calls SIGHT_b3 * ResetStardust calls SIGHT_b3
.SIGHT_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR SIGHT ; Call SIGHT, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: TIDY_b1 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Call the TIDY routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT (Part 1 of 9) calls TIDY_b1
.TIDY_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank4 BEQ bank4 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR TIDY ; Call TIDY, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank4 JMP TIDY ; Call TIDY, which is already paged into memory, and ; return from the subroutine using a tail call
Name: ChooseLanguage_b6 [Show more] Type: Subroutine Category: Start and end Summary: Call the ChooseLanguage routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowStartScreen calls ChooseLanguage_b6
.ChooseLanguage_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR ChooseLanguage ; Call ChooseLanguage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: PlayDemo_b0 [Show more] Type: Subroutine Category: Combat demo Summary: Call the PlayDemo routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowScrollText calls PlayDemo_b0
.PlayDemo_b0 LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JMP PlayDemo ; Call PlayDemo, which is already paged into memory, and ; return from the subroutine using a tail call
Name: STARS_b1 [Show more] Type: Subroutine Category: Stardust Summary: Call the STARS routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 16 of 16) calls STARS_b1 * ShowScrollText calls STARS_b1
.STARS_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank5 BEQ bank5 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR STARS ; Call STARS, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank5 JMP STARS ; Call STARS, which is already paged into memory, and ; return from the subroutine using a tail call
Name: CIRCLE2_b1 [Show more] Type: Subroutine Category: Drawing circles Summary: Call the CIRCLE2 routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT128 calls CIRCLE2_b1
.CIRCLE2_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank6 BEQ bank6 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR CIRCLE2 ; Call CIRCLE2, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank6 JMP CIRCLE2 ; Call CIRCLE2, which is already paged into memory, and ; return from the subroutine using a tail call
Name: SUN_b1 [Show more] Type: Subroutine Category: Drawing suns Summary: Call the SUN routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.SUN_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank7 BEQ bank7 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR SUN ; Call SUN, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank7 JMP SUN ; Call SUN, which is already paged into memory, and ; return from the subroutine using a tail call
Name: DrawBackground_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the DrawBackground routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCmdrImage calls DrawBackground_b3 * DrawSystemImage calls DrawBackground_b3
.DrawBackground_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR DrawBackground ; Call DrawBackground, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawSystemImage_b3 [Show more] Type: Subroutine Category: Universe Summary: Call the DrawSystemImage routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT25 calls DrawSystemImage_b3
.DrawSystemImage_b3 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank8 BEQ bank8 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR DrawSystemImage ; Call DrawSystemImage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank8 LDA storeA ; Restore the value of A that we stored above JMP DrawSystemImage ; Call DrawSystemImage, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: DrawImageNames_b4 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the DrawImageNames routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCobraMkIII calls DrawImageNames_b4
.DrawImageNames_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR DrawImageNames ; Call DrawImageNames, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawCmdrImage_b6 [Show more] Type: Subroutine Category: Status Summary: Call the DrawCmdrImage routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls DrawCmdrImage_b6
.DrawCmdrImage_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DrawCmdrImage ; Call DrawCmdrImage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawSpriteImage_b6 [Show more] Type: Subroutine Category: Drawing sprites Summary: Call the DrawSpriteImage routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCmdrImage calls DrawSpriteImage_b6 * DrawInventoryIcon calls DrawSpriteImage_b6 * DrawSystemImage calls DrawSpriteImage_b6
.DrawSpriteImage_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DrawSpriteImage ; Call DrawSpriteImage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: GetHeadshotType_b4 [Show more] Type: Subroutine Category: Status Summary: Call the GetHeadshotType routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls GetHeadshotType_b4
.GetHeadshotType_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR GetHeadshotType ; Call GetHeadshotType, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawEquipment_b6 [Show more] Type: Subroutine Category: Equipment Summary: Call the DrawEquipment routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCobraMkIII calls DrawEquipment_b6 * EQSHP calls DrawEquipment_b6 * UpdateEquipment calls DrawEquipment_b6
.DrawEquipment_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DrawEquipment ; Call DrawEquipment, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DEATH2_b0 [Show more] Type: Subroutine Category: Start and end Summary: Switch to ROM bank 0 and call the DEATH2 routine Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PauseGame calls DEATH2_b0
.DEATH2_b0 LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JMP DEATH2 ; Call DEATH2, which is now paged into memory, and ; return from the subroutine using a tail call
Name: StartGame_b0 [Show more] Type: Subroutine Category: Start and end Summary: Switch to ROM bank 0 and call the StartGame routine Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowScrollText calls StartGame_b0
.StartGame_b0 LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JMP StartGame ; Call StartGame, which is now paged into memory, and ; return from the subroutine using a tail call
Name: SetViewAttrs_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the SetViewAttrs routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls SetViewAttrs_b3
.SetViewAttrs_b3 LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank9 BEQ bank9 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR SetViewAttrs ; Call SetViewAttrs, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank9 JMP SetViewAttrs ; Call SetViewAttrs, which is already paged into memory, ; and return from the subroutine using a tail call
Name: FadeToColour_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the FadeToColour routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SendViewToPPU calls FadeToColour_b3 * SetupViewInNMI calls FadeToColour_b3
.FadeToColour_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR FadeToColour ; Call FadeToColour, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawSmallBox_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the DrawSmallBox routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * qv calls DrawSmallBox_b3 * TT22 calls DrawSmallBox_b3
.DrawSmallBox_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR DrawSmallBox ; Call DrawSmallBox, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawImageFrame_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the DrawImageFrame routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCmdrImage calls DrawImageFrame_b3
.DrawImageFrame_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR DrawImageFrame ; Call DrawImageFrame, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawLaunchBox_b6 [Show more] Type: Subroutine Category: Flight Summary: Call the DrawLaunchBox routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * LAUN calls DrawLaunchBox_b6
.DrawLaunchBox_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DrawLaunchBox ; Call DrawLaunchBox, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetLinePatterns_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the SetLinePatterns routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowScrollText calls SetLinePatterns_b3 * TT66 calls SetLinePatterns_b3
.SetLinePatterns_b3 LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank10 BEQ bank10 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR SetLinePatterns ; Call SetLinePatterns, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank10 JMP SetLinePatterns ; Call SetLinePatterns, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: TT24_b6 [Show more] Type: Subroutine Category: Universe Summary: Call the TT24 routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * hyp1 calls TT24_b6 * TT111 calls TT24_b6
.TT24_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR TT24 ; Call TT24, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ClearDashEdge_b6 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the ClearDashEdge routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls ClearDashEdge_b6
.ClearDashEdge_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR ClearDashEdge ; Call ClearDashEdge, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: LoadNormalFont_b3 [Show more] Type: Subroutine Category: Text Summary: Call the LoadNormalFont routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls LoadNormalFont_b3
.LoadNormalFont_b3 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank11 BEQ bank11 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR LoadNormalFont ; Call LoadNormalFont, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank11 LDA storeA ; Restore the value of A that we stored above JMP LoadNormalFont ; Call LoadNormalFont, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: LoadHighFont_b3 [Show more] Type: Subroutine Category: Text Summary: Call the LoadHighFont routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls LoadHighFont_b3
.LoadHighFont_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR LoadHighFont ; Call LoadHighFont, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: PAS1_b0 [Show more] Type: Subroutine Category: Missions Summary: Call the PAS1 routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PAUSE calls PAS1_b0
.PAS1_b0 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR PAS1 ; Call PAS1, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: GetSystemImage_b5 [Show more] Type: Subroutine Category: Universe Summary: Call the GetSystemImage routine in ROM bank 5 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SendViewToPPU calls GetSystemImage_b5
.GetSystemImage_b5 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #5 ; Page ROM bank 5 into memory at $8000 JSR SetBank JSR GetSystemImage ; Call GetSystemImage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: GetSystemBack_b5 [Show more] Type: Subroutine Category: Universe Summary: Call the GetSystemBack routine in ROM bank 5 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SetupViewInNMI calls GetSystemBack_b5
.GetSystemBack_b5 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #5 ; Page ROM bank 5 into memory at $8000 JSR SetBank JSR GetSystemBack ; Call GetSystemBack, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: GetCmdrImage_b4 [Show more] Type: Subroutine Category: Status Summary: Call the GetCmdrImage routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SendViewToPPU calls GetCmdrImage_b4
.GetCmdrImage_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR GetCmdrImage ; Call GetCmdrImage, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: GetHeadshot_b4 [Show more] Type: Subroutine Category: Status Summary: Call the GetHeadshot routine in ROM bank 4 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SetupViewInNMI calls GetHeadshot_b4
.GetHeadshot_b4 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #4 ; Page ROM bank 4 into memory at $8000 JSR SetBank JSR GetHeadshot ; Call GetHeadshot, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DIALS_b6 [Show more] Type: Subroutine Category: Dashboard Summary: Call the DIALS routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawSpaceViewInNMI calls DIALS_b6
.DIALS_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DIALS ; Call DIALS, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: InputName_b6 [Show more] Type: Subroutine Category: Controllers Summary: Call the InputName routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * HME2 calls InputName_b6
.InputName_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR InputName ; Call InputName, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ChangeToView_b0 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the ChangeToView routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowScrollText calls ChangeToView_b0
.ChangeToView_b0 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 0 is already paged into memory, jump to CMP #0 ; bank12 BEQ bank12 PHA ; Otherwise store the current bank number on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR ChangeToView ; Call ChangeToView, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank12 LDA storeA ; Restore the value of A that we stored above JMP ChangeToView ; Call ChangeToView, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: LL164_b6 [Show more] Type: Subroutine Category: Flight Summary: Call the LL164 routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT18 calls LL164_b6
.LL164_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR LL164 ; Call LL164, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DrawLightning_b6 [Show more] Type: Subroutine Category: Flight Summary: Call the DrawLightning routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlightLoop4To16 calls DrawLightning_b6 * LAUN calls DrawLightning_b6
.DrawLightning_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR DrawLightning ; Call DrawLightning, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: PauseGame_b6 [Show more] Type: Subroutine Category: Icon bar Summary: Call the PauseGame routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckForPause calls PauseGame_b6 * DOKEY calls PauseGame_b6 * qv calls PauseGame_b6
.PauseGame_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR PauseGame ; Call PauseGame, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetKeyLogger_b6 [Show more] Type: Subroutine Category: Controllers Summary: Call the SetKeyLogger routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOKEY calls SetKeyLogger_b6 * PAS1 calls SetKeyLogger_b6 * PauseGame calls SetKeyLogger_b6
.SetKeyLogger_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR SetKeyLogger ; Call SetKeyLogger, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ChangeCmdrName_b6 [Show more] Type: Subroutine Category: Save and load Summary: Call the ChangeCmdrName routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckSaveLoadBar calls ChangeCmdrName_b6
.ChangeCmdrName_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR ChangeCmdrName ; Call ChangeCmdrName, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ResetCommander_b6 [Show more] Type: Subroutine Category: Save and load Summary: Call the ResetCommander routine in ROM bank 6
Context: See this subroutine on its own page References: This subroutine is called as follows: * BR1 calls ResetCommander_b6 * PlayDemo calls ResetCommander_b6
.ResetCommander_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR ResetCommander ; Call ResetCommander, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: JAMESON_b6 [Show more] Type: Subroutine Category: Save and load Summary: Call the JAMESON routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ShowStartScreen calls JAMESON_b6
.JAMESON_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR JAMESON ; Call JAMESON, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ShowScrollText_b6 [Show more] Type: Subroutine Category: Combat demo Summary: Call the ShowScrollText routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH2 calls ShowScrollText_b6 * Main flight loop (Part 15 of 16) calls ShowScrollText_b6
.ShowScrollText_b6 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 6 is already paged into memory, jump to CMP #6 ; bank13 BEQ bank13 PHA ; Otherwise store the current bank number on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR ShowScrollText ; Call ShowScrollText, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank13 LDA storeA ; Restore the value of A that we stored above JMP ShowScrollText ; Call ShowScrollText, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: BEEP_b7 [Show more] Type: Subroutine Category: Sound Summary: Call the BEEP routine in ROM bank 7 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * EQSHP calls BEEP_b7 * InputName calls BEEP_b7 * Main flight loop (Part 11 of 16) calls BEEP_b7 * refund calls BEEP_b7
.BEEP_b7 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR BEEP ; Call BEEP, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: DETOK_b2 [Show more] Type: Subroutine Category: Text Summary: Call the DETOK routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF2 calls DETOK_b2 * BRIS_b0 calls DETOK_b2 * BRP calls DETOK_b2 * ChangeCmdrName calls DETOK_b2 * ChooseLanguage calls DETOK_b2 * dockEd calls DETOK_b2 * HME2 calls DETOK_b2 * MT17 calls DETOK_b2 * MT28 calls DETOK_b2 * STATUS calls DETOK_b2 * TBRIEF calls DETOK_b2 * TT210 calls DETOK_b2 * YESNO calls DETOK_b2
.DETOK_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank14 BEQ bank14 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR DETOK ; Call DETOK, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank14 LDA storeA ; Restore the value of A that we stored above JMP DETOK ; Call DETOK, which is already paged into memory, and ; return from the subroutine using a tail call
Name: DTS_b2 [Show more] Type: Subroutine Category: Text Summary: Call the DTS routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * MT18 calls DTS_b2
.DTS_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank15 BEQ bank15 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR DTS ; Call DTS, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank15 LDA storeA ; Restore the value of A that we stored above JMP DTS ; Call DTS, which is already paged into memory, and ; return from the subroutine using a tail call
Name: PDESC_b2 [Show more] Type: Subroutine Category: Text Summary: Call the PDESC routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT25 calls PDESC_b2
.PDESC_b2 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank JSR PDESC ; Call PDESC, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetupIconBar_b3 [Show more] Type: Subroutine Category: Icon bar Summary: Call the SetupIconBar routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls SetupIconBar_b3
.SetupIconBar_b3 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank16 BEQ bank16 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR SetupIconBar ; Call SetupIconBar, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank16 LDA storeA ; Restore the value of A that we stored above JMP SetupIconBar ; Call SetupIconBar, which is already paged into memory, ; and return from the subroutine using a tail call
Name: ShowIconBar_b3 [Show more] Type: Subroutine Category: Icon bar Summary: Call the ShowIconBar routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PauseGame calls ShowIconBar_b3
.ShowIconBar_b3 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank17 BEQ bank17 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR ShowIconBar ; Call ShowIconBar, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank17 LDA storeA ; Restore the value of A that we stored above JMP ShowIconBar ; Call ShowIconBar, which is already paged into memory, ; and return from the subroutine using a tail call
Name: DrawDashNames_b3 [Show more] Type: Subroutine Category: Dashboard Summary: Call the DrawDashNames routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls DrawDashNames_b3
.DrawDashNames_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR DrawDashNames ; Call DrawDashNames, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ResetScanner_b3 [Show more] Type: Subroutine Category: Dashboard Summary: Call the ResetScanner routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT66 calls ResetScanner_b3
.ResetScanner_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR ResetScanner ; Call ResetScanner, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ResetScreen_b3 [Show more] Type: Subroutine Category: Start and end Summary: Call the ResetScreen routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ResetVariables calls ResetScreen_b3
.ResetScreen_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR ResetScreen ; Call ResetScreen, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: UpdateScreen [Show more] Type: Subroutine Category: PPU Summary: Update the screen by sending data to the PPU, either immediately or during VBlank, depending on whether the screen is visible Deep dive: Views and view types in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChangeToView calls UpdateScreen * UpdateHangarView calls UpdateScreen * UpdateView calls UpdateScreen
.UpdateScreen LDA screenFadedToBlack ; If bit 7 of screenFadedToBlack is clear then the BPL SetupFullViewInNMI ; screen is visible and has not been faded to black, so ; we need to send the view to the PPU in the NMI handler ; to avoid corrupting the screen, so jump to ; SetupFullViewInNMI to configure the NMI handler ; accordingly ; Otherwise the screen has been faded to black, so we ; can fall through into SendViewToPPU to send the view ; straight to the PPU without having to restrict ; ourselves to VBlank
Name: SendViewToPPU_b3 [Show more] Type: Subroutine Category: PPU Summary: Call the SendViewToPPU routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls SendViewToPPU_b3 * SendSpaceViewToPPU calls SendViewToPPU_b3 * UpdateView calls SendViewToPPU_b3
.SendViewToPPU_b3 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR SendViewToPPU ; Call SendViewToPPU, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetupFullViewInNMI [Show more] Type: Subroutine Category: Drawing the screen Summary: Configure the PPU to send tiles for the full screen during VBlank
Context: See this subroutine on its own page References: This subroutine is called as follows: * PlayDemo calls SetupFullViewInNMI * UpdateScreen calls SetupFullViewInNMI
.SetupFullViewInNMI LDA #116 ; Tell the PPU to send nametable entries up to tile STA lastNameTile ; 116 * 8 = 928 (i.e. to the end of tile row 28) in both STA lastNameTile+1 ; bitplanes ; Fall through into SetupViewInNMI_b3 to setup the view ; and configure the NMI to send both bitplanes to the ; PPU during VBlank
Name: SetupViewInNMI_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the SetupViewInNMI routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SetSpaceViewInNMI calls SetupViewInNMI_b3
.SetupViewInNMI_b3 LDA #%11000000 ; Set A to the bitplane flags to set for the drawing ; bitplane in the call to SetupViewInNMI below: ; ; * Bit 2 clear = send tiles up to configured numbers ; * Bit 3 clear = don't clear buffers after sending ; * Bit 4 clear = we've not started sending data yet ; * Bit 5 clear = we have not yet sent all the data ; * Bit 6 set = send both pattern and nametable data ; * Bit 7 set = send data to the PPU ; ; Bits 0 and 1 are ignored and are always clear STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank18 BEQ bank18 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR SetupViewInNMI ; Call SetupViewInNMI, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank18 LDA storeA ; Restore the value of A that we stored above JMP SetupViewInNMI ; Call SetupViewInNMI, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: SendBitplaneToPPU_b3 [Show more] Type: Subroutine Category: PPU Summary: Call the SendBitplaneToPPU routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.SendBitplaneToPPU_b3 LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank19 BEQ bank19 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR SendBitplaneToPPU ; Call SendBitplaneToPPU, now that it is paged into ; memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank19 JMP SendBitplaneToPPU ; Call SendBitplaneToPPU, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: UpdateIconBar_b3 [Show more] Type: Subroutine Category: Icon bar Summary: Call the UpdateIconBar routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
.UpdateIconBar_b3 LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank20 BEQ bank20 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR UpdateIconBar ; Call UpdateIconBar, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank20 JMP UpdateIconBar ; Call UpdateIconBar, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: DrawScreenInNMI_b0 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the DrawScreenInNMI routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PAUSE calls DrawScreenInNMI_b0 * PAUSE2 calls DrawScreenInNMI_b0 * UpdateSaveScreen calls DrawScreenInNMI_b0
.DrawScreenInNMI_b0 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR DrawScreenInNMI ; Call DrawScreenInNMI, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SVE_b6 [Show more] Type: Subroutine Category: Save and load Summary: Call the SVE routine in ROM bank 6 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT102 calls SVE_b6
.SVE_b6 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #6 ; Page ROM bank 6 into memory at $8000 JSR SetBank JSR SVE ; Call SVE, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: MVS5_b0 [Show more] Type: Subroutine Category: Moving Summary: Call the MVS5 routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * HAS1 calls MVS5_b0
.MVS5_b0 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 0 is already paged into memory, jump to CMP #0 ; bank21 BEQ bank21 PHA ; Otherwise store the current bank number on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR MVS5 ; Call MVS5, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank21 LDA storeA ; Restore the value of A that we stored above JMP MVS5 ; Call MVS5, which is already paged into memory, and ; return from the subroutine using a tail call
Name: HALL_b1 [Show more] Type: Subroutine Category: Ship hangar Summary: Call the HALL routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls HALL_b1
.HALL_b1 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR HALL ; Call HALL, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: CHPR_b2 [Show more] Type: Subroutine Category: Text Summary: Call the CHPR routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChangeLetter calls CHPR_b2 * InputName calls CHPR_b2 * PrintFlightMessage calls CHPR_b2
.CHPR_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank22 BEQ bank22 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR CHPR ; Call CHPR, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank22 LDA storeA ; Restore the value of A that we stored above JMP CHPR ; Call CHPR, which is already paged into memory, and ; return from the subroutine using a tail call
Name: DASC_b2 [Show more] Type: Subroutine Category: Text Summary: Call the DASC routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * BPRNT calls DASC_b2 * cmn calls DASC_b2 * PrintCharacter calls DASC_b2 * PrintCommanderName calls DASC_b2 * SetSelectedSystem calls DASC_b2 * TT160 calls DASC_b2 * TT161 calls DASC_b2 * TT16a calls DASC_b2 * TT210 calls DASC_b2 * TT25 calls DASC_b2
.DASC_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank23 BEQ bank23 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR DASC ; Call DASC, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank23 LDA storeA ; Restore the value of A that we stored above JMP DASC ; Call DASC, which is already paged into memory, and ; return from the subroutine using a tail call
Name: TT27_b2 [Show more] Type: Subroutine Category: Text Summary: Call the TT27 routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * cpl calls TT27_b2 * csh calls TT27_b2 * EQSHP calls TT27_b2 * mes9 calls TT27_b2 * MESS calls TT27_b2 * NLIN3 calls TT27_b2 * plf calls TT27_b2 * PrintEquipment calls TT27_b2 * PrintLaserView calls TT27_b2 * PrintSaveHeader calls TT27_b2 * PrintSpaceAndToken calls TT27_b2 * PrintSpacedHyphen calls TT27_b2 * PrintSpaceViewName calls TT27_b2 * PrintTokenAndColon calls TT27_b2 * PrintTokenCrTab calls TT27_b2 * prq calls TT27_b2 * spc calls TT27_b2 * STATUS calls TT27_b2 * TT147 calls TT27_b2 * TT151 calls TT27_b2 * TT162 calls TT27_b2 * TT210 calls TT27_b2 * TT213 calls TT27_b2 * TT25 calls TT27_b2 * TT60 calls TT27_b2 * TT66 calls TT27_b2 * TT67 calls TT27_b2 * TT68 calls TT27_b2 * TT70 calls TT27_b2 * TT73 calls TT27_b2
.TT27_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank24 BEQ bank24 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR TT27 ; Call TT27, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank24 LDA storeA ; Restore the value of A that we stored above JMP TT27 ; Call TT27, which is already paged into memory, and ; return from the subroutine using a tail call
Name: ex_b2 [Show more] Type: Subroutine Category: Text Summary: Call the ex routine in ROM bank 2 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * PrintMessage calls ex_b2
.ex_b2 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; If ROM bank 2 is already paged into memory, jump to CMP #2 ; bank25 BEQ bank25 PHA ; Otherwise store the current bank number on the stack LDA #2 ; Page ROM bank 2 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR ex ; Call ex, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank25 LDA storeA ; Restore the value of A that we stored above JMP ex ; Call ex, which is already paged into memory, and ; return from the subroutine using a tail call
Name: PrintCtrlCode_b0 [Show more] Type: Subroutine Category: Text Summary: Call the PrintCtrlCode routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT27 calls PrintCtrlCode_b0
.PrintCtrlCode_b0 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR PrintCtrlCode ; Call PrintCtrlCode, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: SetupAfterLoad_b0 [Show more] Type: Subroutine Category: Start and end Summary: Call the SetupAfterLoad routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * SaveLoadCommander calls SetupAfterLoad_b0
.SetupAfterLoad_b0 LDA currentBank ; If ROM bank 0 is already paged into memory, jump to CMP #0 ; bank26 BEQ bank26 PHA ; Otherwise store the current bank number on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR SetupAfterLoad ; Call SetupAfterLoad, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank26 JMP SetupAfterLoad ; Call SetupAfterLoad, which is already paged into ; memory, and return from the subroutine using a tail ; call
Name: HideShip_b1 [Show more] Type: Subroutine Category: Dashboard Summary: Update the current ship so it is no longer shown on the scanner Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls HideShip_b1
.HideShip_b1 LDA #0 ; Zero byte #33 in the current ship's data block at K%, LDY #33 ; so it is not shown on the scanner (a non-zero byte #33 STA (INF),Y ; represents the ship's number on the scanner, with a ; ship number of zero indicating that the ship is not ; shown on the scanner) ; Fall through into HideFromScanner to hide the scanner ; sprites for this ship and reset byte #33 in the INWK ; workspace
Name: HideFromScanner_b1 [Show more] Type: Subroutine Category: Dashboard Summary: Call the HideFromScanner routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF calls HideFromScanner_b1 * KILLSHP calls HideFromScanner_b1 * TITLE calls HideFromScanner_b1
.HideFromScanner_b1 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR HideFromScanner ; Call HideFromScanner, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: TT66_b0 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the TT66 routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChooseLanguage calls TT66_b0 * HALL calls TT66_b0 * MT9 calls TT66_b0 * PAUSE calls TT66_b0 * SVE calls TT66_b0
.TT66_b0 STA storeA ; Store the value of A so we can retrieve it below LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank LDA storeA ; Restore the value of A that we stored above JSR TT66 ; Call TT66, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: CLIP_b1 [Show more] Type: Subroutine Category: Drawing lines Summary: Call the CLIP routine in ROM bank 1, drawing the clipped line if it fits on-screen Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawScrollFrame calls CLIP_b1
.CLIP_b1 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR CLIP ; Call CLIP, now that it is paged into memory BCS P%+5 ; If the C flag is set then the clipped line does not ; fit on-screen, so skip the next instruction JSR LOIN ; The clipped line fits on-screen, so draw it JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: ClearScreen_b3 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the ClearScreen routine in ROM bank 3 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * BAY calls ClearScreen_b3 * TBRIEF calls ClearScreen_b3 * TT66 calls ClearScreen_b3
.ClearScreen_b3 LDA currentBank ; If ROM bank 3 is already paged into memory, jump to CMP #3 ; bank27 BEQ bank27 PHA ; Otherwise store the current bank number on the stack LDA #3 ; Page ROM bank 3 into memory at $8000 JSR SetBank JSR ClearScreen ; Call ClearScreen, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank27 JMP ClearScreen ; Call ClearScreen, which is already paged into memory, ; and return from the subroutine using a tail call
Name: SCAN_b1 [Show more] Type: Subroutine Category: Dashboard Summary: Call the SCAN routine in ROM bank 1 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT (Part 9 of 9) calls SCAN_b1
.SCAN_b1 LDA currentBank ; If ROM bank 1 is already paged into memory, jump to CMP #1 ; bank28 BEQ bank28 PHA ; Otherwise store the current bank number on the stack LDA #1 ; Page ROM bank 1 into memory at $8000 JSR SetBank JSR SCAN ; Call SCAN, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call .bank28 JMP SCAN ; Call SCAN, which is already paged into memory, and ; return from the subroutine using a tail call
Name: UpdateViewWithFade [Show more] Type: Subroutine Category: Drawing the screen Summary: Fade the screen to black, if required, hide all sprites and update the view
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIS calls UpdateViewWithFade * BRIS_b0 calls UpdateViewWithFade * TT210 calls UpdateViewWithFade
.UpdateViewWithFade JSR SetScreenForUpdate ; Get the screen ready for updating by hiding all ; sprites, after fading the screen to black if we are ; changing view ; Fall through into UpdateView to update the view
Name: UpdateView_b0 [Show more] Type: Subroutine Category: Drawing the screen Summary: Call the UpdateView routine in ROM bank 0 Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ChooseLanguage calls UpdateView_b0 * SVE calls UpdateView_b0
.UpdateView_b0 LDA currentBank ; Fetch the number of the ROM bank that is currently PHA ; paged into memory at $8000 and store it on the stack LDA #0 ; Page ROM bank 0 into memory at $8000 JSR SetBank JSR UpdateView ; Call UpdateView, now that it is paged into memory JMP ResetBank ; Fetch the previous ROM bank number from the stack and ; page that bank back into memory at $8000, returning ; from the subroutine using a tail call
Name: UpdateHangarView [Show more] Type: Subroutine Category: PPU Summary: Update the hangar view on-screen by sending the data to the PPU, either immediately or during VBlank Deep dive: Views and view types in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * HALL calls UpdateHangarView
.UpdateHangarView LDA #0 ; Page ROM bank 0 into memory at $8000 (this