Skip to navigation

Elite on the BBC Micro and NES

Backporting the flicker-free algorithm

Applying the BBC Master's flicker-free algorithm to the other versions

Once you've seen the smoother animation and flicker-free ships in BBC Master and Apple II Elite, it does tend to make the older versions look a little less sophisticated. Luckily, the changes that Bell and Braben made for these versions don't require any fancy hardware or more memory; instead, it's all down to an improved ship-drawing algorithm, as explained in the deep dive on flicker-free ship drawing (which you probably want to read before exploring the code below).

This means it's relatively straightforward to backport these changes into all the other versions of Acornsoft Elite, even the Acorn Electron version and Angus Duggan's Elite-A. Here's a comparison in the latter, with the standard version on the left, and the flicker-free version on the right:

You can play the flicker-free versions via the releases page, either in a browser or on real hardware; it's interesting to see the differences on-screen. I have also backported the algorithm to the Commodore 64 version; see the GitHub repository for flicker-free Elite on the Commodore 64 for details.

There are three steps to the backporting process:

  1. Free up enough memory for the flicker-free code
  2. Copy the LSPUT line-drawing routine from the Master
  3. Update the SHPPT and LL9 routines to support the flicker-free algorithm

Interestingly, this backporting process is identical for all the original versions of Elite (i.e. cassette, disc, Electron, 6502 Second Processor and Elite-A). The only difference is that in some cases, we have to move routines around to prevent the extra code from breaking branch instructions that would otherwise have to reach too far. The code changes themselves are the same, as they all share the same SHPPT and LL9 routines.

This is true even for the 6502 Second Processor version, which normally uses a line-queueing system when drawing ships, but we can replace this with the same flicker-free code as the other versions because the flicker-free algorithm draws lines by calling LL30, and this routine still draws a line, even on the Second Processor version, so dropping in the new algorithm just works.

Let's look at these three steps in turn. To be specific, let's look at backporting the flicker-free algorithm to the BBC Micro cassette version. To see the code differences for the other versions, you can check out the flicker-free branches in the accompanying repositories, but the updated routines in all versions are essentially the same as in the code below.

1. Finding enough memory
------------------------

Altogether, the flicker-free changes only require a small number of extra bytes compared to the original versions - in the BBC Micro cassette version, for example, we only need to find eight more bytes. That said, memory usage is famously tight in Elite, so this is easier said than done.

Luckily, all the versions of Elite that have serious memory constraints also contain an unused routine that the authors forgot to remove - it's a duplicate of MULTU that is never called. Removing this routine (or just commenting out the required number of instructions) easily gives us the space we need, even in Elite-A, where only half of the duplicate routine is left in the flight code for us to remove.

So, step 1 is to comment out a big enough chunk of this routine. Simple.

2. Copying over LSPUT
---------------------

As part of the new algorithm in the Master, there's an extra routine called LSPUT, which draws a line, adding the line to the ship line heap (potentially replacing one of the lines already there), and then erasing the line that we just replaced. We need to port this over for the flicker-free code to call, which we can do by simply copying LSPUT directly from the Master, and inserting it after the last stage of the LL9 routine.

So, step 2 is to copy LSPUT from the Master and into the version we are updating. Easy.

3. Updating SHPPT and LL9
-------------------------

The SHPPT routine draws ships as points for when they are far away, and the LL9 routine draws them as wireframes when they are closer. As the final step of backporting the new algorithm, we need to update both of these routines so that instead of erasing the whole ship before redrawing the whole ship again, they instead use the new LSPUT routine to draw and erase ships one line at a time.

Below you can see every single code change that we need to make to convert the BBC Micro cassette version to the flicker-free algorithm (specifically, the changes are in SHPPT and parts 1, 9, 10, 11 and 12 of LL9).

So, step 3 is to make the changes below... and then we're done.

Exploring the code below
------------------------

To explore the code changes below, simply click a routine header to expand it, and click it again to shrink it back. Within the code that appears, the original and flicker-free code are shown side-by-side. You can tap one side to expand it, and tap it again to go back to showing both sides. Each side is sideways scrollable, so you can read the comments on small screens.

It might look like a lot of code, but that's because I've included all the routines in full, so you can see the differences in situ. There are surprisingly few differences between the two versions (especially when you realise that parts 2 to 8 of LL9 are completely unchanged). This is a delightfully simple change that makes a very noticeable improvement to the graphics, and without perceivable loss. That's well worth the effort of backporting, I'd say.

Name: SHPPT Type: Subroutine Category: Drawing ships Summary: Draw a distant ship as a point rather than a full wireframe
.SHPPT

Original

Flicker-free

JSR EE51 \ Call EE51 to remove the ship's wireframe from the \ screen, if there is one
(Empty)
 JSR PROJ               \ Project the ship onto the screen, returning:
                        \
                        \   * K3(1 0) = the screen x-coordinate
                        \   * K4(1 0) = the screen y-coordinate
                        \   * A = K4+1

 ORA K3+1               \ If either of the high bytes of the screen coordinates
 BNE nono               \ are non-zero, jump to nono as the ship is off-screen

 LDA K4                 \ Set A = the y-coordinate of the dot

 CMP #Y*2-2             \ If the y-coordinate is bigger than the y-coordinate of
 BCS nono               \ the bottom of the screen, jump to nono as the ship's
                        \ dot is off the bottom of the space view

Original

Flicker-free

LDY #2 \ Call Shpt with Y = 2 to set up bytes 1-4 in the ship JSR Shpt \ lines space, aborting the call to LL9 if the dot is \ off the side of the screen. This call sets up the \ first row of the dot (i.e. a four-pixel dash) LDY #6 \ Set Y to 6 for the next call to Shpt LDA K4 \ Set A = y-coordinate of dot + 1 (so this is the second ADC #1 \ row of the two-pixel-high dot) JSR Shpt \ Call Shpt with Y = 6 to set up bytes 5-8 in the ship \ lines space, aborting the call to LL9 if the dot is \ off the side of the screen. This call sets up the \ second row of the dot (i.e. another four-pixel dash, \ on the row below the first one)
JSR Shpt \ Call Shpt to draw a horizontal 4-pixel dash for the \ first row of the dot (i.e. a four-pixel dash) LDA K4 \ Set A = y-coordinate of dot + 1 (so this is the second CLC \ row of the two-pixel-high dot) ADC #1 JSR Shpt \ Call Shpt to draw a horizontal 4-pixel dash for the \ first row of the dot (i.e. a four-pixel dash)
 LDA #%00001000         \ Set bit 3 of the ship's byte #31 to record that we
 ORA XX1+31             \ have now drawn something on-screen for this ship
 STA XX1+31

Original

Flicker-free

LDA #8 \ Set A = 8 so when we call LL18+2 next, byte #0 of the \ heap gets set to 8, for the 8 bytes we just stuck on \ the heap JMP LL81+2 \ Call LL81+2 to draw the ship's dot, returning from the \ subroutine using a tail call PLA \ Pull the return address from the stack, so the RTS PLA \ below actually returns from the subroutine that called \ LL9 (as we called SHPPT from LL9 with a JMP)
JMP LSPUT \ Jump to LSPUT to draw any remaining lines that are \ still in the ship line heap and return from the \ subroutine using a tail call
.nono

 LDA #%11110111         \ Clear bit 3 of the ship's byte #31 to record that
 AND XX1+31             \ nothing is being drawn on-screen for this ship
 STA XX1+31

Original

Flicker-free

RTS \ Return from the subroutine .Shpt \ This routine sets up four bytes in the ship line heap, \ from byte Y-1 to byte Y+2. If the ship's screen point \ turns out to be off-screen, then this routine aborts \ the entire call to LL9, exiting via nono. The four \ bytes define a horizontal 4-pixel dash, for either the \ top or the bottom of the ship's dot STA (XX19),Y \ Store A in byte Y of the ship line heap INY \ Store A in byte Y+2 of the ship line heap INY STA (XX19),Y LDA K3 \ Set A = screen x-coordinate of the ship dot DEY \ Store A in byte Y+1 of the ship line heap STA (XX19),Y ADC #3 \ Set A = screen x-coordinate of the ship dot + 3 BCS nono-2 \ If the addition pushed the dot off the right side of \ the screen, jump to nono-2 to return from the parent \ subroutine early (i.e. LL9). This works because we \ called Shpt from above with a JSR, so nono-2 removes \ that return address from the stack, leaving the next \ return address exposed. LL9 called SHPPT with a JMP. \ so the next return address is the one that was put on \ the stack by the original call to LL9. So the RTS in \ nono will actually return us from the original call \ to LL9, thus aborting the entire drawing process DEY \ Store A in byte Y-1 of the ship line heap DEY STA (XX19),Y RTS \ Return from the subroutine
JMP LSPUT \ Jump to LSPUT to draw any remaining lines that are \ still in the ship line heap and return from the \ subroutine .Shpt \ This routine draws a horizontal 4-pixel dash, for \ either the top or the bottom of the ship's dot STA Y1 \ Store A in both y-coordinates, as this is a horizontal STA Y2 \ dash at y-coordinate A LDA K3 \ Set A = screen x-coordinate of the ship dot STA X1 \ Store the x-coordinate of the ship dot in X1, as this \ is where the dash starts CLC \ Set A = screen x-coordinate of the ship dot + 3 ADC #3 BCC P%+4 \ If the addition overflowed, set A = 255, the LDA #255 \ x-coordinate of the right edge of the screen STA X2 \ Store the x-coordinate of the ship dot in X1, as this \ is where the dash starts JMP LSPUT \ Draw this edge using flicker-free animation, by first \ drawing the ship's new line and then erasing the \ corresponding old line from the screen, and return \ from the subroutine using a tail call
Name: LL9 (Part 1 of 12) Type: Subroutine Category: Drawing ships Summary: Draw ship: Check if ship is exploding, check if ship is in front

This routine draws the current ship on the screen. This part checks to see if the ship is exploding, or if it should start exploding, and if it does it sets things up accordingly. It also does some basic checks to see if we can see the ship, and if not it removes it from the screen. In this code, XX1 is used to point to the current ship's data block at INWK (the two labels are interchangeable). Arguments: XX1 XX1 shares its location with INWK, which contains the zero-page copy of the data block for this ship from the K% workspace INF The address of the data block for this ship in workspace K% XX19(1 0) XX19(1 0) shares its location with INWK(34 33), which contains the ship line heap address pointer XX0 The address of the blueprint for this ship Other entry points: EE51 Remove the current ship from the screen, called from SHPPT before drawing the ship as a point
.LL25 JMP PLANET \ Jump to the PLANET routine, returning from the \ subroutine using a tail call .LL9 LDA TYPE \ If the ship type is negative then this indicates a BMI LL25 \ planet or sun, so jump to PLANET via LL25 above LDA #31 \ Set XX4 = 31 to store the ship's distance for later STA XX4 \ comparison with the visibility distance. We will \ update this value below with the actual ship's \ distance if it turns out to be visible on-screen

Original

Flicker-free

(Empty)
\ We now set things up for flicker-free ship plotting, \ by setting the following: \ \ LSNUM = offset to the first coordinate in the ship's \ line heap \ \ LSNUM2 = the number of bytes in the heap for the \ ship that's currently on-screen (or 0 if \ there is no ship currently on-screen) LDY #1 \ Set LSNUM = 1, the offset of the first set of line STY LSNUM \ coordinates in the ship line heap DEY \ Decrement Y to 0 LDA #%00001000 \ If bit 3 of the ship's byte #31 is set, then the ship BIT INWK+31 \ is currently being drawn on-screen, so skip the BNE P%+5 \ following two instructions LDA #0 \ The ship is not being drawn on screen, so set A = 0 \ so that LSNUM2 gets set to 0 below (as there are no \ existing coordinates on the ship line heap for this \ ship) EQUB &2C \ Skip the next instruction by turning it into \ &2C &B1 &BD, or BIT &BDB1 which does nothing apart \ from affect the flags LDA (XX19),Y \ Set LSNUM2 to the first byte of the ship's line heap, STA LSNUM2 \ which contains the number of bytes in the heap
 LDA #%00100000         \ If bit 5 of the ship's byte #31 is set, then the ship
 BIT XX1+31             \ is currently exploding, so jump down to EE28
 BNE EE28

 BPL EE28               \ If bit 7 of the ship's byte #31 is clear then the ship
                        \ has not just been killed, so jump down to EE28

                        \ Otherwise bit 5 is clear and bit 7 is set, so the ship
                        \ is not yet exploding but it has been killed, so we
                        \ need to start an explosion

 ORA XX1+31             \ Clear bits 6 and 7 of the ship's byte #31, to stop the
 AND #%00111111         \ ship from firing its laser and to mark it as no longer
 STA XX1+31             \ having just been killed

 LDA #0                 \ Set the ship's acceleration in byte #31 to 0, updating
 LDY #28                \ the byte in the workspace K% data block so we don't
 STA (INF),Y            \ have to copy it back from INWK later

 LDY #30                \ Set the ship's pitch counter in byte #30 to 0, to stop
 STA (INF),Y            \ the ship from pitching

 JSR EE51               \ Call EE51 to remove the ship from the screen

                        \ We now need to set up a new explosion cloud. We
                        \ initialise it with a size of 18 (which gets increased
                        \ by 4 every time the cloud gets redrawn), and the
                        \ explosion count (i.e. the number of particles in the
                        \ explosion), which go into bytes 1 and 2 of the ship
                        \ line heap. See DOEXP for more details of explosion
                        \ clouds

 LDY #1                 \ Set byte #1 of the ship line heap to 18, the initial
 LDA #18                \ size of the explosion cloud
 STA (XX19),Y

 LDY #7                 \ Fetch byte #7 from the ship's blueprint, which
 LDA (XX0),Y            \ determines the explosion count (i.e. the number of
 LDY #2                 \ vertices used as origins for explosion clouds), and
 STA (XX19),Y           \ store it in byte #2 of the ship line heap

                        \ The following loop sets bytes 3-6 of the of the ship
                        \ line heap to random numbers

.EE55

 INY                    \ Increment Y (so the loop starts at 3)

 JSR DORND              \ Set A and X to random numbers

 STA (XX19),Y           \ Store A in the Y-th byte of the ship line heap

 CPY #6                 \ Loop back until we have randomised the 6th byte
 BNE EE55

.EE28

 LDA XX1+8              \ Set A = z_sign

.EE49

 BPL LL10               \ If A is positive, i.e. the ship is in front of us,
                        \ jump down to LL10

.LL14

                        \ The following removes the ship from the screen by
                        \ redrawing it (or, if it is exploding, by redrawing the
                        \ explosion cloud). We call it when the ship is no
                        \ longer on-screen, is too far away to be fully drawn,
                        \ and so on

 LDA XX1+31             \ If bit 5 of the ship's byte #31 is clear, then the
 AND #%00100000         \ ship is not currently exploding, so jump down to EE51
 BEQ EE51               \ to redraw its wireframe

 LDA XX1+31             \ The ship is exploding, so clear bit 3 of the ship's
 AND #%11110111         \ byte #31 to denote that the ship is no longer being
 STA XX1+31             \ drawn on-screen

 JMP DOEXP              \ Jump to DOEXP to display the explosion cloud, which
                        \ will remove it from the screen, returning from the
                        \ subroutine using a tail call

.EE51

 LDA #%00001000         \ If bit 3 of the ship's byte #31 is clear, then there
 BIT XX1+31             \ is already nothing being shown for this ship, so
 BEQ LL10-1             \ return from the subroutine (as LL10-1 contains an RTS)

 EOR XX1+31             \ Otherwise flip bit 3 of byte #31 and store it (which
 STA XX1+31             \ clears bit 3 as we know it was set before the EOR), so
                        \ this sets this ship as no longer being drawn on-screen

Original

Flicker-free

JMP LL155 \ Jump to LL155 to draw the ship, which removes it from \ the screen, returning from the subroutine using a \ tail call
JMP LSPUT \ Jump to LSPUT to draw the ship, which removes it from \ the screen, returning from the subroutine using a \ tail call
 RTS                    \ Return from the subroutine

Name: LL9 (Part 9 of 12) Type: Subroutine Category: Drawing ships Summary: Draw ship: Draw laser beams if the ship is firing its laser at us

This part sets things up so we can loop through the edges in the next part. It also adds a line to the ship line heap, if the ship is firing at us. When we get here, the heap at XX3 contains all the visible vertex screen coordinates.
.LL72 LDA XX1+31 \ If bit 5 of the ship's byte #31 is clear, then the AND #%00100000 \ ship is not currently exploding, so jump down to EE31 BEQ EE31 LDA XX1+31 \ The ship is exploding, so set bit 3 of the ship's byte ORA #8 \ #31 to denote that we are drawing something on-screen STA XX1+31 \ for this ship JMP DOEXP \ Jump to DOEXP to display the explosion cloud, \ returning from the subroutine using a tail call .EE31

Original

Flicker-free

LDA #%00001000 \ If bit 3 of the ship's byte #31 is clear, then there BIT XX1+31 \ is nothing already being shown for this ship, so skip BEQ LL74 \ to LL74 as we don't need to erase anything from the \ screen JSR LL155 \ Otherwise call LL155 to draw the existing ship, which \ removes it from the screen
LDY #9 \ Fetch byte #9 of the ship's blueprint, which is the LDA (XX0),Y \ number of edges, and store it in XX20 STA XX20
 LDA #%00001000         \ Set bit 3 of A so the next instruction sets bit 3 of
                        \ the ship's byte #31 to denote that we are drawing
                        \ something on-screen for this ship

.LL74

 ORA XX1+31             \ Apply bit 3 of A to the ship's byte #31, so if there
 STA XX1+31             \ was no ship already on screen, the bit is clear,
                        \ otherwise it is set

Original

Flicker-free

LDY #9 \ Fetch byte #9 of the ship's blueprint, which is the LDA (XX0),Y \ number of edges, and store it in XX20 STA XX20 LDY #0 \ We are about to step through all the edges, using Y \ as a counter STY U \ Set U = 0 (though we increment it to 1 below) STY XX17 \ Set XX17 = 0, which we are going to use as a counter \ for stepping through the ship's edges INC U \ We are going to start calculating the lines we need to \ draw for this ship, and will store them in the ship \ line heap, using U to point to the end of the heap, so \ we start by setting U = 1
LDY #0 \ Set XX17 = 0, which we are going to use as a counter STY XX17 \ for stepping through the ship's edges
 BIT XX1+31             \ If bit 6 of the ship's byte #31 is clear, then the
 BVC LL170              \ ship is not firing its lasers, so jump to LL170 to
                        \ skip the drawing of laser lines

                        \ The ship is firing its laser at us, so we need to draw
                        \ the laser lines

 LDA XX1+31             \ Clear bit 6 of the ship's byte #31 so the ship doesn't
 AND #%10111111         \ keep firing endlessly
 STA XX1+31

 LDY #6                 \ Fetch byte #6 of the ship's blueprint, which is the
 LDA (XX0),Y            \ number * 4 of the vertex where the ship has its lasers

 TAY                    \ Put the vertex number into Y, where it can act as an
                        \ index into list of vertex screen coordinates we added
                        \ to the XX3 heap

 LDX XX3,Y              \ Fetch the x_lo coordinate of the laser vertex from the
 STX XX15               \ XX3 heap into XX15

 INX                    \ If X = 255 then the laser vertex is not visible, as
 BEQ LL170              \ the value we stored in part 2 wasn't overwritten by
                        \ the vertex calculation in part 6 and 7, so jump to
                        \ LL170 to skip drawing the laser lines

                        \ We now build a laser beam from the ship's laser vertex
                        \ towards our ship, as follows:
                        \
                        \   XX15(1 0) = laser vertex x-coordinate
                        \
                        \   XX15(3 2) = laser vertex y-coordinate
                        \
                        \   XX15(5 4) = x-coordinate of the end of the beam
                        \
                        \   XX12(1 0) = y-coordinate of the end of the beam
                        \
                        \ The end of the laser beam will be set positioned to
                        \ look good, rather than being directly aimed at us, as
                        \ otherwise we would only see a flashing point of light
                        \ as they unleashed their attack

 LDX XX3+1,Y            \ Fetch the x_hi coordinate of the laser vertex from the
 STX XX15+1             \ XX3 heap into XX15+1

 INX                    \ If X = 255 then the laser vertex is not visible, as
 BEQ LL170              \ the value we stored in part 2 wasn't overwritten by
                        \ a vertex calculation in part 6 and 7, so jump to LL170
                        \ to skip drawing the laser beam

 LDX XX3+2,Y            \ Fetch the y_lo coordinate of the laser vertex from the
 STX XX15+2             \ XX3 heap into XX15+2

 LDX XX3+3,Y            \ Fetch the y_hi coordinate of the laser vertex from the
 STX XX15+3             \ XX3 heap into XX15+3

 LDA #0                 \ Set XX15(5 4) = 0, so their laser beam fires to the
 STA XX15+4             \ left edge of the screen
 STA XX15+5

 STA XX12+1             \ Set XX12(1 0) = the ship's z_lo coordinate, which will
 LDA XX1+6              \ effectively make the vertical position of the end of
 STA XX12               \ the laser beam move around as the ship moves in space

 LDA XX1+2              \ If the ship's x_sign is positive, skip the next
 BPL P%+4               \ instruction

 DEC XX15+4             \ The ship's x_sign is negative (i.e. it's on the left
                        \ side of the screen), so switch the laser beam so it
                        \ goes to the right edge of the screen by decrementing
                        \ XX15(5 4) to 255

 JSR LL145              \ Call LL145 to see if the laser beam needs to be
                        \ clipped to fit on-screen, returning the clipped line's
                        \ end-points in (X1, Y1) and (X2, Y2)

 BCS LL170              \ If the C flag is set then the line is not visible on
                        \ screen, so jump to LL170 so we don't store this line
                        \ in the ship line heap

Original

Flicker-free

LDY U \ Fetch the ship line heap pointer, which points to the \ next free byte on the heap, into Y LDA XX15 \ Add X1 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+1 \ Add Y1 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+2 \ Add X2 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+3 \ Add Y2 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer STY U \ Store the updated ship line heap pointer in U
JSR LSPUT \ Draw the laser line using flicker-free animation, by \ first drawing the new laser line and then erasing the \ corresponding old line from the screen
Name: LL9 (Part 10 of 12) Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the visibility of each of the ship's edges and, for flicker-free only, draw the visible ones

This part calculates which edges are visible - in other words, which lines we should draw - and clips them to fit on the screen. When we get here, the heap at XX3 contains all the visible vertex screen coordinates.
.LL170 LDY #3 \ Fetch byte #3 of the ship's blueprint, which contains CLC \ the low byte of the offset to the edges data LDA (XX0),Y ADC XX0 \ Set V = low byte edges offset + XX0 STA V LDY #16 \ Fetch byte #16 of the ship's blueprint, which contains LDA (XX0),Y \ the high byte of the offset to the edges data ADC XX0+1 \ Set V+1 = high byte edges offset + XX0+1 STA V+1 \ \ So V(1 0) now points to the start of the edges data \ for this ship

Original

Flicker-free

LDY #5 \ Fetch byte #5 of the ship's blueprint, which contains LDA (XX0),Y \ the maximum heap size for plotting the ship (which is STA T1 \ 1 + 4 * the maximum number of visible edges) and store \ it in T1 LDY XX17 \ Set Y to the edge counter in XX17 .LL75
LDY #5 \ Fetch byte #5 of the ship's blueprint, which contains LDA (XX0),Y \ the maximum heap size for plotting the ship (which is STA CNT \ 1 + 4 * the maximum number of visible edges) and store \ it in CNT .LL75 LDY #0 \ Set Y = 0 so we start with byte #0
 LDA (V),Y              \ Fetch byte #0 for this edge, which contains the
                        \ visibility distance for this edge, beyond which the
                        \ edge is not shown

 CMP XX4                \ If XX4 > the visibility distance, where XX4 contains
 BCC LL78               \ the ship's z-distance reduced to 0-31 (which we set in
                        \ part 2), then this edge is too far away to be visible,
                        \ so jump down to LL78 to move on to the next edge

 INY                    \ Increment Y to point to byte #1

 LDA (V),Y              \ Fetch byte #1 for this edge into A, so:
                        \
                        \   A = %ffff ffff, where:
                        \
                        \     * Bits 0-3 = the number of face 1
                        \
                        \     * Bits 4-7 = the number of face 2

Original

Flicker-free

INY \ Increment Y to point to byte #2
(Empty)
 STA P                  \ Store byte #1 into P

 AND #%00001111         \ Extract the number of face 1 into X
 TAX

 LDA XX2,X              \ If XX2+X is non-zero then we decided in part 5 that
 BNE LL79               \ face 1 is visible, so jump to LL79

 LDA P                  \ Fetch byte #1 for this edge into A

 LSR A                  \ Shift right four times to extract the number of face 2
 LSR A                  \ from bits 4-7 into X
 LSR A
 LSR A
 TAX

 LDA XX2,X              \ If XX2+X is zero then we decided in part 5 that
 BEQ LL78               \ face 2 is hidden, so jump to LL78

.LL79

                        \ We now build the screen line for this edge, as
                        \ follows:
                        \
                        \   XX15(1 0) = start x-coordinate
                        \
                        \   XX15(3 2) = start y-coordinate
                        \
                        \   XX15(5 4) = end x-coordinate
                        \
                        \   XX12(1 0) = end y-coordinate
                        \
                        \ We can then pass this to the line clipping routine
                        \ before storing the resulting line in the ship line
                        \ heap

Original

Flicker-free

LDA (V),Y \ Fetch byte #2 for this edge into X, which contains TAX \ the number of the vertex at the start of the edge INY \ Increment Y to point to byte #3 LDA (V),Y \ Fetch byte #3 for this edge into Q, which contains STA Q \ the number of the vertex at the end of the edge
INY \ Increment Y to point to byte #2 LDA (V),Y \ Fetch byte #2 for this edge into X, which contains TAX \ the number of the vertex at the start of the edge
 LDA XX3+1,X            \ Fetch the x_hi coordinate of the edge's start vertex
 STA XX15+1             \ from the XX3 heap into XX15+1

 LDA XX3,X              \ Fetch the x_lo coordinate of the edge's start vertex
 STA XX15               \ from the XX3 heap into XX15

 LDA XX3+2,X            \ Fetch the y_lo coordinate of the edge's start vertex
 STA XX15+2             \ from the XX3 heap into XX15+2

 LDA XX3+3,X            \ Fetch the y_hi coordinate of the edge's start vertex
 STA XX15+3             \ from the XX3 heap into XX15+3

Original

Flicker-free

LDX Q \ Set X to the number of the vertex at the end of the \ edge, which we stored in Q
INY \ Increment Y to point to byte #3 LDA (V),Y \ Fetch byte #3 for this edge into X, which contains TAX \ the number of the vertex at the end of the edge
 LDA XX3,X              \ Fetch the x_lo coordinate of the edge's end vertex
 STA XX15+4             \ from the XX3 heap into XX15+4

 LDA XX3+3,X            \ Fetch the y_hi coordinate of the edge's end vertex
 STA XX12+1             \ from the XX3 heap into XX11+1

 LDA XX3+2,X            \ Fetch the y_lo coordinate of the edge's end vertex
 STA XX12               \ from the XX3 heap into XX12

 LDA XX3+1,X            \ Fetch the x_hi coordinate of the edge's end vertex
 STA XX15+5             \ from the XX3 heap into XX15+5

 JSR LL147              \ Call LL147 to see if the new line segment needs to be
                        \ clipped to fit on-screen, returning the clipped line's
                        \ end-points in (X1, Y1) and (X2, Y2)

 BCS LL78               \ If the C flag is set then the line is not visible on
                        \ screen, so jump to LL78 so we don't store this line
                        \ in the ship line heap

Original

Flicker-free

(Empty)
JSR LSPUT \ Draw this edge using flicker-free animation, by first \ drawing the ship's new line and then erasing the \ corresponding old line from the screen
Name: LL9 (Part 11 of 12) Type: Subroutine Category: Drawing ships Summary: Draw ship: Add all visible edges to the ship line heap

This part adds all the visible edges to the ship line heap, so we can draw them in part 12. Other entry points: LL81+2 Draw the contents of the ship line heap, used to draw the ship as a dot from SHPPT

Original

Flicker-free

.LL80 LDY U \ Fetch the ship line heap pointer, which points to the \ next free byte on the heap, into Y LDA XX15 \ Add X1 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+1 \ Add Y1 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+2 \ Add X2 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer LDA XX15+3 \ Add Y2 to the end of the heap STA (XX19),Y INY \ Increment the heap pointer STY U \ Store the updated ship line heap pointer in U CPY T1 \ If Y >= T1 then we have reached the maximum number of BCS LL81 \ edge lines that we can store in the ship line heap, so \ skip to LL81 so we don't loop back for the next edge .LL78 INC XX17 \ Increment the edge counter to point to the next edge LDY XX17 \ If Y >= XX20, which contains the number of edges in CPY XX20 \ the blueprint, jump to LL81 as we have processed all BCS LL81 \ the edges and don't need to loop back for the next one LDY #0 \ Set Y to point to byte #0 again, ready for the next \ edge LDA V \ Increment V by 4 so V(1 0) points to the data for the ADC #4 \ next edge STA V
.LL78 LDA LSNUM \ If LSNUM >= CNT, skip to LL81 so we don't loop back CMP CNT \ for the next edge (CNT was set to the maximum heap BCS LL81 \ size for this ship in part 10, so this checks whether \ we have just run out of space in the ship line heap, \ and stops drawing edges if we have) LDA V \ Increment V by 4 so V(1 0) points to the data for the CLC \ next edge ADC #4 STA V
 BCC ll81               \ If the above addition didn't overflow, jump to ll81

 INC V+1                \ Otherwise increment the high byte of V(1 0), as we
                        \ just moved the V(1 0) pointer past a page boundary

.ll81

Original

Flicker-free

JMP LL75 \ Loop back to LL75 to process the next edge .LL81 \ We have finished adding lines to the ship line heap, \ so now we need to set the first byte of the heap to \ the number of bytes stored there LDA U \ Fetch the ship line heap pointer from U into A, which \ points to the end of the heap, and therefore contains \ the heap size LDY #0 \ Store A as the first byte of the ship line heap, so STA (XX19),Y \ the heap is now correctly set up
INC XX17 \ Increment the edge counter to point to the next edge LDY XX17 \ If Y < XX20, which contains the number of edges in CPY XX20 \ the blueprint, loop back to LL75 to process the next BCC LL75 \ edge .LL81 JMP LSCLR \ Jump down to part 12 below to draw any remaining lines \ from the old ship that are still in the ship line heap
Name: LL9 (Part 12 of 12) Type: Subroutine Category: Drawing ships Summary: Draw ship: Draw all the visible edges from the ship line heap

This part draws the lines in the ship line heap, which is used both to draw the ship, and to remove it from the screen.

Original

Flicker-free

.LL155 LDY #0 \ Fetch the first byte from the ship line heap into A, LDA (XX19),Y \ which contains the number of bytes in the heap STA XX20 \ Store the heap size in XX20 CMP #4 \ If the heap size is less than 4, there is nothing to BCC LL118-1 \ draw, so return from the subroutine (as LL118-1 \ contains an RTS) INY \ Set Y = 1, which we will use as an index into the ship \ line heap, starting at byte #1 (as byte #0 contains \ the heap size) .LL27
.LSCLR LDY LSNUM \ Set Y to the offset in the line heap LSNUM .LSC1 CPY LSNUM2 \ If Y >= LSNUM2, jump to LSC2 to return from the ship BCS LSC2 \ drawing routine, because the index in Y is greater \ than the size of the existing ship line heap, which \ means we have alrady erased all the old ships lines \ when drawing the new ship \ If we get here then Y < LSNUM2, which means Y is \ pointing to an on-screen line from the old ship that \ we need to erase
 LDA (XX19),Y           \ Fetch the X1 line coordinate from the heap and store
 STA XX15               \ it in XX15

 INY                    \ Increment the heap pointer

 LDA (XX19),Y           \ Fetch the Y1 line coordinate from the heap and store
 STA XX15+1             \ it in XX15+1

 INY                    \ Increment the heap pointer

 LDA (XX19),Y           \ Fetch the X2 line coordinate from the heap and store
 STA XX15+2             \ it in XX15+2

 INY                    \ Increment the heap pointer

 LDA (XX19),Y           \ Fetch the Y2 line coordinate from the heap and store
 STA XX15+3             \ it in XX15+3

 JSR LL30               \ Draw a line from (X1, Y1) to (X2, Y2)

 INY                    \ Increment the heap pointer

Original

Flicker-free

CPY XX20 \ If the heap counter is less than the size of the heap, BCC LL27 \ loop back to LL27 to draw the next line from the heap RTS \ Return from the subroutine
JMP LSC1 \ Loop back to LSC1 to draw (i.e. erase) the next line \ from the heap .LSC2 LDA LSNUM \ Store LSNUM in the first byte of the ship line heap LDY #0 STA (XX19),Y .LSC3 RTS \ Return from the subroutine