REM ******************************************************************************
REM
REM LANDER GAME SOURCE
REM
REM Lander was written by David Braben and is copyright D.J.Braben 1987
REM
REM The code on this site has been reconstructed from a disassembly of the game on
REM the Arthur, RISC OS 2 and RISC OS 3.00 application discs
REM
REM The commentary is copyright Mark Moxon, and any misunderstandings or mistakes
REM in the documentation are entirely my fault
REM
REM The terminology and notations used in this commentary are explained at
REM https;//lander.bbcelite.com/terminology
REM
REM The deep dive articles referred to in this commentary can be found at
REM https;//lander.bbcelite.com/deep_dives
REM
REM ------------------------------------------------------------------------------
REM
REM This source file produces the following binary file;
REM
REM   * GameCode
REM
REM ******************************************************************************

 CODE = &00008000                   : REM The build address for the game code

REM ******************************************************************************
REM
REM Configuration variables
REM
REM ******************************************************************************

 TILE_SIZE = &01000000              : REM The length of one side of a square
                                    : REM landscape tile in 3D coordinates

 TILES_X = 13                       : REM The number of tile corners in a landscape
                                    : REM row from left to right [i.e. 12 tiles]

 TILES_Z = 11                       : REM The number of tile corners in a landscape
                                    : REM row from front to back [i.e. 10 tiles]

 MAX_PARTICLES = 484                : REM The maximum number of particles at any one
                                    : REM time

 LAUNCHPAD_OBJECT = 9               : REM The type of object along the right edge of
                                    : REM the launchpad [i.e. the three rockets]

 LAUNCHPAD_ALTITUDE = &03500000     : REM The altitude of the launchpad

 SEA_LEVEL = &05500000              : REM The altitude of sea level

 LANDING_SPEED = &00200000          : REM The maximum safe landing speed

 SMOKE_RISING_SPEED = &00080000     : REM The speed at which smoke particles rise up
                                    : REM from a destroyed object

 UNDERCARRIAGE_Y = &00640000        : REM The height of the ship's undercarriage
                                    : REM [0.390625 tiles], which is the distance
                                    : REM from the centre of the ship to the bottom
                                    : REM of the ship

 BUFFER_SIZE = 4308                 : REM The size of each of the graphics buffers
                                    : REM in bytes

REM ******************************************************************************
REM
REM Calculated constants
REM
REM ******************************************************************************

 STORE = 256 * INT(TILES_X / 32)    : REM We need 256 bytes of cornerStore for every
                                    : REM 32 tiles across the landscape, so STORE
                                    : REM contains the number of extra bytes of data
                                    : REM we need for each extra set of 32 tiles on
                                    : REM top of the default 256 byte buffers [so
                                    : REM STORE is zero for the default landscape]

 LAUNCHPAD_Y = LAUNCHPAD_ALTITUDE - UNDERCARRIAGE_Y : REM The y-coordinate of the
                                                    : REM ship as it sits on the
                                                    : REM launchpad, and on top of
                                                    : REM its undercarriage

 LAUNCHPAD_SIZE = TILE_SIZE * 8     : REM Size of the launchpad [8 tile sizes]

 HIGHEST_ALTITUDE = TILE_SIZE * 52  : REM Highest altitude for the engines to work
                                    : REM [52 tile sizes, but as a positive number;
                                    : REM the altitude is actually -TILE_SIZE * 52]

 SPLASH_HEIGHT = TILE_SIZE / 16     : REM The height above the sea at which splash
                                    : REM particles are added when a particle
                                    : REM splashes into the sea [1/16 of a tile]

 CRASH_CLOUD_Y = TILE_SIZE * 5 / 16     : REM The vertical distance above the
                                        : REM player's ship where we add the
                                        : REM explosion cloud when they crash
                                        : REM [5/16-th of a tile]

 SMOKE_HEIGHT = TILE_SIZE * 3 / 4   : REM The height at which smoke particles are
                                    : REM added above the bottom of a destroyed
                                    : REM object [3/4 of a tile]

 SAFE_HEIGHT = TILE_SIZE * 3 / 2    : REM The minimum safe height for avoiding
                                    : REM objects on the ground [1.5 tile sizes]

 CAMERA_PLAYER_Z = (TILES_Z - 6) * TILE_SIZE    : REM The distance along the z-axis
                                                : REM between the player and the
                                                : REM camera position [which is at
                                                : REM the back of the landscape, in
                                                : REM the middle]

 LAND_MID_HEIGHT = TILE_SIZE * 5    : REM The altitude of the mid-point of the
                                    : REM generated landscape, to which the height
                                    : REM of +/- 5 tiles is added by the Fourier
                                    : REM synthesis in GetLandscapeAltitude

 PLAYER_FRONT_Z = (TILES_Z - 5) * TILE_SIZE : REM The distance along the z-axis
                                            : REM between the tile in front of the
                                            : REM player and the camera, which is
                                            : REM where we spawn explosions and
                                            : REM falling rocks [set to six tiles
                                            : REM forwards from the camera]

 ROCK_HEIGHT = TILE_SIZE * 32       : REM The height from which we drop rocks from
                                    : REM the sky [32 tile sizes]

 LANDSCAPE_X_WIDTH = TILE_SIZE * (TILES_X - 2)  : REM The width of the visible
                                                : REM landscape in x-coordinates,
                                                : REM counting whole tiles only,
                                                : REM and ignoring the centre tile

 LANDSCAPE_Z_DEPTH = TILE_SIZE * (TILES_Z - 1)  : REM The depth of the visible
                                                : REM landscape in z-coordinates,
                                                : REM counting whole tiles only

 LANDSCAPE_X = LANDSCAPE_X_WIDTH / 2    : REM The x-coordinate of the landscape
                                        : REM offset, which is set to the whole
                                        : REM number of tiles that fit into the half
                                        : REM the width of the landscape, so we end
                                        : REM up looking at the middle point of the
                                        : REM landscape

 LANDSCAPE_Y = 0                    : REM The y-coordinate of the landscape offset,
                                    : REM which is zero so the landscape remains at
                                    : REM the same height

 LANDSCAPE_Z = LANDSCAPE_Z_DEPTH + (10 * TILE_SIZE) : REM The z-coordinate of the
                                                    : REM landscape offset, which is
                                                    : REM set to push the landscape
                                                    : REM away from the viewer by
                                                    : REM ten tile sizes

 HALF_TILES_X = INT(TILES_X / 2)    : REM Half the number of tiles from left to
                                    : REM right in the landscape

 LANDSCAPE_X_HALF = TILE_SIZE * HALF_TILES_X    : REM The width of half the number
                                                : REM of tiles from left to right in
                                                : REM the landscape in x-coordinates

 LANDSCAPE_Z_BEYOND = LANDSCAPE_Z_DEPTH + TILE_SIZE : REM The depth of the visible
                                                    : REM landscape in z-coordinates
                                                    : REM plus one more tile

 LANDSCAPE_Z_FRONT = LANDSCAPE_Z - LANDSCAPE_Z_DEPTH    : REM The z-coordinate of
                                                        : REM the front of the
                                                        : REM visible landscape

 LANDSCAPE_Z_MID = LANDSCAPE_Z - CAMERA_PLAYER_Z    : REM The z-coordinate of the
                                                    : REM mid-point of the landscape
                                                    : REM depth [i.e. the player]

REM ******************************************************************************
REM
REM Operating system SWI numbers
REM
REM ******************************************************************************

 OS_WriteC = &00                    : REM The operating system call to write a
                                    : REM character to all the output streams

 OS_WriteS = &01                    : REM The operating system call to write a
                                    : REM string to all the output streams

 OS_ReadC = &04                     : REM The operating system call to read a key
                                    : REM press

 OS_Byte = &06                      : REM General-purpose operating system calls

 OS_Word = &07                      : REM General-purpose operating system calls

 OS_Mouse = &1C                     : REM The operating system call to read the
                                    : REM mouse status

 OS_BinaryToDecimal = &28           : REM The operating system call to convert a
                                    : REM number into a string

REM ******************************************************************************
REM
REM Workspace variables
REM
REM ******************************************************************************

 stack = &000121FC                  : REM The stack is after the end of the main
                                    : REM game code and descends from this address
                                    : REM down to &00012000 [to give a stack size of
                                    : REM 512 32-bit entries]

 workspace = stack + 4              : REM The variable workspace starts just after
                                    : REM the top of the stack

                                    : REM R11 typically contains the address of the
                                    : REM workspace, so each of the variables in the
                                    : REM workspace can be referred to as;
                                    : REM
                                    : REM   [R11, #offset]
                                    : REM
                                    : REM where #offset is one of the following
                                    : REM variables

 xObject = &00                      : REM The x-coordinate of the object currently
                                    : REM being drawn

 yObject = &04                      : REM The y-coordinate of the object currently
                                    : REM being drawn

 zObject = &08                      : REM The z-coordinate of the object currently
                                    : REM being drawn

                                    : REM Offsets &0C to &2C appear to be unused

 rotationMatrix = &30               : REM The rotation matrix of the object
                                    : REM currently being drawn [i.e. the matrix
                                    : REM formed from the object's three orientation
                                    : REM vectors]

 xNoseV = &30                       : REM The x-coordinate of the nose vector

 xRoofV = &34                       : REM The x-coordinate of the roof vector

 xSideV = &38                       : REM The x-coordinate of the side vector

 yNoseV = &3C                       : REM The y-coordinate of the nose vector

 yRoofV = &40                       : REM The y-coordinate of the roof vector

 ySideV = &44                       : REM The y-coordinate of the side vector

 zNoseV = &48                       : REM The z-coordinate of the nose vector

 zRoofV = &4C                       : REM The z-coordinate of the roof vector

 zSideV = &50                       : REM The z-coordinate of the side vector

 xVertex = &54                      : REM The x-coordinate of the vertex being
                                    : REM processed, relative to the object's origin

 yVertex = &58                      : REM The y-coordinate of the vertex being
                                    : REM processed, relative to the object's origin

 zVertex = &5C                      : REM The z-coordinate of the vertex being
                                    : REM processed, relative to the object's origin

                                    : REM Offset &60 appears to be unused

 xVertexRotated = &64               : REM The x-coordinate of the vertex after being
                                    : REM rotated by the object's rotation matrix

 yVertexRotated = &68               : REM The y-coordinate of the vertex after being
                                    : REM rotated by the object's rotation matrix

 zVertexRotated = &6C               : REM The z-coordinate of the vertex after being
                                    : REM rotated by the object's rotation matrix

                                    : REM Offset &70 appears to be unused

 xCoord = &74                       : REM The x-coordinate of the vertex being
                                    : REM processed, in the game's 3D coordinate
                                    : REM system

 yCoord = &78                       : REM The y-coordinate of the vertex being
                                    : REM processed, in the game's 3D coordinate
                                    : REM system

 zCoord = &7C                       : REM The z-coordinate of the vertex being
                                    : REM processed, in the game's 3D coordinate
                                    : REM system

                                    : REM Offset &80 appears to be unused

 xObjectScaled = &84                : REM The x-coordinate of the vertex being
                                    : REM processed, scaled as high as possible

 yObjectScaled = &88                : REM The y-coordinate of the vertex being
                                    : REM processed, scaled as high as possible

 zObjectScaled = &8C                : REM The z-coordinate of the vertex being
                                    : REM processed, scaled as high as possible

                                    : REM Offset &90 appears to be unused

 xPlayer = &94                      : REM The x-coordinate of the player's ship

 yPlayer = &98                      : REM The y-coordinate of the player's ship

 zPlayer = &9C                      : REM The z-coordinate of the player's ship

 xVelocity = &A0                    : REM The player's velocity in the x-axis

 yVelocity = &A4                    : REM The player's velocity in the y-axis

 zVelocity = &A8                    : REM The player's velocity in the z-axis

 xExhaust = &AC                     : REM The x-coordinate of the ship's exhaust
                                    : REM vector, which points along the exhaust
                                    : REM plume

 yExhaust = &B0                     : REM The y-coordinate of the ship's exhaust
                                    : REM vector, which points along the exhaust
                                    : REM plume

 zExhaust = &B4                     : REM The z-coordinate of the ship's exhaust
                                    : REM vector, which points along the exhaust
                                    : REM plume

                                    : REM Offsets &B8 to &C0 appear to be unused

 previousColumn = &C4               : REM The corner coordinates of the previous
                                    : REM column while working along a row from left
                                    : REM to right

 tileCornerRow = &E4                : REM The number of the landscape tile corner
                                    : REM row that is currently being processed

                                    : REM Offset &E8 appears to be unused

 unusedConfig = &F0                 : REM Storage for the first configuration value
                                    : REM in the landscapeConfig table, which is
                                    : REM stored but not used

 objectType = &F4                   : REM The type of the object currently being
                                    : REM drawn

 tileRowOddEven = &F8               : REM A counter that flips every time we process
                                    : REM a tile corner row when drawing the
                                    : REM landscape, so we can trigger actions
                                    : REM accordingly

                                    : REM Offset &FC appears to be unused

 altitude = &100                    : REM The altitude of the landscape at the
                                    : REM most recently calculated coordinate

 prevAltitude = &104                : REM The altitude of the landscape at the
                                    : REM previously calculated coordinate

 particleEnd = &108                 : REM The address of the end of the particle
                                    : REM data in the particle data buffer

 particleCount = &10C               : REM The number of particles currently
                                    : REM on-screen

 objectData = &110                  : REM The address of the blueprint for the
                                    : REM object currently being drawn

 objectFlags = &114                 : REM The flags of the object currently being
                                    : REM drawn

 mainLoopCount = &118               : REM The main loop counter

 crashLoopCount = &11C              : REM The loop counter for the crash animation

 crashedFlag = &120                 : REM If this is non-zero then the object being
                                    : REM processed has crashed into the ground

 currentScore = &124                : REM Our current score, which is displayed at
                                    : REM the left end of the score bar
                                    : REM
                                    : REM It is initialised to initialScore at the
                                    : REM start of each new game
                                    : REM
                                    : REM It goes down by one each time we fire a
                                    : REM bullet, and goes up by 20 for each object
                                    : REM we destroy

 fuelLevel = &128                   : REM The player's fuel level

 gravity = &12C                     : REM The current setting of gravity [which
                                    : REM changes on higher levels]

 playingGame = &130                 : REM A flag to determine whether the game is
                                    : REM being player, or whether this is the crash
                                    : REM animation;
                                    : REM
                                    : REM   * 0 = this is the crash animation
                                    : REM
                                    : REM   * -1 = game is being played

 remainingLives = &134              : REM The number of remaining lives, which is
                                    : REM displayed towards the right end of the
                                    : REM score bar, just before the high score
                                    : REM
                                    : REM It is initialised to 3 at the start of
                                    : REM each new game

 highScore = &138                   : REM The high score, which is displayed at the
                                    : REM right end of the score bar
                                    : REM
                                    : REM It is initialised to initialHighScore when
                                    : REM the game is loaded

 xCamera = &13C                     : REM The 3D x-coordinate of the camera position
                                    : REM [though note that the camera position is
                                    : REM actually at the back of the on-screen
                                    : REM landscape, not the front]

 yCamera = &140                     : REM The 3D y-coordinate of the camera position
                                    : REM [though note that the camera position is
                                    : REM actually at the back of the on-screen
                                    : REM landscape, not the front]

 zCamera = &144                     : REM The 3D z-coordinate of the camera position
                                    : REM [though note that the camera position is
                                    : REM actually at the back of the on-screen
                                    : REM landscape, not the front, so the camera's
                                    : REM z-coordinate is larger than it would be
                                    : REM for a more traditional camera position;
                                    : REM it is more like the camera's focal point
                                    : REM than position, in a sense]

 xCameraTile = &148                 : REM The 3D x-coordinate of the camera, clipped
                                    : REM to the nearest tile

 yCameraTile = &14C                 : REM The 3D y-coordinate of the camera, clipped
                                    : REM to the nearest tile

 zCameraTile = &150                 : REM The 3D z-coordinate of the camera, clipped
                                    : REM to the nearest tile

 shipDirection = &154               : REM The direction in which the player's ship
                                    : REM faces, which is angle b in the rotation
                                    : REM matrix, and which is determined by the
                                    : REM angle of the mouse from the centre point

 shipPitch = &158                   : REM The pitch of the player's ship, which is
                                    : REM angle a in the rotation matrix, and which
                                    : REM is determined by the distance of the mouse
                                    : REM from the centre point

 fuelBurnRate = &15C                : REM The current fuel burn rate [bit 0 is
                                    : REM ignored]

 xLandscapeRow = &160               : REM The x-coordinate of the far-left corner
                                    : REM of the landscape row that we are drawing,
                                    : REM where rows run from left to right across
                                    : REM the screen [this is a relative coordinate]

 yLandscapeRow = &164               : REM The y-coordinate of the far-left corner
                                    : REM of the landscape row that we are drawing,
                                    : REM where rows run from left to right across
                                    : REM the screen [this is a relative coordinate]
 
 zLandscapeRow = &168               : REM The z-coordinate of the far-left corner
                                    : REM of the landscape row that we are drawing,
                                    : REM where rows run from left to right across
                                    : REM the screen [this is a relative coordinate]

                                    : REM Offset &16C appears to be unused

 xLandscapeCol = &170               : REM The x-coordinate of the far-left corner
                                    : REM of the column [i.e. tile] that we are
                                    : REM drawing in the current landscape row,
                                    : REM where columns run in and out of the screen
                                    : REM [this is a relative coordinate]

 yLandscapeCol = &174               : REM The y-coordinate of the far-left corner
                                    : REM of the column [i.e. tile] that we are
                                    : REM drawing in the current landscape row,
                                    : REM where columns run in and out of the screen
                                    : REM [this is a relative coordinate]

 zLandscapeCol = &178               : REM The z-coordinate of the far-left corner
                                    : REM of the column [i.e. tile] that we are
                                    : REM drawing in the current landscape row,
                                    : REM where columns run in and out of the screen
                                    : REM [this is a relative coordinate]

                                    : REM Offsets &17C to &1FC appear to be unused

 stringBuffer = &200                : REM A string buffer that's used when printing
                                    : REM the scores

 cornerStore1 = &400                : REM Storage for coordinates of tile corners as
                                    : REM we work our way through the landscape

 cornerStore2 = &500 + STORE        : REM Storage for coordinates of tile corners as
                                    : REM we work our way through the landscape

 vertexProjected = &600 + STORE * 2 : REM Storage for projected vertices

 particleData = &700 + STORE * 2    : REM The particle data buffer, which stores
                                    : REM eight data bytes for each on-screen
                                    : REM particle

 objectMap = &4400 + STORE * 2      : REM The object map determines which objects
                                    : REM appear on the landscape, where objects are
                                    : REM trees, buildings, rockets and so on [size
                                    : REM of object map is 256 * 256 bytes = &10000]

 buffers = &14400 + STORE * 2       : REM The graphics buffers, one for each tile
                                    : REM corner row [so there are TILE_Z of them]

REM ******************************************************************************
REM
REM LANDER MAIN GAME CODE
REM
REM Produces the binary file GameCode.
REM
REM ******************************************************************************

 DIM CODE% &A000 + STORE * 2        : REM Reserve a block in memory for the
                                    : REM assembled code

 FOR pass% = 4 TO 6 STEP 2          : REM Perform a two-pass assembly, using both
                                    : REM P% and O%, with errors enabled on the
                                    : REM second pass only

 O% = CODE%                         : REM Assemble the code for deployment to
                                    : REM address O%

 P% = CODE                          : REM Assemble the code into the block at P%

[                                   \ Switch from BASIC into assembly language

 OPT     pass%                      \ Set the assembly option for this pass

\ ******************************************************************************
\
\       Name; landscapeOffset
\       Type; Variable
\   Category; Landscape
\    Summary; The offset we apply to the on-screen landscape to push it away
\             from us and to the left, so the visible tiles fit nicely on-screen
\
\ ******************************************************************************

.landscapeOffset

 EQUD    -LANDSCAPE_X
 EQUD    LANDSCAPE_Y
 EQUD    LANDSCAPE_Z

\ ******************************************************************************
\
\       Name; landscapeOffsetAddr
\       Type; Variable
\   Category; Landscape
\    Summary; The address of the landscape offset
\
\ ******************************************************************************

.landscapeOffsetAddr

 EQUD    landscapeOffset

\ ******************************************************************************
\
\       Name; landscapeConfig
\       Type; Variable
\   Category; Landscape
\    Summary; The configuration data for each tile row in the landscape
\
\ ------------------------------------------------------------------------------
\
\ This table contains configuration data for the tile rows that make up the
\ landscape.
\
\ The first byte is read but is never used.
\
\ The second byte is the number of points [i.e. corners] in each tile row. This
\ is the same for all tile rows, so while this table would allow us to tailor
\ the number of tiles plotted on each row, perhaps to make them taper off into
\ the distance, this isn't actually done.
\
\ ******************************************************************************

.landscapeConfig

                                    \ We need a configuration for each tile
                                    \ corner row

 EQUB &3A : EQUB TILES_X               \ Tile row #0

]

 FOR I% = 1 TO (TILES_Z - 1) / 2

[

  OPT    pass%

  EQUB &E3 : EQUB TILES_X               \ Tile row data [even]
  EQUB &E4 : EQUB TILES_X               \ Tile row data [odd]

]

 NEXT

[

 OPT     pass%

 EQUB &E3 : EQUB TILES_X               \ Tile row #TILES_Z

 OPT     FN_AlignWithZeroes

\ ******************************************************************************
\
\       Name; landscapeConfigAddr
\       Type; Variable
\   Category; Landscape
\    Summary; The address of the landscapeConfig table
\
\ ******************************************************************************

.landscapeConfigAddr

 EQUD    landscapeConfig

\ ******************************************************************************
\
\       Name; graphicsBuffers
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The addresses of each of the graphics buffers [these values do not
\             change]
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.graphicsBuffers

                                    \ We need a graphics buffer for each tile
                                    \ corner row, numbered 0 to 10 [and the game
                                    \ also includes an extra buffer that is
                                    \ unused]

]

 buffer = workspace + buffers       : REM The graphics buffers live at the address
                                    : REM given in the buffersAddr variable

 FOR I% = 1 TO TILES_Z + 1          : REM Add a buffer for each corner row [plus 1]

[

  OPT    pass%

  EQUD   buffer                     \ Insert the address of the buffer

]

  buffer = buffer + BUFFER_SIZE     : REM Move on to the next buffer

 NEXT                               : REM Repeat until we have inserted addresses of
                                    : REM all the graphics buffers

[

 OPT    pass%

\ ******************************************************************************
\
\       Name; graphicsBuffersEnd
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The end addresses of each of the graphics buffers [these values
\             get updated as objects are drawn into the buffers]
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.graphicsBuffersEnd

                                    \ We need a graphics buffer for each tile
                                    \ corner row, numbered 0 to 10 [and the game
                                    \ also includes an extra buffer that is
                                    \ unused]

]

 buffer = workspace + buffers       : REM The graphics buffers live at the address
                                    : REM given in the buffersAddr variable

 FOR I% = 1 TO TILES_Z + 1          : REM Add a buffer for each corner row [plus 1]

[

  OPT    pass%

  EQUD   buffer                     \ Insert the address of the buffer

]

  buffer = buffer + BUFFER_SIZE     : REM Move on to the next buffer

 NEXT                               : REM Repeat until we have inserted addresses of
                                    : REM all the graphics buffers

[

 OPT    pass%

\ ******************************************************************************
\
\       Name; DrawLandscapeAndBuffers [Part 1 of 4]
\       Type; Subroutine
\   Category; Landscape
\    Summary; Draw the landscape and the contents of the graphics buffers, from
\             the back of the screen to the front
\  Deep dive; Drawing the landscape
\             Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ Both the landscape and objects are drawn one tile row at a time, working from
\ the back to the front. In each iteration, we process a horizontal row of tile
\ corners, working from left to right and back to front. For each corner we draw
\ a landscape tile where possible [i.e. when we have already processed the other
\ three corners in that tile, so we don't start drawing until we reach the
\ second corner on the second row].
\
\ Objects are drawn after the tiles on which they sit. This is achieved by
\ staggering the drawing of objects so they are drawn two rows later than the
\ landscape, so we draw an object two iterations after we have finished drawing
\ all four of its surrounding tiles. This ensures that the landscape always
\ appears behind the objects that sit on it.
\
\ More specifically, we do the following;
\
\   * Part 1; Start by setting up all the variables
\
\   * We now step through each row of tile corners, working through the tile
\     corners from left to right, one row at a time, keeping track of the row
\     number in tileCornerRow = 0 to 10
\
\     For each row of tile corners, we do the following;
\
\     * Part 2; Work along the current row of tile corners, from left to right,
\               one corner coordinate at a time, and draw each tile as a
\               quadrilateral once we have four valid corner coordinates from
\               the previous row and previous column
\
\     * Part 3; If tileCornerRow >= 2, also draw the contents of the graphics
\               buffer with number tileCornerRow - 2
\
\   * Part 4; Finish by drawing the objects in graphics buffer 9 and graphics
\             buffer 10
\
\ So we draw the objects in graphics buffer 0 just after we process tile corner
\ row 2, so that's just after we draw the tiles between corner rows 1 and 2.
\
\ Note that the game allocates memory to an extra graphics buffer, but only
\ buffers numbers 0 to TILE_Z are used.
\
\ ******************************************************************************

.DrawLandscapeAndBuffers

 STMFD   R13!, {R5-R9, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R3, [R11, #zCamera]        \ Set R3 to the z-coordinate of the camera,
                                    \ which is the 3D coordinate in the game
                                    \ world at the middle-back of the on-screen
                                    \ view

 AND     R9, R3, #&FF000000         \ Set R9 to the z-coordinate of the tile
                                    \ containing the camera, as this rounds the
                                    \ coordinate down to the nearest tile

 SUB     R3, R3, R9                 \ Set R3 = R3 - R9
                                    \        = zCamera - tile containing zCamera
                                    \
                                    \ So R3 contains the fractional element of
                                    \ zCamera in terms of whole tiles

 STR     R9, [R11, #zCameraTile]    \ Set zCameraTile to the z-coordinate of the
                                    \ tile containing the camera [though this
                                    \ isn't actually used - the value of R9 is
                                    \ used below, though]

 LDR     R4, [R11, #xCamera]        \ Set R4 to the x-coordinate of the camera,
                                    \ which is the 3D coordinate in the game
                                    \ world at the middle-back of the on-screen
                                    \ view

 AND     R8, R4, #&FF000000         \ Set R8 to the x-coordinate of the tile
                                    \ containing the camera, as this rounds the
                                    \ coordinate down to the nearest tile

 SUB     R4, R4, R8                 \ Set R4 = R4 - R8
                                    \        = xCamera - tile containing xCamera
                                    \
                                    \ So R4 contains the fractional element of
                                    \ xCamera in terms of whole tiles

 STR     R8, [R11, #xCameraTile]    \ Set xCameraTile to the x-coordinate of the
                                    \ tile containing the camera

 ADD     R5, R11, #xLandscapeRow    \ Set R5 to the address of xLandscapeRow

 LDR     R0, landscapeOffsetAddr    \ Fetch the landscape offset vector into
 LDMFD   R0, {R0-R2}                \ [R0, R1, R2], which defines the offset
                                    \ that we apply to the on-screen landscape
                                    \ to move it away from the viewer so it
                                    \ fits nicely on-screen

 SUB     R0, R0, R4                 \ Subtract the fractional element of xCamera
                                    \ from the landscape offset x-coordinate in
                                    \ R0

 SUB     R2, R2, R3                 \ Subtract the fractional element of zCamera
                                    \ from the landscape offset z-coordinate in
                                    \ R2

 STMIA   R5, {R0-R2}                \ [R0, R1, R2] now contains the coordinate
                                    \ of the far-left corner of the back row of
                                    \ the landscape, which is the first corner
                                    \ row that we will process, so store it in
                                    \ [xLandscapeRow, yLandscapeRow,
                                    \ zLandscapeRow]

 MOV     R0, #0                     \ Set tileCornerRow = 0 to use as the
 STRB    R0, [R11, #tileCornerRow]  \ number of the tile corner row we are
                                    \ currently processing, working through the
                                    \ corner rows from row 0 [at the back] to
                                    \ row 10 [at the front]

 MOV     R6, #0                     \ Set R6 = 0 to use as the address of the
                                    \ previous row's coordinates, which we set
                                    \ to zero as we don't have a previous row
                                    \ yet

 ADD     R7, R11, #cornerStore1     \ Set R7 to the address of cornerStore1

 STRB    R6, [R11, #tileRowOddEven] \ Set tileRowOddEven = 0, which we will flip
                                    \ between 0 and 1 for each tile corner row
                                    \ that we process

                                    \ So we now have the following variables set
                                    \ up, ready for the iteration through each
                                    \ row of tile corners, stepping from left to
                                    \ right, column by column;
                                    \
                                    \   * R6 = 0
                                    \
                                    \     R6 always points to the set of corner
                                    \     pixel coordinates from the previous
                                    \     tile corner row, so it starts out as
                                    \     zero as there is no previous row at
                                    \     this point
                                    \
                                    \   * R7 = address of cornerStore1
                                    \
                                    \     R7 always points to the place where we
                                    \     store the pixel coordinates for the
                                    \     current row corner as we work our way
                                    \     along the row it [we store these
                                    \     coordinates so we can draw the tiles
                                    \     when we're on the next corner row in
                                    \     the next iteration]
                                    \
                                    \   * R9 = the z-coordinate of the tile
                                    \     containing the camera
                                    \
                                    \     So [x, R9] is the coordinate of each
                                    \     corner on the corner row we're working
                                    \     along, starting from the far-left
                                    \     corner [as the camera position is
                                    \     actually at the back of the on-screen
                                    \     landscape]
                                    \
                                    \   * zLandscapeRow = the z-coordinate of
                                    \     the back corner row of the landscape
                                    \
                                    \     So zLandscapeRow keeps track of the
                                    \     z-coordinate of the tile corner row
                                    \     that we are processing, decreasing as
                                    \     we move towards the front of the tile
                                    \     landscape
                                    \
                                    \   * tileRowOddEven = 0
                                    \
                                    \     tileRowOddEven flips between 0 and 1
                                    \     for each tile corner row, so we can
                                    \     set the correct values of R6 and R7 at
                                    \     the end of each iteration
                                    \
                                    \   * tileCornerRow = 0
                                    \
                                    \     tileCornerRow contains the number of
                                    \     the tile corner row that we are
                                    \     currently processing, which increments
                                    \     on each iteration as we move forwards
                                    \
                                    \  * xCameraTile is the x-coordinate of the
                                    \    tile containing the camera
                                    \
                                    \    This is the same as the x-coordinate of
                                    \    the middle of each tile corner row,
                                    \    rounded down to the nearest tile
                                    \
                                    \ We now fall through into part 2

\ ******************************************************************************
\
\       Name; DrawLandscapeAndBuffers [Part 2 of 4]
\       Type; Subroutine
\   Category; Landscape
\    Summary; Draw a row of landscape tiles
\  Deep dive; Drawing the landscape
\             Depth-sorting with the graphics buffers
\
\ ******************************************************************************

                                    \ We're now ready to iterate through the
                                    \ landscape, from left to right and back to
                                    \ front, looping back to land1 after
                                    \ processing each corner row

.land1

 LDRB    R1, [R11, #tileCornerRow]  \ Set R1 to the number of the tile corner
                                    \ row we are currently processing

 LDR     R0, landscapeConfigAddr    \ Set R0 to the address of the pair of
 ADD     R0, R0, R1, LSL #1         \ bytes in the landscapeConfig table for the
                                    \ tile row number in R1

 LDRB    R10, [R0, #1]              \ Set R10 to the second byte from the pair
                                    \ in landscapeConfig, which is the number
                                    \ of tile corners in the row
                                    \
                                    \ We now use R10 as a loop counter when
                                    \ working our way along the row, counting
                                    \ tile corners as we progress

 LDRB    R0, [R0]                   \ Set unusedConfig to the first byte from
 STRB    R0, [R11, #unusedConfig]   \ the pair in landscapeConfig, though this
                                    \ value is not used, so this has no effect

 ADD     R0, R11, #xLandscapeRow    \ Fetch the coordinate from [xLandscapeRow,
 LDMIA   R0, {R0-R2}                \ yLandscapeRow, zLandscapeRow] into
                                    \ [R0, R1, R2], so they contain the
                                    \ coordinates of the left end of the corner
                                    \ row that we need to process

 ADD     R4, R11, #xLandscapeCol    \ Store the coordinates in [xLandscapeCol,
 STMIA   R4, {R0-R2}                \ yLandscapeCol, zLandscapeCol] so we can
                                    \ start processing from the left end of this
                                    \ tile corner row, using these coordinates
                                    \ as we work through the columns, updating
                                    \ yLandscapeCol with the landscape height as
                                    \ we go

 LDR     R8, [R11, #xCameraTile]    \ Set R8 = xCameraTile - LANDSCAPE_X, so
 SUB     R8, R8, #LANDSCAPE_X       \ R8 is now the coordinate of the tile
                                    \ corner at the left end of the corner row,
                                    \ as subtracting the offset of the landscape
                                    \ effectively moves the camera to the left
                                    \ by that amount

 MOV     R0, #&80000000             \ Set previousColumn = &80000000 to denote
 STR     R0, [R11, #previousColumn] \ that we don't have corner coordinates from
                                    \ the previous column yet [as we are
                                    \ starting a new row]

                                    \ We now do the following loop R10 times,
                                    \ once for each of the tile corners in the
                                    \ row, looping back to land2 as we move
                                    \ right through the columns

.land2

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [x, z] = [R8, R9], which is
                                    \ the current corner on this row

 LDR     R14, [R11, #yCamera]       \ Set yLandscapeCol = R0 - yCamera
 SUB     R0, R0, R14                \                   = altitude - yCamera
 STR     R0, [R11, #yLandscapeCol]  \
                                    \ So this stores the y-coordinate of the
                                    \ landscape at this point, as we're seeing
                                    \ it from the point of view of the camera
                                    \ [so this will move it down the screen if
                                    \ the player is flying high, for example]

 ADD     R0, R11, #xLandscapeCol    \ Set R0 to the address of xLandscapeCol

 BL      ProjectVertexOntoScreen    \ Project [xLandscapeCol, yLandscapeCol,
                                    \ zLandscapeCol] onto the screen, returning
                                    \ the results in [R0, R1], so this projects
                                    \ the tile corner as it sits on the
                                    \ landscape
                                    \
                                    \ This also sets the C flag if the vertex is
                                    \ too far away to draw

 BCS     land4                      \ If the vertex is too far away to draw then
                                    \ jump to land4 to skip the following

 STMIA   R7!, {R0-R1}               \ Store the corner coordinates in [R0, R1]
                                    \ in R7, updating R7 as we go, as R7 is
                                    \ where we store the tile corner pixel
                                    \ coordinates as we go [so this stores the
                                    \ corner coordinates in either cornerStore1
                                    \ or cornerStore2, so we can fetch them when
                                    \ we process the next tile corner row]

 CMP     R6, #0                     \ If R6 = 0 then this is either the very
 BEQ     land4                      \ first tile corner row, or we have already
                                    \ fetched all the data for the previous row
                                    \ from the storage at R6
                                    \
                                    \ In either case, jump to land4 to skip
                                    \ drawing this tile as we can't draw tiles
                                    \ without the corresponding corner
                                    \ coordinates from the previous row and
                                    \ previous column

 LDMIA   R6!, {R2-R3}               \ Load the corner pixel coordinates from R6
                                    \ into [R2, R3], updating R6 as we go, so
                                    \ this fetches the corresponding corner
                                    \ pixel coordinates from the previous tile
                                    \ corner row [i.e. from the opposite corner
                                    \ store to the one where we just stored this
                                    \ row's coordinate]

 CMP     R2, #&80000000             \ If R2 = &80000000 then we have reached the
 MOVEQ   R6, #0                     \ end of the storage in R6, so set R6 = 0
 BEQ     land4                      \ to prevent any more access attempts from
                                    \ the storage in R6, and jump to land4 to
                                    \ skip drawing this tile

                                    \ If we get here then we have the
                                    \ following pixel corner coordinates
                                    \ calculated;
                                    \
                                    \   [R0, R1] = new corner from this row
                                    \
                                    \   [R2, R3] = corresponding corner from
                                    \              previous row [i.e. the corner
                                    \              that's back by one row]

 STMFD   R13!, {R6-R8}              \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 ADD     R14, R11, #previousColumn  \ We now need to fetch the previous corners,
 LDMIA   R14, {R4-R7}               \ looking left along the tile row to the
                                    \ previous column of corners, so we can draw
                                    \ a tile from there to the new corner
                                    \
                                    \ We will have stored these previous corners
                                    \ in the previousColumn table in the
                                    \ previous iteration, so fetch them into
                                    \ R4 to R7 like this;
                                    \
                                    \   [R4, R5] = corner from the column to the
                                    \              left on this row
                                    \
                                    \   [R6, R7] = corresponding corner from the
                                    \              previous row

 STMIA   R14, {R0-R3}               \ Store the new corners from this corner row
                                    \ in [R0, R1] and [R2, R3] in previousColumn
                                    \ so we can fetch them in the next iteration
                                    \ in the same way

 CMP     R4, #&80000000             \ If R4 = &80000000 then this is actually
 BEQ     land3                      \ the first column of corner coordinates in
                                    \ this row, so we can't yet draw the tile,
                                    \ so jump to land3 to skip the drawing part

                                    \ If we get here then we actually have a
                                    \ tile to draw

 BL      GetLandscapeTileColour     \ Calculate the tile colour, depending on
                                    \ the slope, and return it in R8

 BL      DrawQuadrilateral          \ Draw the tile by drawing a quadrilateral
                                    \ in colour R8 with corners at;
                                    \
                                    \   [R0, R1] = the corner we're processing
                                    \   [R2, R3] = same corner in previous row
                                    \   [R4, R5] = previous column in this row
                                    \   [R6, R7] = previous row, previous column
                                    \
                                    \ So this draws the tile we want on-screen

.land3

 LDMFD   R13!, {R6-R8}              \ Retrieve the registers that we stored on
                                    \ the stack

.land4

 SUBS    R10, R10, #1               \ Decrement the loop counter in R10, which
                                    \ counts the number of tile corners in this
                                    \ row

 LDRNE   R0, [R11, #xLandscapeCol]  \ If we haven't yet processed all the tile
 ADDNE   R0, R0, #TILE_SIZE         \ corners in this row, add a tile's width to
 STRNE   R0, [R11, #xLandscapeCol]  \ xLandscapeCol and R8 to move them along
 ADDNE   R8, R8, #TILE_SIZE         \ the row to the next tile corner, and jump
 BNE     land2                      \ back to land2 to process the next corner

                                    \ By this point have drawn all the tiles in
                                    \ this row, so we now move on to the objects
                                    \ in the graphics buffers

\ ******************************************************************************
\
\       Name; DrawLandscapeAndBuffers [Part 3 of 4]
\       Type; Subroutine
\   Category; Landscape
\    Summary; Draw the objects in the graphics buffers for two rows behind the
\             current corner row
\  Deep dive; Drawing the landscape
\             Depth-sorting with the graphics buffers
\
\ ******************************************************************************

 LDRB    R0, [R11, #tileCornerRow]  \ Set R0 to the number of the tile corner
                                    \ row we are currently processing

 SUBS    R0, R0, #2                 \ Subtract 2 from the corner row to get the
                                    \ number of the row that's two tile rows
                                    \ behind the current row

 BLPL    DrawGraphicsBuffer         \ If this results in a valid graphics buffer
                                    \ number, i.e. one that is positive, draw
                                    \ the contents of the buffer

 LDRB    R0, [R11, #tileCornerRow]  \ Set R0 to the number of the tile corner
                                    \ row we are currently processing

 ADDS    R0, R0, #1                 \ Increment the tile row number in R0 to
                                    \ move forwards by one tile row

 CMP     R0, #TILES_Z               \ If R0 = TILES_Z then we just drew the last
 BEQ     land5                      \ tile row, so jump to land5 to finish off
                                    \ by drawing graphics buffers 9 and 10

 STRB    R0, [R11, #tileCornerRow]  \ Otherwise store the updated tile corner
                                    \ row number in tileCornerRow, ready for the
                                    \ next corner row

 LDR     R0, [R11, #zLandscapeRow]  \ Subtract a tile's width from zLandscapeRow
 SUB     R0, R0, #TILE_SIZE         \ to move the coordinates of the current row
 STR     R0, [R11, #zLandscapeRow]  \ forward by one whole tile

 SUB     R9, R9, #TILE_SIZE         \ Subtract a tile's width from R9 to step
                                    \ along the z-axis by one whole tile, going
                                    \ from back to front

 MOV     R0, #&80000000             \ Store &80000000 in the address in R7 to
 STR     R0, [R7]                   \ reset the store, so it's ready to be used
                                    \ to store the new row's pixel corner
                                    \ coordinates

 LDRB    R0, [R11, #tileRowOddEven] \ Flip the value of tileRowOddEven between
 EORS    R0, R0, #1                 \ 0 and 1, setting the flags to feed into
 STRB    R0, [R11, #tileRowOddEven] \ the following logic

 ADDNE   R6, R11, #cornerStore1     \ Do the following after drawing the first
 ADDEQ   R6, R11, #cornerStore2     \ tile row and then every other row;
 ADDEQ   R7, R11, #cornerStore1     \
 ADDNE   R7, R11, #cornerStore2     \   * R6 = address of cornerStore1
                                    \   * R7 = address of cornerStore2
                                    \
                                    \ Or do the following after drawing the
                                    \ second tile row and then every other row;
                                    \
                                    \   * R6 = address of cornerStore2
                                    \   * R7 = address of cornerStore1
                                    \
                                    \ So R6 and R7 swap over after each row, so
                                    \ R6 always points to the set of pixel
                                    \ corner coordinates from the previous row,
                                    \ and R7 points to the place where we store
                                    \ this row's pixel corner coordinates

 B       land1                      \ Loop back to land1 to process the next row
                                    \ of tile corners

\ ******************************************************************************
\
\       Name; DrawLandscapeAndBuffers [Part 4 of 4]
\       Type; Subroutine
\   Category; Landscape
\    Summary; Draw the remaining graphics buffers
\  Deep dive; Drawing the landscape
\             Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.land5

 MOV     R0, #TILES_Z - 2           \ Draw the contents of the penultimate
 BL      DrawGraphicsBuffer         \ graphics buffer

 MOV     R0, #TILES_Z - 1           \ Draw the contents of the last graphics
 BL      DrawGraphicsBuffer         \ buffer

 LDMFD   R13!, {R5-R9, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; GetLandscapeAltitude
\       Type; Subroutine
\   Category; Landscape
\    Summary; Calculate the altitude of the landscape for a given coordinate
\  Deep dive; Generating the landscape
\             Drawing the landscape
\
\ ------------------------------------------------------------------------------
\
\ This routine calculates the altitude of the landscape at a given coordinate.
\ The altitude is inverse, with lower values indicating higher altitudes. The
\ launchpad is at LAUNCHPAD_ALTITUDE, while the sea is at SEA_LEVEL.
\
\ The altitude at landscape coordinate [x, z] is calculated as follows;
\
\   LAND_MID_HEIGHT - [   2*sin[x - 2z]  + 2*sin[4x + 3z]  + 2*sin[3z - 5x]
\                       + 2*sin[3x + 3z] +   sin[5x + 11z] +   sin[10x + 7z]
\                     ] / 256
\
\ Note that the object map is flat, like a paper map, so the x- and z-axes on
\ the map correspond to the x- and z-axes in the three-dimensional space when
\ the map is laid out on the landscape [as the 3D z-axis goes into the screen].
\ When talking about the map, we are talking about [x, z] coordinates that are
\ a bit like longitude and latitude, and this routine returns the y-coordinate
\ of the point on the landscape [as the y-axis goes down the screen and
\ determines the altitude].
\
\ Note that more negative values denote lower altitudes, as the y-axis goes down
\ the screen, working down from zero at the very highest altitude in space. This
\ is the opposite to conventional altitudes in the real world.
\
\ The altitude is capped to a maximum value of SEA_LEVEL, and the altitude on
\ the launchpad is set to LAUNCHPAD_ALTITUDE. The launchpad is defined as [x, z]
\ where 0 <= x < LAUNCHPAD_SIZE and 0 <= z < LAUNCHPAD_SIZE, so that's tiles 0
\ to 7 along each axis [with the origin being at the front-left corner of the
\ launchpad].
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R8                              The x-coordinate of the landscape coordinate
\
\   R9                              The z-coordinate of the landscape coordinate
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R0                              The altitude [y-coordinate] of the landscape
\                                   at these coordinates, with more negative
\                                   values denoting lower altitudes, as the
\                                   y-axis points downwards
\
\ ******************************************************************************

.GetLandscapeAltitude

 LDR     R0, [R11, #altitude]       \ Set prevAltitude = altitude, so we store
 STR     R0, [R11, #prevAltitude]   \ the altitude from the previous calculation

                                    \ In the following commentary, we will refer
                                    \ to the coordinates in R8 and R9 as x and z

 SUB     R0, R8, R9, LSL #1         \ Set R0 = R8 - R9 << 1
                                    \        = x - 2z

 LDR     R2, sinTableAddr           \ Set R2 to the address of the sine lookup
                                    \ table

 BIC     R0, R0, #&00300000         \ Clear bits 21 and 22 of R0 so when we
                                    \ shift R0 to the right by 20 places in the
                                    \ following lookup, the address is aligned
                                    \ to the words in the lookup table

 LDR     R0, [R2, R0, LSR #20]      \ Set
                                    \
                                    \   R0 =   [2^31 - 1]
                                    \        * SIN[2 * PI * [[R0 >> 20] / 1024]]
                                    \
                                    \ So R0 = sin[R0]
                                    \       = sin[x - 2z]

 MOV     R0, R0, ASR #7             \ Set R0 = R0 >> 7
                                    \        = sin[x - 2z] / 128

 ADD     R1, R9, R8, LSL #1         \ R1 = R9 + R8 << 1
                                    \    = 2x + z

 ADD     R1, R9, R1, LSL #1         \ R1 = R9 + R1 << 1
                                    \    = z + [2x + z] << 1
                                    \    = z + 4x + 2z
                                    \    = 4x + 3z

 ADD     R3, R1, R8                 \ R3 = R1 + R8
                                    \    = 4x + 3z + x
                                    \    = 5x + 3z

                                    \ So the above gives us;
                                    \
                                    \   R0 = sin[x - 2z] / 128
                                    \   R1 = 4x + 3z
                                    \   R3 = 5x + 3z

 LDR     R2, sinTableAddr           \ Using the same approach as above, set;
 BIC     R1, R1, #&00300000         \
 LDR     R1, [R2, R1, LSR #20]      \   R1 = sin[R1]
                                    \      = sin[4x + 3z]

 ADD     R0, R0, R1, ASR #7         \ R0 = R0 + R1 >> 7
                                    \    = [sin[x - 2z] + sin[4x + 3z]] / 128

 SUB     R1, R9, R8, LSL #1         \ R1 = R9 - R8 << 1
                                    \    = z - 2x

 RSB     R1, R8, R1, LSL #1         \ R1 = [R1 << 1] - R8
                                    \    = [z - 2x] << 1 - x
                                    \    = 2z - 4x - x
                                    \    = 2z - 5x

 ADD     R1, R1, R9                 \ R1 = R1 + R9
                                    \    = 2z - 5x + z
                                    \    = 3z - 5x

                                    \ So the above gives us;
                                    \
                                    \   R0 = [sin[x - 2z] + sin[4x + 3z]] / 128
                                    \   R1 = 3z - 5x

 LDR     R2, sinTableAddr           \ Using the same approach as above, set;
 BIC     R1, R1, #&00300000         \
 LDR     R1, [R2, R1, LSR #20]      \   R1 = sin[R1]
                                    \      = sin[3z - 5x]

 ADD     R0, R0, R1, ASR #7         \ R0 = R0 + R1 >> 7
                                    \    = [sin[x - 2z] + sin[4x + 3z]
                                    \                   + sin[3z - 5x]] / 128

 ADD     R1, R9, R8, LSL #1         \ R1 = R9 + R8 << 1
                                    \    = z + 2x
                                    \    = 2x + z

 ADD     R1, R9, R1, LSL #2         \ R1 = R9 + R1 << 2
                                    \    = z + [2x + z] << 2
                                    \    = z + 4x + 2z
                                    \    = 4x + 3z

 SUB     R1, R1, R8                 \ R1 = R1 - R8
                                    \    = 4x + 3z - x
                                    \    = 3x + 3z

                                    \ So the above gives us;
                                    \
                                    \   R0 = [sin[x - 2z] + sin[4x + 3z]
                                    \                     + sin[3z - 5x]] / 128
                                    \   R1 = 3x + 3z

 LDR     R2, sinTableAddr           \ Using the same approach as above, set;
 BIC     R1, R1, #&00300000         \
 LDR     R1, [R2, R1, LSR #20]      \   R1 = sin[R1]
                                    \      = sin[3x + 3z]

 ADD     R0, R0, R1, ASR #7         \ R0 = R0 + R1 >> 7
                                    \    = [sin[x - 2z] + sin[4x + 3z]
                                    \                   + sin[3z - 5x]
                                    \                   + sin[3x + 3z]] / 128

 ADD     R1, R3, R9, LSL #3         \ R1 = R3 + R9 << 3
                                    \    = 5x + 3z + 8z
                                    \    = 5x + 11z

                                    \ So the above gives us;
                                    \
                                    \   R0 = [sin[x - 2z] + sin[4x + 3z]
                                    \                     + sin[3z - 5x]
                                    \                     + sin[3x + 3z]] / 128
                                    \   R1 = 5x + 11z

 LDR     R2, sinTableAddr           \ Using the same approach as above, set;
 BIC     R1, R1, #&00300000         \
 LDR     R1, [R2, R1, LSR #20]      \   R1 = sin[R1]
                                    \      = sin[5x + 11z]

 ADD     R0, R0, R1, ASR #8         \ R0 = R0 + R1 >> 8
                                    \    = [sin[x - 2z] + sin[4x + 3z]
                                    \                   + sin[3z - 5x]
                                    \                   + sin[3x + 3z]] / 128
                                    \      + sin[5x + 11z] / 256

 ADD     R1, R9, R3, LSL #1         \ R1 = R9 + R3 << 1
                                    \    = z + [5x + 3z] << 1
                                    \    = z + 10x + 6z
                                    \    = 10x + 7z

                                    \ So the above gives us;
                                    \
                                    \   R0 = [sin[x - 2z] + sin[4x + 3z]
                                    \                     + sin[3z - 5x]
                                    \                     + sin[3x + 3z]] / 128
                                    \        + sin[5x + 11z] / 256
                                    \   R1 = 10x + 7z

 LDR     R2, sinTableAddr           \ Using the same approach as above, set;
 BIC     R1, R1, #&00300000         \
 LDR     R1, [R2, R1, LSR #20]      \   R1 = sin[R1]
                                    \      = sin[10x + 7z]

 ADD     R0, R0, R1, ASR #8         \ R0 = R0 + R1 >> 8
                                    \    = [sin[x - 2z] + sin[4x + 3z]
                                    \                   + sin[3z - 5x]
                                    \                   + sin[3x + 3z]] / 128
                                    \      +
                                    \      [sin[5x + 11z] + sin[10x + 7z]] / 256

 RSB     R0, R0, #LAND_MID_HEIGHT   \ R0 = LAND_MID_HEIGHT - R0
                                    \
                                    \    = LAND_MID_HEIGHT -
                                    \      [2*sin[x - 2z] + 2*sin[4x + 3z]
                                    \                     + 2*sin[3z - 5x]
                                    \                     + 2*sin[3x + 3z]
                                    \                     + sin[5x + 11z]
                                    \                     + sin[10x + 7z]] / 256
                                    \
                                    \ which is the result that we want

 CMP     R0, #SEA_LEVEL             \ If R0 > SEA_LEVEL, set R0 = SEA_LEVEL so
 MOVGT   R0, #SEA_LEVEL             \ we don't create landscapes lower then sea
                                    \ level

 CMP     R8, #LAUNCHPAD_SIZE        \ If R8 and R9 are both < LAUNCHPAD_SIZE,
 CMPLO   R9, #LAUNCHPAD_SIZE        \ then this coordinate is on the launchpad,
 MOVLO   R0, #LAUNCHPAD_ALTITUDE    \ so set R0 = LAUNCHPAD_ALTITUDE

 STR     R0, [R11, #altitude]       \ Set altitude = R0, so we return the result
                                    \ in R0 and set the altitude variable

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; GetLandscapeBelowVertex
\       Type; Subroutine
\   Category; Landscape
\    Summary; Calculate the landscape altitude directly below an object's vertex
\  Deep dive; Generating the landscape
\             Drawing 3D objects
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The address containing the object's vertex
\                                   [x, y, z], relative to the camera position
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R0                              The altitude [y-coordinate] of the landscape
\                                   directly below the coordinate
\
\ ******************************************************************************

.GetLandscapeBelowVertex

 STMFD   R13!, {R8-R9, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R8, [R0]                   \ Add xCamera to the x-coordinate in R8 to
 LDR     R1, [R11, #xCamera]        \ get the vertex position in the game's
 ADD     R8, R8, R1                 \ world coordinate system

 LDR     R9, [R0, #8]               \ Add zCamera to the z-coordinate in R8 to
 LDR     R1, [R11, #zCamera]        \ get the vertex position in the game's
 ADD     R9, R9, R1                 \ world coordinate system

 SUB     R9, R9, #LANDSCAPE_Z       \ Move the z-coordinate forward by the
                                    \ landscape offset, as the altitude
                                    \ calculation needs the coordinate to be
                                    \ relative to the front-centre point of the
                                    \ landscape

                                    \ The [x, z] coordinate in [R8, R9] is now
                                    \ relative to the game's coordinate system,
                                    \ rather than the camera or the landscape
                                    \ offset, which is what we need in order
                                    \ to calculate the altitude of the landscape
                                    \ at this point

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [x, z] = [R8, R9], which is
                                    \ the point directly below the vertex

 LDR     R14, [R11, #yCamera]       \ Subtract yCamera from the altitude so the
 SUB     R0, R0, R14                \ result is relative to the camera position

 LDMFD   R13!, {R8-R9, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; GetLandscapeTileColour
\       Type; Subroutine
\   Category; Landscape
\    Summary; Calculate the colour of the landscape tile currently being drawn
\  Deep dive; Drawing the landscape
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R8                              The colour of the landscape tile
\
\ ******************************************************************************

.GetLandscapeTileColour

 STMFD   R13!, {R0-R4, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R3, [R11, #prevAltitude]   \ Set R3 to the altitude of the previous
                                    \ point on the landscape

 LDR     R4, [R11, #altitude]       \ Set R4 to the altitude of the current
                                    \ point on the landscape

 SUBS    R14, R3, R4                \ Set R14 to the slope of the tile, which we
 MOVMI   R14, #0                    \ get from the change of altitude from the
                                    \ previous point to this one, making sure
                                    \ the slope value is greater than zero;
                                    \
                                    \   R14 = min[0, prevAltitude - altitude]
                                    \
                                    \ The y-axis that measures altitude goes
                                    \ down the screen [counterintuitively], so
                                    \ if prevAltitude has a bigger value than
                                    \ altitude, this means the previous point
                                    \ is lower down than the current point
                                    \
                                    \ As we draw the landscape from left to
                                    \ right, this means the slope value will be
                                    \ non-zero for tiles that face left, with a
                                    \ greater value for steeper slopes, while
                                    \ tiles that face right will have a slope
                                    \ value of zero

                                    \ We now calculate the colour, with the red,
                                    \ green and blue channels in R0, R1 and R2
                                    \ respectively

 AND     R2, R4, #&10               \ This instruction appears to have no
                                    \ effect, as we overwrite the result in the
                                    \ next instruction, but it looks like it's
                                    \ all that remains of an experiment to add
                                    \ blue to the landscape

 MOV     R2, #0                     \ Set the blue channel in R2 to zero, as we
                                    \ only use blue for the sea

                                    \ We now set the green and red channels
                                    \ depending on bits 2 and 3 of the altitude,
                                    \ for red and green respectively
                                    \
                                    \ This makes the green channel change more
                                    \ slowly between neighbouring tiles, with
                                    \ the red channel changing more quickly,
                                    \ giving the overall effect of a gentle
                                    \ green landscape pockmarked with small
                                    \ groups of red-brown dirt

 AND     R1, R4, #%00001000         \ Set the green channel in R1 to bit 3 of
 MOV     R1, R1, LSR #1             \ the current point's altitude, as follows;
 ADD     R1, R1, #4                 \
                                    \   R1 = [bit 3] * 4 + 4
                                    \
                                    \ So it's 4 if bit 3 is clear, or 8 if bit 3
                                    \ is set

 AND     R0, R4, #%00000100         \ Set the red channel in R0 to bit 2 of the
                                    \ current point's altitude

 CMP     R4, #LAUNCHPAD_ALTITUDE    \ If the current point is on the launchpad,
 MOVEQ   R0, #4                     \ set the colour to grey, i.e. red, green
 MOVEQ   R1, #4                     \ and blue all have the same value of 4
 MOVEQ   R2, #4

 CMP     R4, #SEA_LEVEL             \ If both the previous and current points
 CMPEQ   R3, #SEA_LEVEL             \ are at sea level, set the colour to blue,
 MOVEQ   R1, #0                     \ i.e. the blue channel has value 4 while
 MOVEQ   R2, #4                     \ red and green are zero
 MOVEQ   R0, #0

 LDRB    R8, [R11, #tileCornerRow]  \ Set R8 to the number of the tile corner
                                    \ row that we're processing, so it goes from
                                    \ 1 at the back to TILES_Z - 1 at the front,
                                    \ or 1 to 10 [it doesn't start at zero as we
                                    \ don't draw any tiles for the very first
                                    \ row of tile corners, so we don't call this
                                    \ routine]

 ADD     R3, R8, R14, LSR #22       \ Set the tile's brightness in R3 to;
                                    \
                                    \   tileCornerRow + slope >> 22
                                    \
                                    \ where tileCornerRow is 1 at the back and
                                    \ TILES_Z - 1 at the front
                                    \
                                    \ As we draw the landscape from back to
                                    \ front and left to right, this means that;
                                    \
                                    \   * Tiles nearer the front will have a
                                    \     higher brightness level than those at
                                    \     the back [as tileCornerRow will be
                                    \     higher for closer tiles]
                                    \
                                    \   * Tiles that face to the left will have
                                    \     a higher brightness level than those
                                    \     that face to the right, with steeper
                                    \     sloping tiles being brighter than
                                    \     shallow ones [as slope will be higher
                                    \     for steeper sloping tiles]
                                    \
                                    \   * Tiles that face to the right will have
                                    \     fixed brightness levels that only vary
                                    \     with distance and not with slope [as
                                    \     slope is zero for tiles that face
                                    \     right]
                                    \
                                    \ This implements a light source that is
                                    \ directly above and slightly to the left

 ADD     R0, R0, R3                 \ Add the brightness in R3 to all three
 ADD     R1, R1, R3                 \ channels
 ADD     R2, R2, R3

 CMP     R0, #16                    \ Ensure that the red channel in R0 fits
 MOVHS   R0, #15                    \ into four bits

 CMP     R1, #16                    \ Ensure that the green channel in R1 fits
 MOVHS   R1, #15                    \ into four bits

 CMP     R2, #16                    \ Ensure that the blue channel in R2 fits
 MOVHS   R2, #15                    \ into four bits

                                    \ We now build a VIDC colour number in R8
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R8 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R8, R1, R2                 \ Set R8 to the bottom three bits of;
 AND     R8, R8, #%00000011         \
 ORR     R8, R8, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R8, R8, #%00000111         \
                                    \ So this sets bits 0, 1 and 2 of R8 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R8, R8, #%00010000         \ set bit 4 of R8

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R8, R8, R1, LSL #3         \ And stick them into bits 5-6 of R8

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R8, R8, #%00001000         \ set bit 3 of R8

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R8, R8, #%10000000         \ set bit 7 of R8

 ORR     R8, R8, R8, LSL #8         \ Duplicate the lower byte of R8 into the
 ORR     R8, R8, R8, LSL #16        \ other three bytes in the word to produce
 ORR     R8, R8, R8, LSL #24        \ a four-pixel colour word containing four
                                    \ pixels of this colour

 LDMFD   R13!, {R0-R4, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; MoveAndDrawPlayer [Part 1 of 5]
\       Type; Subroutine
\   Category; Player
\    Summary; Process player movement and draw the player's ship into the
\             graphics buffers, starting with reading the mouse position
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

.MoveAndDrawPlayer

 STMFD   R13!, {R14}                \ Store the return address on the stack

                                    \ We start by reading the position of the
                                    \ mouse and generating a rotation matrix for
                                    \ the player's ship, based on its new
                                    \ orientation

 SWI     OS_Mouse                   \ Read the mouse coordinates, returning;
                                    \
                                    \   R0 = x-coordinate
                                    \
                                    \   R1 = y-coordinate
                                    \
                                    \   R2 = mouse buttons [%lmr]

 STRB    R2, [R11, #fuelBurnRate]   \ Set the fuel burn rate to the mouse button
                                    \ result, so it's set to;
                                    \
                                    \   * %000 [0] if no buttons are being
                                    \     pressed
                                    \
                                    \   * %001 [1] if the right button is being
                                    \     pressed [i.e. fire bullets]
                                    \
                                    \   * %010 [2] if the middle button is being
                                    \     pressed [i.e. hover]
                                    \
                                    \   * %100 [4] if the left button is being
                                    \     pressed [i.e. full thrust]
                                    \
                                    \ Bit 0 of the fuel rate is ignored in the
                                    \ fuel calculations, so firing bullets does
                                    \ not burn fuel, even though fuelBurnRate is
                                    \ set to a non-zero value

 LDR     R14, [R11, #fuelLevel]     \ If the fuel level is zero, also zero the
 CMP     R14, #0                    \ fuel burn rate, as we can't burn fuel that
 STREQB  R14, [R11, #fuelBurnRate]  \ we don't have

                                    \ At the start of each new life, the mouse
                                    \ is initialised to coordinates [511, 511]

 CMP     R0, #1024                  \ Cap the mouse x-coordinate in R0 so it's
 MOVHS   R0, #1024                  \ in the range 0 to 1023
 SUBHS   R0, R0, #1

 SUB     R0, R0, #512               \ Convert R0 into the range -512 to +511

 RSB     R1, R1, #1024              \ Set R1 = 1024 - R1 - 512
 SUB     R1, R1, #512               \        = 512 - R1
                                    \
                                    \ So the mouse y-coordinate in R1 is now in
                                    \ the range -512 to +512

 MOV     R0, R0, LSL #22            \ Scale both mouse coordinates up as far as
 MOV     R1, R1, LSL #22            \ possible without losing data [512 << 22 is
                                    \ &80000000, so this is as high as we can
                                    \ go]

 BL      GetMouseInPolarCoordinates \ Convert the mouse coordinates into polar
                                    \ coordinates, returning the polar angle in
                                    \ R1 and the polar distance in R0

 CMP     R0, #&40000000             \ Cap the polar distance in R0 so it's in
 MOVHS   R0, #&40000000             \ the range 0 to &3FFFFFFF
 SUBHS   R0, R0, #1

 MOV     R0, R0, LSL #1             \ Scale R0 to the range 0 to &7FFFFFFE

 LDR     R2, [R11, #shipPitch]      \ Set R2 = shipPitch

 LDR     R3, [R11, #shipDirection]  \ Set R3 = shipDirection

 SUBS    R4, R3, R1                 \ Set R4 = R3 - R1
                                    \        = shipDirection - polar angle

 BMI     ship1                      \ If the result is negative, jump to ship1
                                    \ so we cap R4 against negative values

 CMP     R4, #&30000000             \ Cap the value in R4 to a maximum magnitude
 MOVHS   R4, #&30000000             \ of &30000001

 B       ship2                      \ Jump to ship2 to skip the following and
                                    \ keep going

.ship1

 CMN     R4, #&30000000             \ Cap the value in R4 to a maximum magnitude
 MVNLO   R4, #&30000000             \ of -&30000001

.ship2

 SUBS    R5, R2, R0                 \ Set R5 = R2 - R0
                                    \        = shipPitch - polar distance

 BLE     ship3                      \ If the result is zero or negative, jump to
                                    \ ship3 so we cap R5 against negative values

 CMP     R5, #&30000000             \ Cap the value in R5 to a maximum magnitude
 MOVHS   R5, #&30000000             \ of &30000001

 B       ship4                      \ Jump to ship4 to skip the following and
                                    \ keep going

.ship3

 CMN     R5, #&30000000             \ Cap the value in R5 to a maximum magnitude
 MVNLO   R5, #&30000000             \ of -&30000001

.ship4

                                    \ We now update the rotation angles with the
                                    \ latest mouse values [in the form of the
                                    \ distance and angle]
                                    \
                                    \ We do this by adding half of the new value
                                    \ and half of the old value, which seems to
                                    \ apply some kind of damping to the controls

 SUB     R0, R2, R5, ASR #1         \ Set R0 = R2 - R5 / 2
                                    \        = shipPitch
                                    \          - [shipPitch - distance] / 2

 SUB     R1, R3, R4, ASR #1         \ Set R1 = R3 - R4 / 2
                                    \        = shipDirection
                                    \          - [shipDirection - angle] / 2

 STR     R0, [R11, #shipPitch]      \ Store the updated value in shipPitch

 STR     R1, [R11, #shipDirection]  \ Store the updated value in shipDirection

 BL      CalculateRotationMatrix    \ Calculate the rotation matrix from the
                                    \ updated angles given in R0 and R1, so
                                    \ this sets the rotation matrix for the
                                    \ player's ship

                                    \ Now that we have the player's rotation
                                    \ matrix, we can move on to the ship's
                                    \ movement in space

\ ******************************************************************************
\
\       Name; MoveAndDrawPlayer [Part 2 of 5]
\       Type; Subroutine
\   Category; Player
\    Summary; Update the player's velocity and coordinates
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

 ADD     R14, R11, #xPlayer         \ Set R0 to R5 as follows;
 LDMIA   R14, {R0-R5}               \
                                    \   R0 = xPlayer
                                    \   R1 = yPlayer
                                    \   R2 = zPlayer
                                    \
                                    \   R3 = xVelocity
                                    \   R4 = yVelocity
                                    \   R5 = zVelocity
                                    \
                                    \ So [R0, R1, R2] is the player's coordinate
                                    \ and [R3, R4, R5] is the player's velocity
                                    \ vector
                                    \
                                    \ We now update the velocity by adding in
                                    \ various factors such as friction, thrust
                                    \ and gravity

 CMN     R1, #HIGHEST_ALTITUDE      \ If R1 is higher than the altitude where
 LDRLTB  R9, [R11, #fuelBurnRate]   \ the engines cut out, clear bits 2 and 3 of
 BICLT   R9, R9, #%00000110         \ the fuel burn rate to cut the engines
 STRLTB  R9, [R11, #fuelBurnRate]

 LDR     R6, [R11, #xRoofV]         \ Set [R6, R7, R8] to the roof vector from
 LDR     R7, [R11, #yRoofV]         \ the rotation matrix, which is the vector
 LDR     R8, [R11, #zRoofV]         \ that points directly down through the
                                    \ ship's floor as the y-axis is inverted
                                    \ [so it's in the direction of thrust, as
                                    \ the thrusters are on the bottom of the
                                    \ ship]
                                    \
                                    \ Let's refer to this thrust vector as
                                    \ follows;
                                    \
                                    \   R6 = xExhaust
                                    \   R7 = yExhaust
                                    \   R8 = zExhaust

 LDRB    R9, [R11, #fuelBurnRate]   \ Set R9 to the fuel burn rate

 TST     R9, #%00000100             \ Set the flags according to bit 2 of the
                                    \ fuel burn rate, which is set if the left
                                    \ button is being pressed [i.e. full thrust]

                                    \ We start by updating xVelocity in R3

 SUB     R3, R3, R3, ASR #6         \ Set R3 = R3 - R3 >> 6
                                    \        = xVelocity - [xVelocity / 64]
                                    \
                                    \ This reduces the velocity along the x-axis
                                    \ by 1/64, so this applies a deceleration in
                                    \ this direction [due to friction]

 SUBNE   R3, R3, R6, ASR #11        \ Increase R3 by a further xExhaust / 2048
                                    \ if full thrust is being pressed, so this
                                    \ applies the engine thrust
                                    \
                                    \ We subtract the exhaust vector to apply
                                    \ thrust because the direction of the thrust
                                    \ applied by the plume is in the opposite
                                    \ direction to the plume [in other words, a
                                    \ rocket's plume pushes downwards so the
                                    \ rocket can rise up]

 ADD     R0, R0, R3                 \ Set R0 = R0 + R3
                                    \        = xPlayer + R3
                                    \
                                    \ This applies the newly calculated velocity
                                    \ to the player's x-coordinate, which moves
                                    \ the player in 3D space

                                    \ Next we update yVelocity in R4

 SUB     R4, R4, R4, ASR #6         \ Set R4 = R4 - R4 >> 6
                                    \        = yVelocity - [yVelocity / 64]
                                    \
                                    \ This reduces the velocity along the y-axis
                                    \ by 1/64, so this applies a deceleration in
                                    \ this direction [due to friction]

 SUBNE   R4, R4, R7, ASR #11        \ Increase R3 by a further yExhaust / 2048
                                    \ if full thrust is being pressed, so this
                                    \ applies the engine thrust
                                    \
                                    \ We subtract the exhaust vector to apply
                                    \ thrust because the direction of the thrust
                                    \ applied by the plume is in the opposite
                                    \ direction to the plume [in other words, a
                                    \ rocket's plume pushes downwards so the
                                    \ rocket can rise up]

 ADD     R1, R1, R4                 \ Set R1 = R1 + R4
                                    \        = yPlayer + R4
                                    \
                                    \ This applies the newly calculated velocity
                                    \ to the player's y-coordinate, which moves
                                    \ the player in 3D space

                                    \ And finally we update zVelocity in R4

 SUB     R5, R5, R5, ASR #6         \ Set R5 = R5 - R5 >> 6
                                    \        = zVelocity - [zVelocity / 64]
                                    \
                                    \ This reduces the velocity along the z-axis
                                    \ by 1/64, so this applies a deceleration in
                                    \ this direction [due to friction]

 SUBNE   R5, R5, R8, ASR #11        \ Increase R3 by a further zExhaust / 2048
                                    \ if full thrust is being pressed, so this
                                    \ applies the engine thrust
                                    \
                                    \ We subtract the exhaust vector to apply
                                    \ thrust because the direction of the thrust
                                    \ applied by the plume is in the opposite
                                    \ direction to the plume [in other words, a
                                    \ rocket's plume pushes downwards so the
                                    \ rocket can rise up]

 ADD     R2, R2, R5                 \ Set R2 = R2 + R5
                                    \        = zPlayer + R5
                                    \
                                    \ This applies the newly calculated velocity
                                    \ to the player's z-coordinate, which moves
                                    \ the player in 3D space

                                    \ By this point we have updated the player's
                                    \ coordinates in [R0, R1, R2] by the new
                                    \ velocity in [R3, R4, R5]

 TST     R9, #%00000010             \ Set the flags according to bit 1 of the
                                    \ fuel burn rate, which is set if the middle
                                    \ button is being pressed [i.e. hover]

 SUBNE   R3, R3, R6, ASR #13        \ If the middle button is being pressed, for
 SUBNE   R4, R4, R7, ASR #13        \ hovering mode, then apply a quarter of the
 SUBNE   R5, R5, R8, ASR #13        \ full thrust vector to the velocity, rather
                                    \ than the full thrust vector we apply for
                                    \ the left button
                                    \
                                    \ Note that the hover thrust is applied
                                    \ after we applied the velocity vector to
                                    \ the player's coordinates, so hovering has
                                    \ a slightly delayed impact on the ship, to
                                    \ simulate the effects of inertia

 LDR     R9, [R11, #gravity]        \ Add the effect of gravity to yVelocity in
 ADD     R4, R4, R9                 \ R4, adding it to R4 as it is a downwards
                                    \ force along the z-axis

 STMIA   R14, {R0-R8}               \ Store the updated player coordinates, ship
                                    \ velocity and thrust vector as follows;
                                    \
                                    \   xPlayer = R0
                                    \   yPlayer = R1
                                    \   zPlayer = R2
                                    \
                                    \   xVelocity = R3
                                    \   yVelocity = R4
                                    \   zVelocity = R5
                                    \
                                    \   xExhaust = R6
                                    \   yExhaust = R7
                                    \   zExhaust = R8
                                    \
                                    \ We use these values in part 4

\ ******************************************************************************
\
\       Name; MoveAndDrawPlayer [Part 3 of 5]
\       Type; Subroutine
\   Category; Player
\    Summary; Check for collisions and draw the ship
\  Deep dive; Drawing 3D objects
\             Collisions and bullets
\             Flying by mouse
\
\ ******************************************************************************

 CMP     R1, #0                     \ If R1 [i.e. the y-coordinate of the ship]
 MOVPL   R1, #0                     \ is positive, then set it to zero
                                    \
                                    \ As we store this value as the camera's
                                    \ y-coordinate below, this ensures that the
                                    \ camera doesn't drop down all the way with
                                    \ the ship in last few moments before it
                                    \ lands, and similarly it doesn't start
                                    \ rising up with the ship as it takes off
                                    \ until the ship is at a reasonable height

 ADD     R2, R2, #CAMERA_PLAYER_Z   \ Set R2 to the z-coordinate we want to use
                                    \ for the camera, which is at the back of
                                    \ the landscape

 ADD     R14, R11, #xCamera         \ Set [xCamera, yCamera, zCamera] to the
 STMIA   R14, {R0-R2}               \ coordinates in [R0, R1, R2], which is at
                                    \ the back of the landscape and in the
                                    \ middle
                                    \
                                    \ This sets the camera so that it follows
                                    \ the player's ship as it flies around,
                                    \ and is positioned at the back of the
                                    \ visible screen

 MOV     R8, R0                     \ Set [R8, R9] to the point on the landscape
 MOV     R9, R2                     \ that's directly below the player, by
 SUB     R9, R9, #CAMERA_PLAYER_Z   \ setting [R8, R9] to the ship's [x, z]
                                    \ coordinates in [R0, R2] and subtracting
                                    \ the five tiles we added above

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [x, z] = [R8, R9]

 SUB     R0, R0, #UNDERCARRIAGE_Y   \ Set R0 to the altitude of a point that's
                                    \ the height of the ship's undercarriage
                                    \ above the point on the landscape beneath
                                    \ the player, which is the lowest altitude
                                    \ the player can be without being in danger
                                    \ of hitting the ground with the bottom of
                                    \ the ship

 LDR     R1, [R11, #yPlayer]        \ Set R14 to the y-coordinate of the player
                                    \ in yPlayer

 SUB     R14, R0, R1                \ If R0 - yPlayer >= SAFE_HEIGHT [1.5 tile
 CMP     R14, #SAFE_HEIGHT          \ sizes] then this means the bottom of the
 BHS     ship6                      \ player's ship is safely clear of objects
                                    \ on the ground, as SAFE_HEIGHT is the
                                    \ minimum safe height for avoiding objects
                                    \ on the ground, and the undercarriage of
                                    \ the player's ship is above this height
                                    \
                                    \ So if this is the case, jump to ship6 to
                                    \ skip the following set of collision
                                    \ checks, as we know we are safe

 STMFD   R13!, {R0}                 \ Store R0 on the stack so we can retrieve
                                    \ it below

 ADD     R14, R11, #objectMap       \ Set R14 to the address of the object map

 AND     R9, R9, #&FF000000         \ Clip R9 down to the nearest tile

 ADD     R14, R14, R8, LSR #24      \ Set R14 = R6 + [R8 >> 24] + [R9 >> 16]
 ADD     R14, R14, R9, LSR #16      \
                                    \ R8 is shifted into the bottom byte of R14,
                                    \ so that's the x-coordinate, and R9 is
                                    \ shifted into the second byte of R14, so
                                    \ that's the z-coordinate, so R14 points to
                                    \ the object map entry for coordinate
                                    \ [R8, R9]

 LDRB    R0, [R14]                  \ Set R0 to the byte in the object map at
                                    \ the coordinate [R8, R9]

 CMP     R0, #0                     \ If the object map entry is zero, jump to
 BEQ     ship5                      \ ship5 to skip the following [though this
                                    \ shouldn't ever happen, as an object type
                                    \ of zero is never added to the object map]

 CMP     R0, #12                    \ If R0 < 12 then the object map entry is a
 ADDLO   R13, R13, #4               \ valid object number in the range 1 to 11,
 BLO     LoseLife                   \ so the player's ship just hit the object
                                    \ below it on the ground, so jump to
                                    \ LoseLife to show the explosion animation
                                    \ and lose a life

.ship5

 LDMFD   R13!, {R0}                 \ Retrieve R0 from the stack, so it is once
                                    \ again the lowest altitude the player can
                                    \ be without being in danger of hitting the
                                    \ ground

.ship6

                                    \ If we get here then R0 is the lowest
                                    \ altitude the player can be without being
                                    \ in danger of hitting the ground, and R1 is
                                    \ the y-coordinate of the player, i.e. the
                                    \ player's altitude

 CMP     R1, R0                     \ If R1 > R0 then the player is lower down
 BLGT    LandOnLaunchpad            \ then the safe altitude, so call
                                    \ LandOnLaunchpad to check whether we have
                                    \ landed on the launchpad [as we need to be
                                    \ within the danger zone in order to land]

 CMP     R1, #0                     \ If R1 [i.e. the y-coordinate of the ship]
 MOVMI   R1, #0                     \ is negative, then set it to zero
                                    \
                                    \ This is the converse of the test we did at
                                    \ the start of this part, as it sets R1 to
                                    \ zero only when the altitude of the player
                                    \ is higher than zero, so this only keeps
                                    \ R1 set to the player's y-coordinate when
                                    \ they are really close to the ground
                                    \
                                    \ As we pass this value to DrawObject below,
                                    \ this means we draw the player's ship in
                                    \ the middle of the screen most of the time
                                    \ [by passing an R1 of zero to DrawObject],
                                    \ but when the ship is close to the ground,
                                    \ we draw the ship lower down the screen

 ADD     R3, R11, #rotationMatrix   \ Set R3 to the address of the ship's
                                    \ rotation matrix to pass to DrawObject

 LDR     R14, objectPlayerAddr      \ Set objectData to the object blueprint
 STR     R14, [R11, #objectData]    \ for the player's ship

 MOV     R0, #0                     \ Set the coordinates in [R0, R1, R2] so
 MOV     R2, #LANDSCAPE_Z_MID       \ the ship gets drawn in the middle of the
                                    \ screen or slightly below if the ship is
                                    \ near the ground [as R0 = 0 and R1 is set
                                    \ as described above], at a position into
                                    \ the screen of LANDSCAPE_Z_MID, which
                                    \ places it above the middle of the
                                    \ landscape
                                    \
                                    \ This works because the landscape offset
                                    \ pushes the far edge of the landscape 20
                                    \ tiles into the screen, and the landscape
                                    \ is ten tiles deep, so the centre is 15
                                    \ tiles into the screen

 BL      DrawObject                 \ Draw the player's ship with the correct
                                    \ orientation and position

 LDRB    R10, [R11, #crashedFlag]   \ DrawObject sets crashedFlag to -1 if the
 CMP     R10, #0                    \ ship is lower down than its shadow, to
 BLNE    LoseLife                   \ indicate that it has crashed, so jump to
                                    \ LoseLife is this is the case

\ ******************************************************************************
\
\       Name; MoveAndDrawPlayer [Part 4 of 5]
\       Type; Subroutine
\   Category; Player
\    Summary; Spawn the particles in the exhaust plume if the engine is engaged
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

 LDRB    R10, [R11, #fuelBurnRate]  \ If both bits 1 and 2 of the fuel burn rate
 TST     R10, #%00000110            \ are clear then the engine is not running,
 BEQ     ship7                      \ so jump to ship7 to skip generating an
                                    \ exhaust plume

 ADD     R14, R11, #xVelocity       \ Set R0 to R2 and R6 to R8 as follows by
 LDMIA   R14, {R0-R2, R6-R8}        \ fetching the values we stored in part 2;
                                    \
                                    \   R0 = xVelocity
                                    \   R1 = yVelocity
                                    \   R2 = zVelocity
                                    \
                                    \   R6 = xExhaust
                                    \   R7 = yExhaust
                                    \   R8 = zExhaust
                                    \
                                    \ So [R0, R1, R2] is the player's velocity
                                    \ and [R6, R7, R8] is the player's thrust
                                    \ vector

 ADD     R3, R0, R6, ASR #7         \ Set [R3, R4, R5] as follows;
 ADD     R4, R1, R7, ASR #7         \
 ADD     R5, R2, R8, ASR #7         \   [ [xVelocity + xExhaust / 128] / 2 ]
 MOV     R3, R3, ASR #1             \   [ [yVelocity + yExhaust / 128] / 2 ]
 MOV     R4, R4, ASR #1             \   [ [zVelocity + zExhaust / 128] / 2 ]
 MOV     R5, R5, ASR #1             \
                                    \ So this sets [R3, R4, R5] to a vector in
                                    \ the direction of the player's velocity
                                    \ [so the particles move along with the
                                    \ ship] and in the direction of the exhaust
                                    \ plume [so that's heading away from the
                                    \ engine, in the direction that it's
                                    \ pointing], and we halve the result so they
                                    \ shoot out of the engine but soon get left
                                    \ behind as we blast away

 ADD     R0, R11, #xPlayer          \ Set R0 to R2 as follows;
 LDMIA   R0, {R0-R2}                \ 
                                    \   R0 = xPlayer
                                    \   R1 = yPlayer
                                    \   R2 = zPlayer
                                    \
                                    \ So [R0, R1, R2] is the player's coordinate

 SUB     R0, R0, R3                 \ Set [R0, R1, R2] as follows;
 SUB     R1, R1, R4                 \
 SUB     R2, R2, R5                 \   [ xPlayer - R3 + [xExhaust / 128] ]
 ADD     R0, R0, R6, ASR #7         \   [ yPlayer - R4 + [yExhaust / 128] ]
 ADD     R1, R1, R7, ASR #7         \   [ zPlayer - R5 + [zExhaust / 128] ]
 ADD     R2, R2, R8, ASR #7         \
                                    \ So this sets [R0, R1, R2] to the position
                                    \ of the player's ship, but a little way in
                                    \ the direction of the exhaust plume [so
                                    \ that's below the engine in the direction
                                    \ that it's pointing], and we also subtract
                                    \ the velocity in [R3, R4, R5] because the
                                    \ first thing that happens when we process
                                    \ the particle in MoveAndDrawParticles is
                                    \ to add the velocity, so this cancels that
                                    \ out to ensure the particle starts out
                                    \ along the line of the exhaust plume

                                    \ By this stage we have;
                                    \
                                    \   [R0, R1, R2] = particle coordinate
                                    \
                                    \   [R3, R4, R5] = particle velocity
                                    \
                                    \ We now set the values of R6 to R9 to pass
                                    \ to the AddExhaustParticleToBuffer routine

 MOV     R7, #&001D0000             \ Set bits 16, 18, 19 and 20 of the particle
                                    \ flags, so that's;
                                    \
                                    \   * Bit 16 set = colour fades white to red
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle

 MOV     R6, #8                     \ Set the particle's lifespan counter to 8
                                    \ iterations of the main loop

 MOV     R8, #10                    \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 10],
                                    \ i.e. -&400000 to +&400000

 MOV     R9, #29                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 29],
                                    \ i.e. 0 to 8

 STMFD   R13!, {R0-R9}              \ Store R0 to R9 on the stack so we can
                                    \ fetch this set of values before each call
                                    \ to AddExhaustParticleToBuffer

                                    \ We now call the AddExhaustParticleToBuffer
                                    \ routine eight times if full thrust is
                                    \ engaged, or twice if hover mode is being
                                    \ used [so the exhaust plume is four times
                                    \ denser when full thrust is engaged]

 BL      AddExhaustParticleToBuffer \ Call AddExhaustParticleToBuffer with the
                                    \ set of parameters in R0 to R9 to add the
                                    \ first exhaust plume particle to the
                                    \ particle data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the second particle to the particle
                                    \ data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the third particle to the particle
                                    \ data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the fourth particle to the particle
                                    \ data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the fifth particle to the particle
                                    \ data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the sixth particle to the particle
                                    \ data buffer

 TST     R10, #%00000100            \ If bit 2 of R10 is set then full thrust is
 LDMNEIA R13, {R0-R9}               \ engaged, so fetch the same parameters and
 BLNE    AddExhaustParticleToBuffer \ add the seventh particle to the particle
                                    \ data buffer

 LDMFD   R13!, {R0-R9}              \ Fetch the same parameters and add the
 BL      AddExhaustParticleToBuffer \ final particle to the particle data buffer

\ ******************************************************************************
\
\       Name; MoveAndDrawPlayer [Part 5 of 5]
\       Type; Subroutine
\   Category; Player
\    Summary; Spawn a bullet particle if the fire button is being pressed
\  Deep dive; Collisions and bullets
\
\ ******************************************************************************

.ship7

 TST     R10, #%00000001            \ If bit 0 of the fuel burn rate is clear
 BEQ     ship8                      \ then the fire button is not being pressed,
                                    \ so jump to ship8 to skip the bullet-firing
                                    \ process and return from the subroutine

 LDR     R8, [R11, #currentScore]   \ Decrement the current score by one, as we
 SUB     R8, R8, #1                 \ are about to fire a bullet
 STR     R8, [R11, #currentScore]

 ADD     R14, R11, #xPlayer         \ Set R0 to R5 as follows;
 LDMIA   R14, {R0-R5}               \ 
                                    \   R0 = xPlayer
                                    \   R1 = yPlayer
                                    \   R2 = zPlayer
                                    \
                                    \   R3 = xVelocity
                                    \   R4 = yVelocity
                                    \   R5 = zVelocity
                                    \
                                    \ So [R0, R1, R2] is the player's coordinate
                                    \ and [R3, R4, R5] is the player's velocity

 LDR     R6, [R11, #xNoseV]         \ Set [R6, R7, R8] to the nose vector from
 LDR     R7, [R11, #yNoseV]         \ the rotation matrix, which is the vector
 LDR     R8, [R11, #zNoseV]         \ that points out through the ship's nose,
                                    \ just like the ship's gun
                                    \
                                    \ Let's refer to this gun vector as follows;
                                    \
                                    \   R6 = xGun
                                    \   R7 = yGun
                                    \   R8 = zGun

 ADD     R3, R3, R6, ASR #8         \ Set [R3, R4, R5] as follows;
 ADD     R4, R4, R7, ASR #8         \
 ADD     R5, R5, R8, ASR #8         \   [ xVelocity + xGun / 256 ]
                                    \   [ yVelocity + yGun / 256 ]
                                    \   [ zVelocity + zGun / 256 ]
                                    \
                                    \ So this sets [R3, R4, R5] to a vector in
                                    \ the direction of the player's velocity
                                    \ [so the bullet particles move along with
                                    \ the ship from which they are fired] and
                                    \ then in the direction that the gun is
                                    \ pointing [so they leave the barrel in the
                                    \ correct direction]

 SUB     R0, R0, R3                 \ Set [R0, R1, R2] as follows;
 SUB     R1, R1, R4                 \
 SUB     R2, R2, R5                 \   [ xPlayer - R3 + [xGun / 128] ]
 ADD     R0, R0, R6, ASR #7         \   [ yPlayer - R4 + [yGun / 128] ]
 ADD     R1, R1, R7, ASR #7         \   [ zPlayer - R5 + [zGun / 128] ]
 ADD     R2, R2, R8, ASR #7         \
                                    \ So this sets [R0, R1, R2] to the position
                                    \ of the player's ship, but a little way in
                                    \ the direction of the gun [so the bullet
                                    \ fires out of the end of the gun], and we
                                    \ also subtract the velocity in [R3, R4, R5]
                                    \ because the first thing that happens when
                                    \ we process the particle in
                                    \ MoveAndDrawParticles is to add the
                                    \ velocity, so this cancels that out to
                                    \ ensure the particle starts out at the end
                                    \ of the gun




 MOV     R6, #20                    \ Set the bullet particle's lifespan
                                    \ counter to 20 iterations of the main loop

 MOV     R7, #&01BC0000             \ Set bits 18, 19, 20, 21, 23 and 24 of the
                                    \ particle flags, so that's;
                                    \
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle
                                    \   * Bit 21 set = can destroy objects
                                    \   * Bit 23 set = splash size is big
                                    \   * Bit 24 set = explode on hitting ground

 ORR     R7, R7, #&FF               \ Set the particle colour to white in bits 0
                                    \ to 7 of the particle flag

 BL      AddBulletParticleToBuffer  \ Add a bullet particle to the particle data
                                    \ buffer

.ship8

 LDMFD   R13!, {PC}                 \ Return from the subroutine

\ ******************************************************************************
\
\       Name; LandOnLaunchpad
\       Type; Subroutine
\   Category; Player
\    Summary; Check to see if the player has landed on the launchpad
\  Deep dive; Collisions and bullets
\
\ ------------------------------------------------------------------------------
\
\ We call this routine from MoveAndDrawPlayer when the player's altitude matches
\ that of the launchpad, so this performs additional checks to see if the player
\ just landed.
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R1                              The y-coordinate of the player's ship
\
\ ******************************************************************************

.LandOnLaunchpad

 ADD     R3, R11, #xPlayer          \ Fetch the six words at xPlayer as follows;
 LDMIA   R3, {R0-R5}                \
                                    \  [R0, R1, R2] = the player's coordinates
                                    \           from [xPlayer, yPlayer, zPlayer]
                                    \
                                    \  [R3, R4, R5] = the player's velocity
                                    \     from [xVelocity, yVelocity, zVelocity]

 CMP     R0, #LAUNCHPAD_SIZE        \ If either of xPlayer or zPlayer is >=
 CMPLO   R2, #LAUNCHPAD_SIZE        \ LAUNCHPAD_SIZE then we are not over the
 BHS     LoseLife                   \ launchpad, as the launchpad is defined as
                                    \ this part of the map;
                                    \
                                    \   0 <= x <LAUNCHPAD_SIZE
                                    \
                                    \ and;
                                    \
                                    \   0 <= z <LAUNCHPAD_SIZE
                                    \
                                    \ So we just crashed into the ground rather
                                    \ than the launchpad, so jump to LoseLife to
                                    \ process the crash

 CMP     R3, #0                     \ Set R3 = |R3|
 RSBMI   R3, R3, #0                 \        = |xVelocity|

 CMP     R4, #0                     \ Set R4 = |R4|
 RSBMI   R4, R4, #0                 \        = |yVelocity|

 CMP     R5, #0                     \ Set R5 = |R5|
 RSBMI   R5, R5, #0                 \        = |zVelocity|

 ADD     R3, R3, R4                 \ If R3 + R4 + R5 >= LANDING_SPEED, then;
 ADD     R3, R3, R5                 \ 
 CMP     R3, #LANDING_SPEED         \   |xVelocity| + |yVelocity| + |zVelocity|
 MOVHS   PC, R14                    \
                                    \ is greater than the safe landing speed, so
                                    \ we can't be performing a safe landing, so
                                    \ return from the subroutine

                                    \ If we get here then we have landed, as the
                                    \ altitude checks were made before this
                                    \ routine was called, we know we are above
                                    \ the launchpad, and our speed is slow
                                    \ enough to land
                                    \
                                    \ So now we start refuelling

 MOV     R1, #LAUNCHPAD_Y           \ Set R1 to the y-coordinate of the player's
                                    \ ship as it sits on the launchpad [which is
                                    \ set to the launchpad altitude plus the
                                    \ height of the ship's undercarriage]

 LDR     R3, [R11, #fuelLevel]      \ Bump up the fuel level by 1/160 of a full
 ADD     R3, R3, #&20               \ tank
 CMP     R3, #&1400
 STRLO   R3, [R11, #fuelLevel]

 MOV     R3, #0                     \ Zero the player's velocity
 MOV     R4, #0
 MOV     R5, #0
 STR     R3, [R11, #xVelocity]
 STR     R4, [R11, #yVelocity]
 STR     R5, [R11, #zVelocity]

 STR     R1, [R11, #yPlayer]        \ Set the y-coordinate of the player to the
                                    \ y-coordinate of the launchpad, so the
                                    \ player's ship is resting correctly on the
                                    \ pad

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; LoseLifeFromParticleLoop
\       Type; Subroutine
\   Category; Main loop
\    Summary; Lose a life when a crash is detected in the particle processing
\             loop
\  Deep dive; Collisions and bullets
\
\ ******************************************************************************

.LoseLifeFromParticleLoop

 LDMFD   R13!, {R10, R12}           \ Remove the R10 and R12 registers that the
                                    \ MoveAndDrawParticles put on the stack,
                                    \ leaving just the return address

                                    \ Fall through into LoseLife to lose a life

\ ******************************************************************************
\
\       Name; LoseLife
\       Type; Subroutine
\   Category; Main loop
\    Summary; Display a crash animation when we lose a life and end the game if
\             this is our last life
\  Deep dive; The main game loop
\             Collisions and bullets
\
\ ******************************************************************************

.LoseLife

 MOV     R0, #0                     \ Set playingGame = 0 to flag that the game
 STR     R0, [R11, #playingGame]    \ is no longer being played and that this is
                                    \ the crash animation

 MOV     R0, #30                    \ Set crashLoopCount = 30 to act as a loop
 STR     R0, [R11, #crashLoopCount] \ the crash animation below

 MOV     R8, #81                    \ Set R8 = 81 to use as the size of the
                                    \ explosion in AddExplosionToBuffer

 ADD     R0, R11, #xPlayer          \ Set [R0, R1, R2] to the coordinate in
 LDMIA   R0, {R0-R2}                \ xPlayer

 SUB     R1, R1, #CRASH_CLOUD_Y     \ Subtract CRASH_CLOUD_Y from the ship's
                                    \ y-coordinate so the explosion occurs just
                                    \ above the player's ship [5/16 tile sizes
                                    \ above the ship, to be precise]

 BL      AddExplosionToBuffer       \ Draw a large explosion in place of the
                                    \ player's ship

.lose1

                                    \ We now run a cut-down version of the main
                                    \ loop to display the crash animation [this
                                    \ is like the main loop but without the
                                    \ calls to drop rocks from the sky, draw the
                                    \ player's ship or update the fuel level]

                                    \ We now set up the rotation matrix for the
                                    \ rocks, using the main loop counter to
                                    \ generate rotation angles that change along
                                    \ with the main loop [so the rocks spin at a
                                    \ nice steady speed]

 LDR     R0, [R11, #mainLoopCount]  \ Set R0 = mainLoopCount << 24
 MOV     R0, R0, LSL #24

 MOV     R1, R0, LSL #1             \ Set R1 = mainLoopCount << 25

 BL      CalculateRotationMatrix    \ Calculate the rotation matrix from the
                                    \ 'angles' given in R0 and R1, which we can
                                    \ apply to any rocks we draw in the
                                    \ MoveAndDrawParticles routine [as rocks are
                                    \ only rotating 3D objects apart from the
                                    \ player, and the player calculates its own
                                    \ rotation matrix]

 BL      MoveAndDrawParticles       \ Move and draw all the particles, such as
                                    \ smoke clouds and bullets, into the
                                    \ graphics buffers

 BL      DrawObjects                \ Draw all the objects, such as trees and
                                    \ buildings, into the graphics buffers

 BL      AddTerminatorsToBuffers    \ Add terminators to the ends of the
                                    \ graphics buffers so we know when to stop
                                    \ drawing

 BL      DrawLandscapeAndBuffers    \ Draw the landscape and the contents of the
                                    \ graphics buffers

 BL      PrintCurrentScore          \ Print the number of remaining bullets at
                                    \ the left end of the score bar

 BL      SwitchScreenBank           \ Switch screen banks and clear the newly
                                    \ hidden screen bank to black

 LDR     R0, [R11, #mainLoopCount]  \ Increment the main loop counter
 ADD     R0, R0, #1
 STR     R0, [R11, #mainLoopCount]

 LDR     R0, [R11, #crashLoopCount] \ Decrement the loop counter for the crash
 SUBS    R0, R0, #1                 \ animation above
 STR     R0, [R11, #crashLoopCount]

 BPL     lose1                      \ Loop back to keep running the crash
                                    \ animation until the loop counter runs down

 LDR     R0, [R11, #remainingLives] \ Decrement the number of remaining lives
 SUBS    R0, R0, #1                 \ and set the flags accordingly
 STR     R0, [R11, #remainingLives] \ 

 ADD     R13, R13, #4               \ Increment the stack pointer by one word so
                                    \ we discard the return address from the top
                                    \ of the stack, so we rejoin the main loop
                                    \ without keeping the return address of the
                                    \ subroutine we were in before we jumped
                                    \ here [i.e. MoveAndDrawPlayer or
                                    \ LandOnLaunchpad]

 BNE     PlacePlayerOnLaunchpad     \ If we still have one or more lives left,
                                    \ jump to PlacePlayerOnLaunchpad to play the
                                    \ next life

\ ******************************************************************************
\
\       Name; GameOver
\       Type; Subroutine
\   Category; Main loop
\    Summary; Print a Game Over message and start a new game
\
\ ******************************************************************************

.GameOver

 MOV     R0, #112                   \ Set the VDU driver screen bank to bank 1
 MOV     R1, #1
 SWI     OS_Byte

 MOV     R0, #31                    \ Print the following VDU command;
 SWI     OS_WriteC                  \
 MOV     R0, #1                     \   VDU 31, 1, 16
 SWI     OS_WriteC                  \ 
 MOV     R0, #16                    \ which moves the text cursor to column 1 on
 SWI     OS_WriteC                  \ row 16, halfway down the screen

 SWI     OS_WriteS                  \ Print the Game Over message
 EQUS    "GAME OVER - press a "
 EQUS    "key to start again"
 EQUB    0
 OPT     FN_AlignWithZeroes

 MOV     R0, #112                   \ Set the VDU driver screen bank to bank 2
 MOV     R1, #2
 SWI     OS_Byte

 MOV     R0, #31                    \ Print the following VDU command;
 SWI     OS_WriteC                  \
 MOV     R0, #1                     \   VDU 31, 1, 16
 SWI     OS_WriteC                  \ 
 MOV     R0, #16                    \ which moves the text cursor to column 1 on
 SWI     OS_WriteC                  \ row 16, i.e. the same text coordinates as
                                    \ the text we printed in screen bank 1 above

 SWI     OS_WriteS                  \ Print the Game Over message
 EQUS    "GAME OVER - press a "
 EQUS    "key to start again"
 EQUB    0
 OPT     FN_AlignWithZeroes

 SWI     OS_ReadC                   \ Wait for a key press

 B       StartNewGame               \ Jump to StartNewGame to start a brand new
                                    \ game

\ ******************************************************************************
\
\       Name; graphicsBuffEndAddr2
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The addresses of the tables containing the graphics buffer
\             addresses [same as graphicsBufferEndAddr and graphicsBufferAddr]
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.graphicsBuffEndAddr2

 EQUD    graphicsBuffersEnd

 EQUD    graphicsBuffers

\ ******************************************************************************
\
\       Name; MoveAndDrawParticles [Part 1 of 4]
\       Type; Subroutine
\   Category; Particles
\    Summary; Process particle movement and draw the particles into the graphics
\             buffers, starting with the movement of particles
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Other entry points;
\
\   dpar1                           The start of the main particle-processing
\                                   loop
\
\ ******************************************************************************

.MoveAndDrawParticles

 STMFD   R13!, {R10, R12, R14}      \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 ADD     R10, R11, #particleData    \ Set R10 to the address of the particle
                                    \ data buffer, which we update as we work
                                    \ through the particle data buffer, so this
                                    \ points to the data of the particle being
                                    \ processed

 LDR     R12, graphicsBuffEndAddr2  \ Set R1 to the address of the table that
                                    \ contains the end addresses of the graphics
                                    \ buffers

                                    \ We now work our way through the particle
                                    \ data buffer, processing each particle in
                                    \ turn

.dpar1

 LDMIA   R10, {R0-R7}               \ Fetch the eight words of particle data
                                    \ from the particle data buffer at R10, so
                                    \ this sets the following;
                                    \
                                    \   [R0, R1, R2] = particle coordinate
                                    \
                                    \   [R3, R4, R5] = particle velocity
                                    \
                                    \   R6 = particle lifespan counter
                                    \
                                    \   R7 = particle flags

 CMP     R7, #0                     \ If the last word of the data is zero, then
 LDMEQFD R13!, {R10, R12, PC}       \ this is a null terminator and we have
                                    \ reached the end of the buffer, so retrieve
                                    \ the registers that we stored on the stack
                                    \ and return from the subroutine

.dpar2

 SUBS    R6, R6, #1                 \ Decrement the particle's lifespan counter
                                    \ in R6

 BEQ     DeleteParticleData         \ If R6 is now zero then the particle just
                                    \ expired, so jump to DeleteParticleData to
                                    \ delete this particle from the particle
                                    \ data buffer
                                    \
                                    \ DeleteParticleData then jumps back to
                                    \ dpar1 to move on to the next particle

 ADD     R0, R0, R3                 \ Move the particle by its current velocity
 ADD     R1, R1, R4                 \ by adding the particle's velocity vector
 ADD     R2, R2, R5                 \ in [R3, R4, R5] to the particle's
                                    \ coordinates in [R0, R1, R2]

 TST     R7, #&00100000             \ If bit 20 of the particle flags is set,
 LDRNE   R14, [R11, #gravity]       \ add gravity to the velocity's y-coordinate
 ADDNE   R4, R4, R14                \ in R4, so the particle accelerates towards
                                    \ the ground

 TST     R7, #&00010000             \ If bit 16 of the particle flags is set,
 BLNE    SetParticleColourToFade    \ call SetParticleColourToFade to fade the
                                    \ particle colour from white to red,
                                    \ according to the particle's age, and store
                                    \ the colour in the bottom byte of the
                                    \ particle flags [bits 0 to 7]

 MOV     R8, R0                     \ Set [R8, R9] = [R0, R2] so we can fetch
 MOV     R9, R2                     \ the landscape altitude directly below the
                                    \ particle

 STMFD   R13!, {R0-R3}              \ Store R0 to R3 on the stack so they don't
                                    \ get corrupted by the following call to
                                    \ GetLandscapeAltitude

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [x, z] = [R8, R9]

 MOV     R9, R0                     \ Copy the altitude into R9

 LDMFD   R13!, {R0-R3}              \ Retrieve the values of R0 to R3 that we
                                    \ stored above

 TST     R7, #&00200000             \ If bit 21 of the particle flags is set
 BLNE    ProcessObjectDestruction   \ then the particle can destroy objects that
                                    \ it hits, so call ProcessObjectDestruction
                                    \ to process this

 CMP     R1, R9                     \ If R1 > R9 then the particle is below the
 BLGT    BounceParticle             \ level of the landscape, so bounce the
                                    \ particle off the ground

 STMIA   R10!, {R0-R7}              \ Store the updated particle data in the
                                    \ particle data buffer, updating R10 so it
                                    \ points to the next particle, ready for the
                                    \ next iteration around the loop

\ ******************************************************************************
\
\       Name; MoveAndDrawParticles [Part 2 of 4]
\       Type; Subroutine
\   Category; Particles
\    Summary; Draw particles [including rocks] into the graphics buffers
\  Deep dive; Particles and particle clouds
\
\ ******************************************************************************

 LDR     R8, [R11, #xCamera]        \ Set R0 = R0 - xCamera
 SUB     R0, R0, R8                 \        = x - xCamera
                                    \
                                    \ So R0 contains the x-coordinate of the
                                    \ particle relative to the camera

 LDR     R8, [R11, #zCamera]        \ Set R2 = R2 - zCamera
 SUB     R2, R2, R8                 \
                                    \ So R2 contains the z-coordinate of the
                                    \ particle relative to the camera

 ADD     R2, R2, #LANDSCAPE_Z       \ Move the coordinate back by the landscape
                                    \ offset, so [R0, R2] contains the
                                    \ coordinate of the particle relative to the
                                    \ back-centre point of the landscape

 LDR     R8, [R11, #yCamera]        \ Set R1 = R1 - yCamera
 SUB     R1, R1, R8                 \        = y - yCamera
                                    \
                                    \ So R1 contains the y-coordinate of the
                                    \ particle relative to the camera

 CMP     R2, #LANDSCAPE_Z           \ If the z-coordinate of the particle in R2
 BHS     dpar1                      \ is further into the screen than the
                                    \ landscape offset in LANDSCAPE_Z, then it
                                    \ is beyond the back of the visible
                                    \ landscape, so jump to dpar1 to move on to
                                    \ the next particle in the buffer

 CMP     R2, #LANDSCAPE_Z_FRONT     \ If the z-coordinate of the particle in R2
 BLO     dpar1                      \ is closer to us than LANDSCAPE_Z_FRONT,
                                    \ then it is closer than the front of the
                                    \ visible landscape, so jump to dpar1 to
                                    \ move on to the next particle in the buffer

 MOVS    R14, R0                    \ Set R14 = |R0|
 RSBMI   R14, R0, #0                \         = |particle x-coordinate|

 CMP     R14, #LANDSCAPE_X_HALF     \ If the x-coordinate of the particle in R14
 BHS     dpar1                      \ is more than half the x-axis width of the
                                    \ landscape to the left or right, then it is
                                    \ past the edge of the landscape and is too
                                    \ far to be drawn, so jump to dpar1 to move
                                    \ on to the next particle in the buffer

 TST     R7, #&00020000             \ If bit 17 of the particle flags is clear
 BEQ     dpar4                      \ then this is not a rock, so jump to dpar4
                                    \ to draw the particle and its shadow into
                                    \ the graphics buffers in part 4

                                    \ Otherwise this is a rock, so fall through
                                    \ into part 3 to process collisions

\ ******************************************************************************
\
\       Name; MoveAndDrawParticles [Part 3 of 4]
\       Type; Subroutine
\   Category; Particles
\    Summary; Process rocks by checking for collisions and drawing them as 3D
\             objects
\  Deep dive; Drawing 3D objects
\             Particles and particle clouds
\             Collisions and bullets
\
\ ******************************************************************************

                                    \ If we get here then the particle is a
                                    \ rock, so we need to check whether it has
                                    \ hit the player's ship

 STMFD   R13!, {R0-R2}              \ Store the rock coordinates in [R0, R1, R2]
                                    \ on the stack so we can retrieve them below

 LDR     R14, [R11, #playingGame]   \ If playingGame = 0 then this is the crash
 CMP     R14, #0                    \ animation, so jump to dpar3 to draw the
 BEQ     dpar3                      \ rock, skipping the call to lose a life [as
                                    \ the EQ condition is true, which does not
                                    \ match BLO]

 CMP     R0, #0                     \ Set R0 = |R0|
 RSBMI   R0, R0, #0                 \        = |rock x-coordinate|

 SUBS    R2, R2, #LANDSCAPE_Z_MID    \ Set R2 = |R2 - LANDSCAPE_Z_MID|
 RSBMI   R2, R2, #0                 \        = |rock z-coordinate - 15 tiles|
                                    \
                                    \ Because the player's ship is always at
                                    \ x-coordinate 0 and the z-coordinate at
                                    \ the mid-point of the landscape, this works
                                    \ out the rock's coordinate relative to the
                                    \ ship [for the x- and z-coordinates]
                                    \
                                    \ The rock object is one tile in size along
                                    \ each axis, so we can do a check against
                                    \ the file size to see if we are being hit
                                    \ by the rock

 CMP     R0, #TILE_SIZE             \ If either of R0 or R2 is bigger than one
 CMPLO   R2, #TILE_SIZE             \ tile size, jump to dpar3 as the rock is
 BHS     dpar3                      \ missing us, skipping the call to lose a
                                    \ life [as the HS condition is true, which
                                    \ does not match BLO]

                                    \ The rock is overlapping the player in
                                    \ either the x- or z-coordinate, so now we
                                    \ need to check its altitude

 LDR     R0, [R11, #yCamera]        \ Set R1 = |R1 + yCamera - yPlayer|
 ADD     R1, R1, R0                 \
 LDR     R0, [R11, #yPlayer]        \ So R1 contains the y-coordinate of the
 SUBS    R1, R1, R0                 \ rock relative to the player, which we can
 RSBMI   R1, R1, #0                 \ also test against the tile size to check
                                    \ for a collision

 CMP     R1, #TILE_SIZE             \ If R1 < TILE_SIZE then the LO condition
                                    \ will be true, which will send us to
                                    \ LoseLifeFromParticleLoop below to lose a
                                    \ life, as the rock has hit us

.dpar3

 LDMFD   R13!, {R0-R2}              \ Retrieve the rock coordinates that we
                                    \ stored above into [R0, R1, R2]

 BLO     LoseLifeFromParticleLoop   \ Jump to LoseLifeFromParticleLoop if the LO
                                    \ condition is true, which will only be the
                                    \ case if we reached the CMP just before
                                    \ dpar3 and R1 < TILE_SIZE

 LDR     R14, objectRockAddr        \ Store the address of the rock's object
 STR     R14, [R11, #objectData]    \ blueprint in objectData to pass to the
                                    \ DrawObject routine

 ADD     R3, R11, #rotationMatrix   \ Set R3 to the address of the rock's
                                    \ rotation matrix, to pass to DrawObject

 BL      DrawObject                 \ Draw the rock into the graphics buffers

 B       dpar1                      \ Loop back to dpar1 to process the next
                                    \ particle in the particle data buffer

\ ******************************************************************************
\
\       Name; MoveAndDrawParticles [Part 4 of 4]
\       Type; Subroutine
\   Category; Particles
\    Summary; Draw particles into the graphics buffers
\  Deep dive; Particles and particle clouds
\
\ ******************************************************************************

.dpar4

                                    \ If we get here then we draw the particle
                                    \ and its shadow into the graphics buffers

 STMFD   R13!, {R0-R2, R7}          \ Store R0, R1, R2 and R7 on the stack so
                                    \ they don't get corrupted by the following
                                    \ calls

 LDR     R14, [R11, #yCamera]       \ Set R1 = R9 - yCamera
 SUB     R1, R9, R14                \        = landscape altitude - yCamera
                                    \
                                    \ We set R9 in part 1 to the altitude of the
                                    \ landscape below the particle, so this sets
                                    \ R1 to the y-coordinate of the particle's
                                    \ shadow relative to the camera

 MOV     R8, R2                     \ Set R8 to the z-coordinate of the particle
                                    \ relative to the camera

 BL      ProjectParticleOntoScreen  \ Project the coordinates of the particle's
                                    \ shadow in [R0, R1, R2] onto the screen,
                                    \ returning the results in [R0, R1]
                                    \
                                    \ This also clears the C flag if the
                                    \ particle coordinates are on-screen

 BLCC    DrawParticleShadowToBuffer \ If the projected coordinates fit onto the
                                    \ screen, draw the particle's projected
                                    \ shadow into the graphics buffers

 LDMFD   R13!, {R0-R2, R7}          \ Retrieve the values of R0, R1, R2 and R7
                                    \ that we stored above

 MOV     R8, R2                     \ Set R8 to the z-coordinate of the particle
                                    \ relative to the camera

 BL      ProjectParticleOntoScreen  \ Project the coordinates of the particle in
                                    \ [R0, R1, R2] onto the screen, returning
                                    \ the results in [R0, R1]
                                    \
                                    \ This also clears the C flag if the
                                    \ particle coordinates are on-screen

 BLCC    DrawParticleToBuffer       \ If the projected coordinates fit onto the
                                    \ screen, draw the projected particle into
                                    \ the graphics buffers

 LDMIA   R10, {R0-R7}               \ Fetch the eight words of particle data
                                    \ for the next particle from the particle
                                    \ data buffer at R10

 CMP     R7, #0                     \ If the last word of the data is not zero
 BNE     dpar2                      \ then this is a valid particle rather than
                                    \ a null terminator, so loop back to dpar2
                                    \ to process the next particle

 LDMFD   R13!, {R10, R12, PC}       \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; SetParticleColourToFade
\       Type; Subroutine
\   Category; Particles
\    Summary; Set the flags for a particle whose colour fades from white to red
\             over time, to give a white-hot explosion particle that cools down
\  Deep dive; Particles and particle clouds
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R7                              Particle flags for a fading colour particle
\
\                                     * Bits 0-7   = particle colour
\                                     * Bit 16 set = colour fades white to red
\                                     * Bit 18 set = splash on impact with sea
\                                     * Bit 19 set = bounce on ground
\                                     * Bit 20 set = apply gravity to particle
\
\ ******************************************************************************

.SetParticleColourToFade

 STMFD   R13!, {R0-R2, R6}          \ Store R0, R1, R2 and R6 on the stack so
                                    \ they don't get corrupted by the following

                                    \ We start by setting up a three-channel
                                    \ colour, with the red channel in R0, the
                                    \ green channel in R1 and the blue channel
                                    \ in R2

 MOV     R0, #15                    \ Set the red channel to 15

 CMP     R6, #8                     \ If R6 >= 8, set;
 MOVHS   R1, #15                    \
 MOVLO   R1, R6, LSL #1             \   Green channel in R1 = 15
 SUBHS   R2, R6, #8                 \   Blue channel in R2 = [R6 - 8] * 2
 MOVHS   R2, R2, LSL #1             \ 
 MOVLO   R2, #0                     \ otherwise set;
                                    \
                                    \   Green channel in R1 = R6 * 2
                                    \   Blue channel in R2 = 0
                                    \
                                    \ So particles start out white [R6 > 8]
                                    \ with a fading level of blue, until the
                                    \ blue disappears entirely [R6 = 8] and
                                    \ then the green fades away [R6 < 8] to
                                    \ leave a pure red particle [R6 = 0]
                                    \
                                    \ So this is a fading particle from white
                                    \ to red, so this looks like a burning
                                    \ particle from an explosion that cools
                                    \ over time

                                    \ We now build a VIDC colour number in R7
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R7 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R7, R1, R2                 \ Set R7 to the bottom three bits of;
 AND     R7, R7, #%00000011         \
 ORR     R7, R7, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R7, R7, #7                 \ 
                                    \ So this sets bits 0, 1 and 2 of R7 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R7, R7, #%00010000         \ set bit 4 of R7

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R7, R7, R1, LSL #3         \ And stick them into bits 5-6 of R7

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%00001000         \ set bit 3 of R7

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%10000000         \ set bit 7 of R7

 ORR     R7, R7, #&001D0000         \ Set bits 16, 18, 19 and 20 of the particle
                                    \ flags, so that's;
                                    \
                                    \   * Bit 16 set = colour fades white to red
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle

 LDMFD   R13!, {R0-R2, R6}          \ Retrieve the registers that we stored on
                                    \ the stack above

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; BounceParticle
\       Type; Subroutine
\   Category; Particles
\    Summary; Bounce a particle off the ground
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates
\
\   [R3, R4, R5]                    Particle velocity
\
\   R7                              Particle flags
\
\   R9                              The altitude of the landscape directly below
\                                   the particle
\
\ ******************************************************************************

.BounceParticle

 MOV     R1, R9                     \ Set R1 to R9, so [R0, R1, R2] now contains
                                    \ the coordinates of the point on the
                                    \ landscape directly below the particle

 CMP     R9, #SEA_LEVEL             \ If the particle is above the sea, jump to
 BEQ     SplashParticleIntoSea      \ SplashParticleIntoSea to splash the
                                    \ particle into the sea, returning from the
                                    \ subroutine using a tail call

 TST     R7, #&00080000             \ If bit 19 of the particle flags is clear,
 BEQ     DeleteParticleData         \ jump to DeleteParticleData to delete the
                                    \ particle without bouncing or exploding

 TST     R7, #&01000000             \ If bit 24 of the particle flags is set,
 BNE     AddSmallExplosionToBuffer  \ jump to AddSmallExplosionToBuffer to
                                    \ destroy the particle in a small explosion

 MOV     R3, R3, ASR #1             \ Otherwise we bounce the particle off the
 MOV     R4, R4, ASR #1             \ ground by setting the particle's velocity
 MOV     R5, R5, ASR #1             \ vector to half its previous speed, and in
 RSB     R4, R4, #0                 \ the opposite direction in the y-axis

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; ProcessObjectDestruction
\       Type; Subroutine
\   Category; Particles
\    Summary; If this particle has hit an object, destroy the object and the
\             particle in an explosion, scoring points if it's a bullet
\  Deep dive; Particles and particle clouds
\             Collisions and bullets
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    The particle's coordinates
\
\   R7                              Particle flags
\
\   R9                              The altitude of the landscape directly below
\                                   the particle
\
\ ******************************************************************************

.ProcessObjectDestruction

 SUB     R8, R9, R1                 \ Set R8 = altitude - y-coordinate
                                    \
                                    \ So R8 contains the vertical distance
                                    \ between the particle and the ground

 CMP     R8, #SAFE_HEIGHT           \ If R8 is higher than the minimum safe
 MOVHS   PC, R14                    \ height for avoiding objects on the ground
                                    \ in SAFE_HEIGHT [which is set to 1.5 tile
                                    \ sizes] then return from the subroutine as
                                    \ the particle is too high off the ground to
                                    \ be hitting any objects

 STMFD   R13!, {R2}                 \ Store R2 on the stack so we can retrieve
                                    \ it below

 STR     R14, [R11, #objectType]    \ Store the return address in objectType
                                    \ [the choice of variable is not important,
                                    \ we are just using it as temporary storage
                                    \ here]

 ADD     R14, R11, #objectMap       \ Set R14 to the address of the object map

 AND     R2, R2, #&FF000000         \ Set the bottom three bytes of R2 to zero,
                                    \ leaving just the top byte, so we can use
                                    \ it in the following

 ADD     R14, R14, R0, LSR #24      \ Set R14 = R14 + [R0 >> 24] + [R2 >> 16]
 ADD     R14, R14, R2, LSR #16      \
                                    \ R0 is shifted into the bottom byte of R14,
                                    \ so that's the x-coordinate, and R2 is
                                    \ shifted into the second byte of R14, so
                                    \ that's the z-coordinate, so R14 points to
                                    \ the object map entry for coordinate
                                    \ [R0, R2]

 LDMFD   R13!, {R2}                 \ Retrieve the value of R2 that we stored
                                    \ above, so it contains the particle
                                    \ y-coordinate once again

 LDRB    R8, [R14]                  \ Set R8 to the byte in the object map at
                                    \ the coordinate [R0, R2]

 CMP     R8, #&FF                   \ If the object map entry is &FF, then there
 LDREQ   PC, [R11, #objectType]     \ is no object on the map at the particle's
                                    \ location, so we haven't hit anything, so
                                    \ return from the subroutine by fetching the
                                    \ return address that we stored above

 CMP     R8, #12                    \ If the object being hit by the particle is
 BHS     AddSmallExplosionToBuffer  \ 12 or greater, then the object has already
                                    \ been destroyed, so draw a small explosion
                                    \ by calling AddSmallExplosionToBuffer, and
                                    \ return from the subroutine using a tail
                                    \ call

 ADD     R8, R8, #12                \ Otherwise the particle has just hit an
 STRB    R8, [R14]                  \ undestroyed object, so add 12 to the
                                    \ object's type in the object map to denote
                                    \ that it has been destroyed

 TST     R7, #&00020000             \ If bit 17 the particle flags is clear then
 LDREQ   R8, [R11, #currentScore]   \ the particle doing the hitting is not a
 ADDEQ   R8, R8, #20                \ rock, so it must be a bullet, so increment
 STREQ   R8, [R11, #currentScore]   \ the current score by 20

 MOV     R8, #20                    \ Set R8 = 20 and call AddExplosionToBuffer
 BL      AddExplosionToBuffer       \ to draw a medium-sized explosion where the
                                    \ particle hit

 B       DeleteParticleData         \ Jump to DeleteParticleData to delete the
                                    \ particle, as it gets destroyed in the
                                    \ explosion, and return from the subroutine
                                    \ using a tail call

\ ******************************************************************************
\
\       Name; AddSmallExplosionToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a small explosion to the particle data buffer
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Explosion coordinates
\
\   R7                              Particle flags
\
\ ******************************************************************************

.AddSmallExplosionToBuffer

 MOV     R8, #3                     \ Set R8 = 3 and call AddExplosionToBuffer
 BL      AddExplosionToBuffer       \ to draw a small explosion at the given
                                    \ coordinates

 B       DeleteParticleData         \ Jump to DeleteParticleData to delete the
                                    \ particle, as it gets destroyed in the
                                    \ explosion, and return from the subroutine
                                    \ using a tail call

\ ******************************************************************************
\
\       Name; SplashParticleIntoSea
\       Type; Subroutine
\   Category; Particles
\    Summary; Splash a particle into the sea, creating a spray particle
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates [on the sea's surface]
\
\   R7                              Particle flags
\
\ ******************************************************************************

.SplashParticleIntoSea

 TST     R7, #&00040000             \ If bit 18 of the particle flags is clear,
 BEQ     DeleteParticleData         \ jump to DeleteParticleData to delete the
                                    \ particle without splashing

 TST     R7, #&00800000             \ If bit 23 of the particle flags is set,
 MOVNE   R8, #65                    \ set the number of spray particles in R8
 MOVEQ   R8, #4                     \ to 65, otherwise set it to 4

 SUB     R1, R1, #SPLASH_HEIGHT     \ Set R1 to a point just above the sea's
                                    \ surface [R1 is on the surface, so this
                                    \ moves it to SPLASH_HEIGHT above the waves,
                                    \ or 1/16 of a tile size]

.psea1

 BL      AddSprayParticleToBuffer   \ Add a spray particle to the particle data
                                    \ buffer

 SUBS    R8, R8, #1                 \ Decrement the particle counter in R8

 BNE     psea1                      \ Loop back until we have added all the
                                    \ spray particles

                                    \ Fall through into DeleteParticleData to
                                    \ delete the particle and return from the
                                    \ subroutine, as the particle has now
                                    \ crashed into the sea

\ ******************************************************************************
\
\       Name; DeleteParticleData
\       Type; Subroutine
\   Category; Particles
\    Summary; Delete a particle from the particle data buffer and move the last
\             particle's data down to take its place
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R10                             The address in the particle data buffer of
\                                   the particle to delete
\
\ ******************************************************************************

.DeleteParticleData

 LDR     R8, [R11, #particleEnd]    \ Decrease the address of the end of the
 SUB     R8, R8, #8*4               \ particle data buffer by eight words, so it
 STR     R8, [R11, #particleEnd]    \ points to the last particle's data in the
                                    \ buffer

 LDMIA   R8, {R0-R7}                \ Copy the eight bytes of particle data from
 STMIA   R10, {R0-R7}               \ the end of the buffer to the address in
                                    \ R10

 MOV     R7, #0                     \ Zero the last word of the last particle's
 STR     R7, [R8, #7*4]             \ data, so the last particle in the buffer
                                    \ now acts as a null terminator

 LDR     R8, [R11, #particleCount]  \ Decrement the particle counter to reflect
 SUB     R8, R8, #1                 \ the new number of particles on-screen
 STR     R8, [R11, #particleCount]

 B       dpar1                      \ Jump to the start of the main loop in
                                    \ MoveAndDrawParticles to process the next
                                    \ particle in the particle data buffer

\ ******************************************************************************
\
\       Name; AddBulletParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a bullet particle to the particle data buffer
\  Deep dive; Particles and particle clouds
\             Collisions and bullets
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   [R3, R4, R5]                    Particle velocity
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags
\
\   R8                              Magnitude of the random element that's added
\                                   to the velocity, with a larger figure giving
\                                   a smaller random element; the actual range
\                                   is +/- 2^[32 - R8]
\
\   R9                              Magnitude of the random element that's added
\                                   to the particle lifespan, with a larger
\                                   figure giving a smaller random element; the
\                                   actual range is 0 to 2^[32 - R9]
\
\ ******************************************************************************

.AddBulletParticleToBuffer

 STMFD   R13!, {R8, R14}            \ Store R8 and the return address on the
                                    \ stack, so the call to StoreParticleData
                                    \ can return properly

 B       StoreParticleData          \ Jump to StoreParticleData to store the
                                    \ particle in the particle data buffer,
                                    \ returning from the subroutine using a tail
                                    \ call

\ ******************************************************************************
\
\       Name; AddExhaustParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add one of the moving particles in the exhaust plume to the
\             particle data buffer
\  Deep dive; Particles and particle clouds
\             Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   [R3, R4, R5]                    Particle velocity
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags
\
\   R8                              Magnitude of the random element that's added
\                                   to the velocity, with a larger figure giving
\                                   a smaller random element; the actual range
\                                   is +/- 2^[32 - R8]
\
\   R9                              Magnitude of the random element that's added
\                                   to the particle lifespan, with a larger
\                                   figure giving a smaller random element; the
\                                   actual range is 0 to 2^[32 - R9]
\
\ ******************************************************************************

.AddExhaustParticleToBuffer

 STMFD   R13!, {R8, R14}            \ Store R8 and the return address on the
                                    \ stack, so the following call to the
                                    \ particle adding routine below can return
                                    \ properly

 B       AddMovingParticleToBuffer  \ Add a moving particle to the particle data
                                    \ buffer that starts off moving along the
                                    \ exhaust vector, and with a random element
                                    \ being added to its velocity and lifespan
                                    \ counter, returning from the subroutine
                                    \ using a tail call

\ ******************************************************************************
\
\       Name; AddStaticParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a particle to the particle data buffer that starts off static,
\             adding a random element to its velocity and lifespan counter
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags;
\
\                                     * Bits 0-7   = particle colour
\                                     * Bit 16 set = colour fades white to red
\                                     * Bit 17 set = particle is a rock
\                                     * Bit 18 set = splash on impact with sea
\                                     * Bit 19 set = bounce on hitting ground
\                                     * Bit 20 set = apply gravity to particle
\                                     * Bit 21 set = can destroy objects
\                                     * Bit 23 set = splash size [big when set]
\                                     * Bit 24 set = explode on hitting ground
\
\   R8                              Magnitude of the random element that's added
\                                   to the velocity, with a larger figure giving
\                                   a smaller random element; the actual range
\                                   is +/- 2^[32 - R8]
\
\   R9                              Magnitude of the random element that's added
\                                   to the particle lifespan, with a larger
\                                   figure giving a smaller random element; the
\                                   actual range is 0 to 2^[32 - R9]
\
\   Stack                           Contains a value to restore into R8 at the
\                                   end, and the return address
\
\ ******************************************************************************

.AddStaticParticleToBuffer

 MOV     R4, #0                     \ Set R4 = 0

                                    \ Fall into AddRisingParticleToBuffer so we
                                    \ pass a velocity of [0, 0, 0] to the 
                                    \ AddMovingParticleToBuffer routine

\ ******************************************************************************
\
\       Name; AddRisingParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a particle to the particle data buffer that initially drifts
\             up, with a random element to its velocity and lifespan counter
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   R4                              Particle velocity in the y-axis [up-down]
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags;
\
\                                     * Bits 0-7   = particle colour
\                                     * Bit 16 set = colour fades white to red
\                                     * Bit 17 set = particle is a rock
\                                     * Bit 18 set = splash on impact with sea
\                                     * Bit 19 set = bounce on hitting ground
\                                     * Bit 20 set = apply gravity to particle
\                                     * Bit 21 set = can destroy objects
\                                     * Bit 23 set = splash size [big when set]
\                                     * Bit 24 set = explode on hitting ground
\
\   R8                              Magnitude of the random element that's added
\                                   to the velocity, with a larger figure giving
\                                   a smaller random element; the actual range
\                                   is +/- 2^[32 - R8]
\
\   R9                              Magnitude of the random element that's added
\                                   to the particle lifespan, with a larger
\                                   figure giving a smaller random element; the
\                                   actual range is 0 to 2^[32 - R9]
\
\   Stack                           Contains a value to restore into R8 at the
\                                   end, and the return address
\
\ ******************************************************************************

.AddRisingParticleToBuffer

 MOV     R3, #0                     \ Set R3 = 0

 MOV     R5, #0                     \ Set R5 = 0

                                    \ Fall into AddMovingParticleToBuffer with
                                    \ a velocity of [0, R4, 0]

\ ******************************************************************************
\
\       Name; AddMovingParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a moving particle to the particle data buffer, adding a random
\             element to its velocity and lifespan counter
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   [R3, R4, R5]                    Particle velocity
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags;
\
\                                     * Bits 0-7   = particle colour
\                                     * Bit 16 set = colour fades white to red
\                                     * Bit 17 set = particle is a rock
\                                     * Bit 18 set = splash on impact with sea
\                                     * Bit 19 set = bounce on hitting ground
\                                     * Bit 20 set = apply gravity to particle
\                                     * Bit 21 set = can destroy objects
\                                     * Bit 23 set = splash size [big when set]
\                                     * Bit 24 set = explode on hitting ground
\
\   R8                              Magnitude of the random element that's added
\                                   to the velocity, with a larger figure giving
\                                   a smaller random element; the actual range
\                                   is +/- 2^[32 - R8]
\
\   R9                              Magnitude of the random element that's added
\                                   to the particle lifespan, with a larger
\                                   figure giving a smaller random element; the
\                                   actual range is 0 to 2^[32 - R9]
\
\   Stack                           Contains a value to restore into R8 at the
\                                   end, and the return address
\
\ ******************************************************************************

.AddMovingParticleToBuffer

 STMFD   R13!, {R0-R1}              \ Store R0 and R1 on the stack so we can
                                    \ restore them after the following

                                    \ We now add a random signed element to the
                                    \ particle velocity in [R3, R4, R5], with
                                    \ the scale of the random element determined
                                    \ by R8 [so a larger R8 adds a smaller
                                    \ random element to the velocity]

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 ADD     R3, R3, R0, ASR R8         \ Set R3 = R3 + R0 >> R8
                                    \
                                    \ We keep the sign in R0, so this can either
                                    \ increase or decrease R3

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 ADD     R4, R4, R0, ASR R8         \ Set R4 = R4 + R0 >> R8
                                    \
                                    \ We keep the sign in R0, so this can either
                                    \ increase or decrease R4

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 ADD     R5, R5, R0, ASR R8         \ Set R5 = R5 + R0 >> R8
                                    \
                                    \ We keep the sign in R0, so this can either
                                    \ increase or decrease R5

                                    \ We now add a random positive element to
                                    \ the particle's lifespan counter in R6,
                                    \ with the scale determined by R9 [so a
                                    \ larger R9 adds a smaller random element
                                    \ to the particle's lifespan]

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 ADD     R6, R6, R0, LSR R9         \ Set R6 = R6 + R0 >> R9
                                    \
                                    \ We do not keep the sign in R0, so the
                                    \ addition is always positive

 LDMFD   R13!, {R0-R1}              \ Retrieve the values of R0 and R1 that we
                                    \ stored above

\ ******************************************************************************
\
\       Name; StoreParticleData
\       Type; Subroutine
\   Category; Particles
\    Summary; Store the data for a new particle in the particle data buffer
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\   [R3, R4, R5]                    Particle velocity
\
\   R6                              Particle lifespan counter [i.e. how many
\                                   iterations around the main loop before the
\                                   particle expires]
\
\   R7                              Particle flags
\
\   Stack                           Contains a value to restore into R8 at the
\                                   end, and the return address
\
\ ******************************************************************************

.StoreParticleData

 LDR     R8, [R11, #particleCount]  \ Set R8 to the number of particles that are
                                    \ already on-screen

 CMP     R8, #MAX_PARTICLES         \ If R8 >= MAX_PARTICLES then we already
 LDMHSFD R13!, {R8, PC}             \ have the maximum number of particles
                                    \ on-screen, so restore the value of R8 from
                                    \ the stack and return from the subroutine

 ADD     R8, R8, #1                 \ Increment the particle counter to record
 STR     R8, [R11, #particleCount]  \ that we have added a new particle

 LDR     R8, [R11, #particleEnd]    \ Set R8 to the address of the end of the
                                    \ particle data buffer, which is where we
                                    \ can store the new batch of data

 STMIA   R8!, {R0-R7}               \ Store the eight words of particle data in
                                    \ the buffer, updating R8 as we go

 STR     R8, [R11, #particleEnd]    \ Update particleEnd with the new address of
                                    \ the end of the buffer

 MOV     R9, #0                     \ Zero the last word of the next particle's
 STR     R9, [R8, #7*4]             \ data after the one we just wrote, so this
                                    \ acts as a null terminator

 LDMFD   R13!, {R8, PC}             \ Restore the value of R8 from the stack and
                                    \ return from the subroutine

\ ******************************************************************************
\
\       Name; InitialiseParticleData
\       Type; Subroutine
\   Category; Start and end
\    Summary; Initialise the particle data buffer and associated variables
\  Deep dive; Particles and particle clouds
\
\ ******************************************************************************

.InitialiseParticleData

 ADD     R0, R11, #particleData     \ Set particleEnd to the address of the
 STR     R0, [R11, #particleEnd]    \ particle data buffer, so the buffer starts
                                    \ off empty

 MOV     R1, #0                     \ Zero the last word of the first particle's
 STR     R1, [R0, #7*4]             \ data, so it acts as a null terminator

 STR     R1, [R11, #particleCount]  \ Set particleCount = 0 to indicate that
                                    \ there are no particles on-screen

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; AddSmokeParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a smoke particle to the particle data buffer
\  Deep dive; Particles and particle clouds
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinate
\
\ ******************************************************************************

.AddSmokeParticleToBuffer

 STMFD   R13!, {R0-R2, R8, R14}     \ Store the particle coordinates on the
                                    \ stack so we can retrieve them below, and
                                    \ also store R8 and the return address so
                                    \ we can pass them to the particle adding
                                    \ routine below

                                    \ We start by setting up a three-channel
                                    \ colour, with the red channel in R0, the
                                    \ green channel in R1 and the blue channel
                                    \ in R2

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 AND     R0, R0, #7                 \ Reduce R0 to a random number in the range
 ADD     R0, R0, #3                 \ 3 to 10

 MOV     R1, R0                     \ Set R1 and R2 to the same number, so if
 MOV     R2, R0                     \ R0, R1 and R2 represent the three colour
                                    \ channels, we have a grey colour of a
                                    \ random intensity

                                    \ We now build a VIDC colour number in R7
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R7 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R7, R1, R2                 \ Set R7 to the bottom three bits of;
 AND     R7, R7, #%00000011         \
 ORR     R7, R7, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R7, R7, #7                 \ 
                                    \ So this sets bits 0, 1 and 2 of R7 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R7, R7, #%00010000         \ set bit 4 of R7

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R7, R7, R1, LSL #3         \ And stick them into bits 5-6 of R7

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%00001000         \ set bit 3 of R7

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%10000000         \ set bit 7 of R7

 ORR     R7, R7, #&00080000         \ Set bit 19 of the particle flags, so that
                                    \ the particle bounces on the ground should
                                    \ it ever reach it

 MVN     R4, #SMOKE_RISING_SPEED    \ Set R4 to the speed of the smoke rising
                                    \ to pass as the y-axis velocity in the
                                    \ particle adding routine below, so the
                                    \ particle drifts slowly up and away from
                                    \ the ground

 LDMFD   R13!, {R0-R2}              \ Retrieve the particle coordinates that we
                                    \ stored above into [R0, R1, R2]

 MOV     R9, #25                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 25],
                                    \ i.e. 0 to 128

 MOV     R8, #13                    \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 13],
                                    \ i.e. -&80000 to +&80000

 MOV     R6, #15                    \ Set the particle's lifespan counter to 15
                                    \ iterations of the main loop

 B       AddRisingParticleToBuffer  \ Add a particle to the particle data buffer
                                    \ that initially drifts upwards, and with a
                                    \ random element being added to its velocity
                                    \ and lifespan counter, returning from the
                                    \ subroutine using a tail call

\ ******************************************************************************
\
\       Name; AddDebrisParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a debris particle to the particle data buffer, which is a
\             purple-brownish-green particle that bounces out of an explosion
\  Deep dive; Particles and particle clouds
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates
\
\ ******************************************************************************

.AddDebrisParticleToBuffer

 STMFD   R13!, {R0-R2, R8, R14}     \ Store the particle coordinates on the
                                    \ stack so we can retrieve them below, and
                                    \ also store R8 and the return address so
                                    \ we can pass them to the particle adding
                                    \ routine below

                                    \ We start by setting up a three-channel
                                    \ colour, with the red channel in R0, the
                                    \ green channel in R1 and the blue channel
                                    \ in R2

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 MOV     R1, R1, LSR #29            \ Set R1 to a random number in the range 0
                                    \ to 7

 MOV     R2, R0, LSR #30            \ Set R2 to a random number in the range 0
                                    \ to 3

 AND     R0, R0, #7                 \ Set R0 to a random number in the range 0
                                    \ to 7

 ADD     R0, R0, #4                 \ Set R0 to a random number in the range 4
                                    \ to 11, to use in the red channel

 ADD     R1, R1, #2                 \ Set R1 to a random number in the range 2
                                    \ to 2, to use for the green channel

 ADD     R2, R2, #4                 \ Set R2 to a random number in the range 4
                                    \ to 7, to use in the blue channel

                                    \ This sets the channels to a randomly
                                    \ purple-brownish-green colour

                                    \ We now build a VIDC colour number in R7
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R7 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R7, R1, R2                 \ Set R7 to the bottom three bits of;
 AND     R7, R7, #%00000011         \
 ORR     R7, R7, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R7, R7, #7                 \ 
                                    \ So this sets bits 0, 1 and 2 of R7 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R7, R7, #%00010000         \ set bit 4 of R7

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R7, R7, R1, LSL #3         \ And stick them into bits 5-6 of R7

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%00001000         \ set bit 3 of R7

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%10000000         \ set bit 7 of R7

 ORR     R7, R7, #&001C0000         \ Set bits 18, 19 and 20 of the particle
                                    \ flags, so that's;
                                    \
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle

 LDMFD   R13!, {R0-R2}              \ Retrieve the particle coordinates that we
                                    \ stored above into [R0, R1, R2]

 MOV     R9, #26                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 26],
                                    \ i.e. 0 to 64

 MOV     R8, #10                    \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 10],
                                    \ i.e. -&400000 to +&400000

 MOV     R6, #15                    \ Set the particle's lifespan counter to 15
                                    \ iterations of the main loop

 B       AddStaticParticleToBuffer  \ Add a particle to the particle data buffer
                                    \ that starts off static, and with a random
                                    \ element being added to its velocity and
                                    \ lifespan counter, returning from the
                                    \ subroutine using a tail call

\ ******************************************************************************
\
\       Name; DropARockFromTheSky
\       Type; Subroutine
\   Category; Particles
\    Summary; Drop a rock from the specified coordinates by spawning it as a
\             particle, albeit a very big particle with an associated 3D object
\  Deep dive; Particles and particle clouds
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    The 3D coordinate [x, y, z] where we spawn
\                                   the rock
\
\ ******************************************************************************

.DropARockFromTheSky

 STMFD   R13!, {R0-R2, R8, R14}     \ Store the particle coordinates on the
                                    \ stack so we can retrieve them below, and
                                    \ also store R8 and the return address so
                                    \ we can pass them to the particle adding
                                    \ routine below

                                    \ We start by setting up a three-channel
                                    \ colour, with the red channel in R0, the
                                    \ green channel in R1 and the blue channel
                                    \ in R2

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 MOV     R1, R1, LSR #29            \ Set R1 to a random number in the range 0
                                    \ to 7

 MOV     R2, R0, LSR #30            \ Set R2 to a random number in the range 0
                                    \ to 3

 AND     R0, R0, #7                 \ Set R0 to a random number in the range 0
                                    \ to 7

 ADD     R0, R0, #4                 \ Set R0 to a random number in the range 4
                                    \ to 11, to use in the red channel

 ADD     R1, R1, #2                 \ Set R1 to a random number in the range 2
                                    \ to 2, to use for the green channel

 ADD     R2, R2, #4                 \ Set R2 to a random number in the range 4
                                    \ to 7, to use in the blue channel

                                    \ This sets the channels to a randomly
                                    \ purple-brownish-green colour

                                    \ We now build a VIDC colour number in R7
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R7 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R7, R1, R2                 \ Set R7 to the bottom three bits of;
 AND     R7, R7, #%00000011         \
 ORR     R7, R7, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R7, R7, #7                 \ 
                                    \ So this sets bits 0, 1 and 2 of R7 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R7, R7, #%00010000         \ set bit 4 of R7

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R7, R7, R1, LSL #3         \ And stick them into bits 5-6 of R7

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%00001000         \ set bit 3 of R7

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%10000000         \ set bit 7 of R7


 ORR     R7, R7, #&00FE0000         \ Set bits 17 to 23 of the particle flags,
                                    \ so that's;
                                    \
                                    \   * Bit 17 set = particle is a rock
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle
                                    \   * Bit 21 set = can destroy objects
                                    \   * Bit 23 set = splash size is big
                                    \   * Bit 24 set = explode on hitting ground

 LDMFD   R13!, {R0-R2}              \ Retrieve the particle coordinates that we
                                    \ stored above into [R0, R1, R2]

 MOV     R9, #27                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 27],
                                    \ i.e. 0 to 32

 MOV     R8, #10                    \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 10],
                                    \ i.e. -&400000 to +&400000

 MOV     R6, #170                   \ Set the particle's lifespan counter to
                                    \ 170 iterations of the main loop, so it
                                    \ won't disappear before it hits the ground

 B       AddStaticParticleToBuffer  \ Add a rock to the particle data buffer
                                    \ that starts off static, and with a random
                                    \ element being added to its velocity and
                                    \ lifespan counter, returning from the
                                    \ subroutine using a tail call

\ ******************************************************************************
\
\       Name; AddSparkParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a spark particle to the particle data buffer that fades from
\             white-hot to red over time
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates
\  Deep dive; Particles and particle clouds
\
\ ******************************************************************************

.AddSparkParticleToBuffer

 STMFD   R13!, {R8, R14}            \ Store R8 and the return address on the
                                    \ stack, so the call to the particle adding
                                    \ routine below can return properly

 MOV     R7, #&001D0000             \ Set bits 16, 18, 19 and 20 of the particle
                                    \ flags, so that's;
                                    \
                                    \   * Bit 16 set = colour fades white to red
                                    \   * Bit 18 set = splash on impact with sea
                                    \   * Bit 19 set = bounce on ground
                                    \   * Bit 20 set = apply gravity to particle

 MOV     R9, #29                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 29],
                                    \ i.e. 0 to 8

 MOV     R8, #8                     \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 8],
                                    \ i.e. -&1000000 to +&1000000

 MOV     R6, #8                     \ Set the particle's lifespan counter to 8
                                    \ iterations of the main loop

 B       AddStaticParticleToBuffer  \ Add a particle to the particle data buffer
                                    \ that starts off static, and with a random
                                    \ element being added to its velocity and
                                    \ lifespan counter, returning from the
                                    \ subroutine using a tail call

\ ******************************************************************************
\
\       Name; AddSprayParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Add a spray particle to the particle data buffer
\  Deep dive; Particles and particle clouds
\             Screen memory in the Archimedes
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates
\
\ ******************************************************************************

.AddSprayParticleToBuffer

 STMFD   R13!, {R0-R2, R8, R14}     \ Store the particle coordinates on the
                                    \ stack so we can retrieve them below, and
                                    \ also store R8 and the return address so
                                    \ we can pass them to the particle adding
                                    \ routine below

                                    \ We start by setting up a three-channel
                                    \ colour, with the red channel in R0, the
                                    \ green channel in R1 and the blue channel
                                    \ in R2

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 AND     R2, R0, #3                 \ Set R2 to a random number in the range 12
 ADD     R2, R2, #12                \ to 15, to use as the blue channel

 AND     R0, R0, #4                 \ Set R0 to a random number that's either 8
 ADD     R0, R0, #8                 \ or 12

 MOV     R1, R0                     \ Set R1 to the same as R0, so the red and
                                    \ green channels are the same, giving an
                                    \ overall colour that combines the high blue
                                    \ channel level with one of two greyscale
                                    \ levels, giving one of four shades of blue

                                    \ We now build a VIDC colour number in R7
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R7 from
                                    \ the red, green and blue values in R0, R1
                                    \ and R2

 ORR     R7, R1, R2                 \ Set R7 to the bottom three bits of;
 AND     R7, R7, #%00000011         \
 ORR     R7, R7, R0                 \   [the bottom two bits of R1 OR R2] OR R0
 AND     R7, R7, #7                 \ 
                                    \ So this sets bits 0, 1 and 2 of R7 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R7, R7, #%00010000         \ set bit 4 of R7

 AND     R1, R1, #%00001100         \ Clear all bits of the green channel in R1
                                    \ except bits 2-3

 ORR     R7, R7, R1, LSL #3         \ And stick them into bits 5-6 of R7

 TST     R2, #%00000100             \ If bit 2 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%00001000         \ set bit 3 of R7

 TST     R2, #%00001000             \ If bit 3 of the blue channel in R2 is set,
 ORRNE   R7, R7, #%10000000         \ set bit 7 of R7


 ORR     R7, R7, #&00100000         \ Set bit 20 of the particle flags, so that
                                    \ gravity is applied to the particle

 LDMFD   R13!, {R0-R2}              \ Retrieve the particle coordinates that we
                                    \ stored above into [R0, R1, R2]

 MOV     R9, #26                    \ Set the random element of the particle's
                                    \ lifespan to the range 0 to 2^[32 - 26],
                                    \ i.e. 0 to 64

 MOV     R8, #10                    \ Set the random element of the particle's
                                    \ velocity to the range +/- 2^[32 - 10],
                                    \ i.e. -&400000 to +&400000

 MOV     R6, #20                    \ Set the particle's lifespan counter to 20
                                    \ iterations of the main loop

 B       AddStaticParticleToBuffer  \ Add a particle to the particle data buffer
                                    \ that starts off static, and with a random
                                    \ element being added to its velocity and
                                    \ lifespan counter, returning from the
                                    \ subroutine using a tail call

\ ******************************************************************************
\
\       Name; AddExplosionToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Create a big explosion of particles and add it to the particle
\             data buffer
\  Deep dive; Particles and particle clouds
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R8                              The number of particle clusters in the
\                                   explosion [there are four particles per
\                                   cluster]
\
\ ******************************************************************************

.AddExplosionToBuffer

 STMFD   R13!, {R3-R9, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

.expl1

                                    \ Each cluster is made up of four particles,
                                    \ so we now add them to the particle data
                                    \ buffer

 BL      AddSparkParticleToBuffer   \ Add a spark particle to the particle data
                                    \ buffer [one that fades from white to red]

 BL      AddDebrisParticleToBuffer  \ Add a spark particle to the particle data
                                    \ buffer [a purple-brownish-green particle
                                    \ that flies out and bounces on the ground]

 BL      AddSmokeParticleToBuffer   \ Add a smoke particle to the particle data
                                    \ buffer [a grey particle that slowly rises]

 BL      AddSparkParticleToBuffer   \ Add another spark particle to the particle
                                    \ data buffer  [one that fades from white to
                                    \ red]

 SUBS    R8, R8, #1                 \ Decrement the particle cluster counter in
                                    \ R8

 BPL     expl1                      \ Loop back until we have drawn all R8
                                    \ particle clusters in the explosion

 LDMFD   R13!, {R3-R9, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; AddShipExplosionToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; An unused routine that adds a 50-cluster explosion cloud to the
\             particle data buffer, just front of the player's ship
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.AddShipExplosionToBuffer

 STMFD   R13!, {R14}                \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOV     R8, #50                    \ Set R8 = 50 so the explosion contains 50
                                    \ clusters of four particles

                                    \ Now we add an explosion to the particle
                                    \ buffer at coordinates [R0, R1, R2], which
                                    \ are set to PLAYER_FRONT_Z tiles forwards
                                    \ from the camera coordinates, so that's
                                    \ just in front of the ship [when the ship
                                    \ is not close to the ground, in which case
                                    \ the explosion would be above the ship as
                                    \ the ship moves down the screen]

 LDR     R0, [R11, #xCamera]        \ Set R0 = xCamera

 MOV     R1, #0                     \ Set R1 = 0

 LDR     R2, [R11, #zCamera]        \ Set R2 = zCamera - PLAYER_FRONT_Z
 SUB     R2, R2, #PLAYER_FRONT_Z

 BL      AddExplosionToBuffer       \ Add an explosion into the particle data
                                    \ buffers

 LDMFD   R13!, {PC}                 \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; AddSparkCloudToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; An unused routine that adds a cloud of 150 spark particles to the
\             particle data buffer, just in front of the player's ship
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.AddSparkCloudToBuffer

 STMFD   R13!, {R6-R9, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOV     R8, #150                   \ Set R8 = 150 so the explosion contains 150
                                    \ clusters of four particles

                                    \ Now we add an explosion to the particle
                                    \ buffer at coordinates [R0, R1, R2], which
                                    \ are set to PLAYER_FRONT_Z tiles forwards
                                    \ from the camera coordinates, so that's
                                    \ just in front of the ship [when the ship
                                    \ is not close to the ground, in which case
                                    \ the explosion would be above the ship as
                                    \ the ship moves down the screen]

 LDR     R0, [R11, #xCamera]        \ Set R0 = xCamera

 MOV     R1, #0                     \ Set R1 = 0

 LDR     R2, [R11, #zCamera]        \ Set R2 = zCamera - PLAYER_FRONT_Z
 SUB     R2, R2, #PLAYER_FRONT_Z

.spcl1

 BL      AddSparkParticleToBuffer   \ Add a spark particle to the particle data
                                    \ buffer [one that fades from white to red]

 SUBS    R8, R8, #1                 \ Decrement the particle counter in R8

 BPL     spcl1                      \ Loop back until we have drawn all R8
                                    \ particles

 LDMFD   R13!, {R6-R9, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; SpawnRock
\       Type; Subroutine
\   Category; Particles
\    Summary; An unused routine that spawns a rock in the sky, at half the
\             altitude of the rocks in the DropRocksFromTheSky routine
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.SpawnRock

 STMFD   R13!, {R6-R9, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

                                    \ Drop a rock from the sky, spawning the
                                    \ rock at coordinates [R0, R1, R2], which
                                    \ are set to half the height of a normal
                                    \ rock and PLAYER_FRONT_Z tiles forwards
                                    \ from the camera coordinates, which is
                                    \ fairly high in the sky above the ship's
                                    \ current position and one tile in front of
                                    \ the ship

 LDR     R0, [R11, #xCamera]        \ Set R0 = xCamera

 MVN     R1, #ROCK_HEIGHT / 2       \ Set R1 = ~ROCK_HEIGHT / 2
                                    \        = -[ROCK_HEIGHT + 1] / 2

 LDR     R2, [R11, #zCamera]        \ Set R2 = zCamera - PLAYER_FRONT_Z
 SUB     R2, R2, #PLAYER_FRONT_Z

 BL      DropARockFromTheSky        \ Drop a rock from the coordinates in
                                    \ [R0, R1, R2]

 LDMFD   R13!, {R6-R9, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DropRocksFromTheSky
\       Type; Subroutine
\   Category; Particles
\    Summary; If the score is 800 or more, then randomly drop rocks from the sky
\             by spawning them as particles
\
\ ******************************************************************************

.DropRocksFromTheSky

 LDR     R4, [R11, #currentScore]   \ Set R4 to the current score minus 800
 SUBS    R4, R4, #800

 MOVMI   PC, R14                    \ If the result is negative then the current
                                    \ score is 800 or less, so return from the
                                    \ subroutine without dropping any rocks

                                    \ If we get here then the current score is
                                    \ greater than 800, so we randomly drop
                                    \ rocks from the sky

 STMFD   R13!, {R14}                \ Store the return address on the stack

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 MOV     R0, R0, LSR #18            \ Scale R0 to the range 0 to 16383

 CMP     R0, R4                     \ If R0 >= R4 then return from the
 LDMHSIA R13!, {PC}                 \ subroutine without dropping a rock, so the
                                    \ chances of a rock dropping from the sky on
                                    \ each iteration of the main loop increases
                                    \ with higher scores

                                    \ If we get here then we drop a rock from
                                    \ the sky, spawning the rock at coordinates
                                    \ [R0, R1, R2], which are set to ROCK_HEIGHT
                                    \ tiles above and PLAYER_FRONT_Z tiles
                                    \ forwards from the camera coordinates,
                                    \ which is very high in the sky above the
                                    \ ship's current position and one tile in
                                    \ front of the ship

 LDR     R0, [R11, #xCamera]        \ Set R0 = xCamera

 MVN     R1, #ROCK_HEIGHT           \ Set R1 = ~ROCK_HEIGHT
                                    \        = -[ROCK_HEIGHT + 1]

 LDR     R2, [R11, #zCamera]        \ Set R2 = zCamera - PLAYER_FRONT_Z
 SUB     R2, R2, #PLAYER_FRONT_Z

 BL      DropARockFromTheSky        \ Drop a rock from the coordinates in
                                    \ [R0, R1, R2] by spawning it as a
                                    \ particle, albeit a very big particle with
                                    \ an associated 3D object

 LDMFD   R13!, {PC}                 \ Return from the subroutine

\ ******************************************************************************
\
\       Name; objectTypes
\       Type; Variable
\   Category; 3D objects
\    Summary; A table that maps object types to object blueprints
\  Deep dive; Placing objects on the map
\             Object blueprints
\
\ ******************************************************************************

.objectTypes

 EQUD    objectPyramid              \ 0  = pyramid [unused]
 EQUD    objectSmallLeafyTree       \ 1  = small leafy tree
 EQUD    objectTallLeafyTree        \ 2  = tall leafy tree
 EQUD    objectSmallLeafyTree       \ 3  = small leafy tree
 EQUD    objectSmallLeafyTree       \ 4  = small leafy tree
 EQUD    objectGazebo               \ 5  = gazebo
 EQUD    objectTallLeafyTree        \ 6  = tall leafy tree
 EQUD    objectFirTree              \ 7  = fir tree
 EQUD    objectBuilding             \ 8  = building
 EQUD    objectRocket               \ 9  = rocket
 EQUD    objectRocket               \ 10 = rocket
 EQUD    objectRocket               \ 11 = rocket

 EQUD    objectRocket               \ 12 = smoking but intact rocket [unused]
 EQUD    objectSmokingRemainsRight  \ 13 = smoking remains [bends to the right]
 EQUD    objectSmokingRemainsLeft   \ 14 = smoking remains [bends to the left]
 EQUD    objectSmokingRemainsLeft   \ 15 = smoking remains [bends to the left]
 EQUD    objectSmokingRemainsLeft   \ 16 = smoking remains [bends to the left]
 EQUD    objectSmokingGazebo        \ 17 = smoking remains of a gazebo
 EQUD    objectSmokingRemainsRight  \ 18 = smoking remains [bends to the right]
 EQUD    objectSmokingRemainsRight  \ 19 = smoking remains [bends to the right]
 EQUD    objectSmokingBuilding      \ 20 = smoking remains of a building
 EQUD    objectSmokingRemainsRight  \ 21 = smoking remains [bends to the right]
 EQUD    objectSmokingRemainsLeft   \ 22 = smoking remains [bends to the left]
 EQUD    objectSmokingRemainsLeft   \ 23 = smoking remains [bends to the left]

 EQUD    objectSmokingRemainsLeft   \ 24 = smoking remains [unused]

\ ******************************************************************************
\
\       Name; DrawObjects [Part 1 of 3]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw all the objects in the visible portion of the object map,
\             starting by working our way through the map looking for objects
\  Deep dive; Drawing 3D objects
\
\ ******************************************************************************

.DrawObjects

 STMFD   R13!, {R5-R12, R14}        \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

                                    \ We are going to loop through every tile on
                                    \ the screen to check whether there are any
                                    \ corresponding objects in the object map,
                                    \ so we start by working out which entry in
                                    \ the object map corresponds to the far left
                                    \ corner at the back of the on-screen
                                    \ landscape view
                                    \
                                    \ We work from back to front so that objects
                                    \ get drawn in that order, to ensure that
                                    \ distant objects always appear behind
                                    \ closer objects

 LDR     R0, [R11, #xCamera]        \ Set R0 to the x-coordinate of the camera,
                                    \ which is at the back of the landscape and
                                    \ in the middle

 AND     R8, R0, #&FF000000         \ Zero the bottom three bytes of R0 and
                                    \ store in R8, so R8 contains the
                                    \ x-coordinate of the tile below the camera,
                                    \ in the middle of the landscape

 LDR     R1, [R11, #zCamera]        \ Set R1 to the z-coordinate of the camera

 AND     R9, R1, #&FF000000         \ Zero the bottom three bytes of R1 and
                                    \ store in R9, so R9 contains the
                                    \ z-coordinate of the tile below the camera,
                                    \ at the back of the landscape

 SUB     R8, R8, #LANDSCAPE_X       \ Set xCameraTile = R8 - LANDSCAPE_X
 STR     R8, [R11, #xCameraTile]    \
                                    \ We already rounded xCamera down to the
                                    \ nearest tile, so this moves us to the
                                    \ coordinate of the tile corner at the left
                                    \ end of the corner row, as subtracting the
                                    \ landscape offset effectively moves the
                                    \ camera to the left by that amount
                                    \
                                    \ We store the result in xCameraTile, so
                                    \ this is set to the x-coordinate of the
                                    \ left end of the tile row
                                    \
                                    \ As we only take the top byte of the
                                    \ x-coordinate when reading the object map,
                                    \ this moves us to the front-left corner of
                                    \ the on-screen landscape, so we can now
                                    \ work along the x-axis in the object map
                                    \ from left to right in the following loop

                                    \ We are now ready to loop through the
                                    \ object map, so we set up the loop counters
                                    \ for two loops - an outer loop with R7 as
                                    \ the counter to work through the z-axis
                                    \ from back to front, and an inner loop with
                                    \ R6 as the counter to work through the
                                    \ x-axis for each row from left to right

 MOV     R7, #TILES_Z               \ Set R7 = TILES_Z to act as a loop counter
                                    \ as we work our way along the tiles on the
                                    \ z-axis [i.e. from back to front on the
                                    \ screen]

.dobs1

 MOV     R6, #TILES_X               \ Set R6 = TILES_X to act as a loop counter
                                    \ as we work our way along the tiles on the
                                    \ x-axis [i.e. from left to right on the
                                    \ screen]

 LDR     R8, [R11, #xCameraTile]    \ Set R8 = xCameraTile, so R8 contains the
                                    \ x-coordinate of the leftmost tile of the
                                    \ visible landscape

                                    \ We now have the coordinates of the far
                                    \ left tile in [R8, R9], so we can start to
                                    \ iterate through the object map one row at
                                    \ a time, checking each tile to see if there
                                    \ is an object there [so we can draw it]

.dobs2

 ADD     R14, R11, #objectMap       \ Set R14 to the address of the object map

 ADD     R14, R14, R8, LSR #24      \ Set R14 = R6 + [R8 >> 24] + [R9 >> 16]
 ADD     R14, R14, R9, LSR #16      \
                                    \ R8 is shifted into the bottom byte of R14,
                                    \ so that's the x-coordinate, and R9 is
                                    \ shifted into the second byte of R14, so
                                    \ that's the z-coordinate, so R14 points to
                                    \ the object map entry for coordinate
                                    \ [R8, R9]

 LDRB    R0, [R14]                  \ Set R0 to the byte in the object map at
                                    \ the coordinate [R8, R9]

 CMP     R0, #&FF                   \ If the object map entry is not &FF, then
 BNE     dobs4                      \ there is an object at this point, so jump
                                    \ to dobs4 to consider drawing the object

.dobs3

 ADD     R8, R8, #TILE_SIZE         \ Step along the x-axis by one whole tile,
                                    \ going from left to right

 SUBS    R6, R6, #1                 \ Decrement the x-axis loop counter in R6

 BNE     dobs2                      \ Loop back to check the next tile until we
                                    \ have checked along the whole x-axis

 SUB     R9, R9, #TILE_SIZE         \ Step along the z-axis by one whole tile,
                                    \ going from back to front

 SUBS    R7, R7, #1                 \ Decrement the z-axis loop counter in R6

 BNE     dobs1                      \ Loop back to check the next tile until we
                                    \ have checked along the whole z-axis

 LDMFD   R13!, {R5-R12, PC}         \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawObjects [Part 2 of 3]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw the object that we have found on the object map
\  Deep dive; Drawing 3D objects
\
\ ******************************************************************************

.dobs4

                                    \ If we get here then there is an object at
                                    \ [R8, R9] of type R0

 STRB    R0, [R11, #objectType]     \ Store the object type in objectType

]

 typeOffset = P% + 8 - objectTypes  : REM Set typeOffset to the offset back to the
                                    : REM objectTypes table from the next
                                    : REM instruction

[

 OPT    pass%

 ADD     R0, PC, R0, LSL #2         \ Set R14 to the address in entry R0 in the
 LDR     R14, [R0, #-typeOffset]    \ objectTypes table, which is the address of
                                    \ the blueprint for this object type

 STR     R14, [R11, #objectData]    \ Store the address of the blueprint in
                                    \ objectData

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [R8, R9], which is where we
                                    \ are drawing our object

 MOV     R1, R0                     \ Make a copy of the altitude in R1, so R1
                                    \ contains the y-coordinate of the object as
                                    \ it sits on the landscape

 CMP     R0, #SEA_LEVEL             \ If the object is on the sea, jump back to
 BEQ     dobs3                      \ dobs3 to move on to the next object, as
                                    \ there are no objects on the sea
                                    \
                                    \ This check sounds unnecessary as there
                                    \ shouldn't be any objects in the sea in the
                                    \ object map anyway, as this check is also
                                    \ performed when populating the object map
                                    \
                                    \ However, because the landscape in Lander
                                    \ is infinite but the object map is finite,
                                    \ the object map gets repeated every 256
                                    \ tiles in each direction, and objects that
                                    \ appear on land in one instance of the
                                    \ object may well appear in the sea in
                                    \ another instance, so this check prevents
                                    \ that from happening

 LDR     R14, [R11, #xCamera]       \ Set R0 = R8 - xCamera
 SUB     R0, R8, R14                \        = x - xCamera
                                    \
                                    \ So R0 contains the x-coordinate of the
                                    \ object relative to the camera

 LDR     R14, [R11, #zCamera]       \ Set R2 = R9 - zCamera
 SUB     R2, R9, R14                \
                                    \ So R2 contains the z-coordinate of the
                                    \ object relative to the camera

 ADD     R2, R2, #LANDSCAPE_Z       \ Move the coordinate back by the landscape
                                    \ offset, so [R0, R2] contains the
                                    \ coordinate of the particle relative to the
                                    \ back-centre point of the landscape

 LDR     R14, [R11, #yCamera]       \ Set R1 = R1 - yCamera
 SUB     R1, R1, R14                \        = y - yCamera
                                    \
                                    \ So R1 contains the y-coordinate of the
                                    \ object relative to the camera

 LDRB    R14, [R11, #objectType]    \ If the object type is 12 or more, then it
 CMP     R14, #12                   \ represents a destroyed object, so jump to
 BHS     dobs6                      \ dobs6

.dobs5

 ADD     R3, R11, #rotationMatrix   \ Set R3 to the address of the object's
                                    \ rotation matrix, to pass to DrawObject

 BL      DrawObject                 \ Draw the object

 B       dobs3                      \ Jump back to dobs3 to move on to the next
                                    \ object

\ ******************************************************************************
\
\       Name; DrawObjects [Part 3 of 3]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw a destroyed object that we have found on the object map
\  Deep dive; Drawing 3D objects
\
\ ******************************************************************************

.dobs6

                                    \ If we get here then this is a destroyed
                                    \ object

 LDR     R14, [R11, #mainLoopCount] \ If either bit 0 or 1 of mainLoopCount are
 TST     R14, #%00000011            \ set, jump to dobs5 to draw the object and
 BNE     dobs5                      \ skip the following

                                    \ We only get here on one out of every four
                                    \ iterations around the main loop

 STMFD   R13!, {R0-R7, R9}          \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R14, [R11, #yCamera]       \ Set R1 = R1 + yCamera
 ADD     R1, R1, R14                \         = y + yCamera
                                    \
                                    \ This reverts R1 to the altitude of the
                                    \ landscape at the object [as we subtracted
                                    \ yCamera from the altitude back in part 3]

 MOV     R0, R8                     \ Set [R0, R1, R2] = [x, y-SMOKE_HEIGHT, z]
 MOV     R2, R9                     \
 SUB     R1, R1, #SMOKE_HEIGHT      \ So this coordinate is SMOKE_HEIGHT above
                                    \ the base of the object, or 3/4 of the tile
                                    \ size [as the y-axis points downwards],
                                    \ which is where we add our smoke particles,
                                    \ one on each iteration around the main loop

 BL      AddSmokeParticleToBuffer   \ Call AddSmokeParticleToBuffer to draw a
                                    \ smoke particle rising from the destroyed
                                    \ object

 LDMFD   R13!, {R0-R7, R9}          \ Retrieve the registers that we stored on
                                    \ the stack

 B       dobs5                      \ Jump to dobs5 to draw the object

\ ******************************************************************************
\
\       Name; objectPlayerAddr
\       Type; Variable
\   Category; 3D objects
\    Summary; The address of the object blueprint for the player's ship
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectPlayerAddr

 EQUD    objectPlayer

\ ******************************************************************************
\
\       Name; objectRockAddr
\       Type; Variable
\   Category; 3D objects
\    Summary; The address of the object blueprint for a rock
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectRockAddr

 EQUD    objectRock

\ ******************************************************************************
\
\       Name; DrawObject [Part 1 of 5]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw a 3D object and its shadow
\  Deep dive; Drawing 3D objects
\             Object blueprints
\             Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    The 3D coordinate [x, y, z] of the object
\                                   relative to the camera
\
\   R3                              The address of the object's rotation matrix
\
\ ******************************************************************************

.DrawObject

 STMFD   R13!, {R6-R12, R14}        \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R12, graphicsBufferEndAddr \ Set R12 to the address of the table that
                                    \ contains the end addresses of the graphics
                                    \ buffers, so when we draw the objects, we
                                    \ can add them as drawing commands onto the
                                    \ ends of the relevant graphics buffers
                                    \
                                    \ In other words, R12 = graphicsBuffersEnd
                                    \ throughout the following

 ADD     R4, R11, #xObject          \ Set R4 to the address of xObject

 STMIA   R4, {R0-R2}                \ Store [R0, R1, R2] in the first three
                                    \ words of xObject, so we have;
                                    \
                                    \   * xObject = x-coordinate of the object
                                    \               relative to the camera
                                    \
                                    \   * yObject = y-coordinate of the object
                                    \               relative to the camera
                                    \
                                    \   * zObject = z-coordinate of the object
                                    \               relative to the camera

                                    \ We now scale up the coordinate [x, y, z]
                                    \ in [R0, R1, R2] as high as it can go while
                                    \ still staying within 32-bit coordinates

 MOVS    R4, R0                     \ Set R4 = |R0|
 MVNMI   R4, R4                     \        = |x|
                                    \
                                    \ If R0 is negative then R4 is actually set
                                    \ ~x, which is -[x+1], but it's close enough

 MOVS    R5, R1                     \ Set R5 = |R1|
 MVNMI   R5, R5                     \        = |y|
                                    \
                                    \ If R1 is negative then R5 is actually set
                                    \ ~y, which is -[y+1], but it's close enough

 ORR     R4, R4, R5                 \ Set R4 = |R0| OR |R1| OR R2
 ORR     R4, R4, R2                 \        = |x| OR |y| OR z
 ORRS    R4, R4, #1                 \
                                    \ And round it up so it is non-zero
                                    \
                                    \ So this sets R4 to a number that is
                                    \ greater than |x|, |y| and z, so R4 is
                                    \ therefore an upper bound on the values of
                                    \ all three coordinates

 MOVPL   R4, R4, LSL #1             \ If R4 is positive then we know bit 7 is
                                    \ clear, so shift the sign bit off the end
                                    \ so we don't include it in the scale factor
                                    \ calculation below

                                    \ We now work out how many times we can
                                    \ scale up R4 so that it is as large as
                                    \ possible but still fits within a 32-bit
                                    \ word

 MOV     R5, #0                     \ Set R5 = 0 to use as the scale factor in
                                    \ the following loop

 STRB    R5, [R11, #crashedFlag]    \ Set crashedFlag = 0 to indicate that the
                                    \ object has not crashed [though we will
                                    \ change this later if we find that it has]

.dobj1

 MOVS    R4, R4, LSL #1             \ Shift R4 to the left until the top bit is
 ADDPL   R5, R5, #1                 \ set, incrementing R5 for each shift so R5
 BPL     dobj1                      \ contains the scale factor we have applied
                                    \ to R4
                                    \
                                    \ So this scales R4 as high as possible
                                    \ while still staying within 32 bits, with
                                    \ the scale factor given in R5

 MOV     R0, R0, LSL R5             \ We now scale up the coordinate [x, y, z]
 MOV     R1, R1, LSL R5             \ in [R0, R1, R2] by the scale factor in R5,
 MOV     R2, R2, LSL R5             \ as we know the result will stay within
                                    \ 32-bit words

 ADD     R4, R11, #xObjectScaled    \ Store [R0, R1, R2] in xObjectScaled,
 STMIA   R4, {R0-R2}                \ zObjectScaled and zObjectScaled

 ADD     R4, R11, #rotationMatrix   \ If R3 does not already point to the
 CMP     R3, R4                     \ rotation matrix at rotationMatrix, copy
 LDMNEIA R3!, {R0-R2}               \ the nine-word matrix from the address in
 STMNEIA R4!, {R0-R2}               \ R3 into rotationMatrix, so rotationMatrix
 LDMNEIA R3!, {R0-R2}               \ now contains the object's rotation matrix,
 STMNEIA R4!, {R0-R2}               \ which is made up of the three orientation
 LDMNEIA R3!, {R0-R2}               \ vectors in;
 STMNEIA R4!, {R0-R2}               \
                                    \   [xNoseV, xRoofV, xSideV]
                                    \   [yNoseV, yRoofV, ySideV]
                                    \   [zNoseV, zRoofV, zSideV]

                                    \ So by this point we have;
                                    \
                                    \   * The object's coordinate in;
                                    \
                                    \     [xObject, yObject, zObject]
                                    \
                                    \   * The scaled-up coordinate in;
                                    \
                                    \     [xObjectScaled, yObjectScaled,
                                    \     zObjectScaled]
                                    \
                                    \   * The object's rotation matrix at
                                    \     rotationMatrix, which is made up of
                                    \     the orientation vectors as follows;
                                    \
                                    \     [xNoseV, xRoofV, xSideV]
                                    \     [yNoseV, yRoofV, ySideV]
                                    \     [zNoseV, zRoofV, zSideV]
                                    \
                                    \   * crashedFlag = 0
                                    \
                                    \   * R12 = graphicsBuffersEnd
                                    \
                                    \ We are now ready to move on to processing
                                    \ the object's vertices and faces

\ ******************************************************************************
\
\       Name; DrawObject [Part 2 of 5]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Process the object's vertices
\  Deep dive; Drawing 3D objects
\             Object blueprints
\             Collisions and bullets
\
\ ******************************************************************************

 LDR     R0, [R11, #objectData]     \ Set R0 to the address of the blueprint
                                    \ for the object that is being drawn

 LDR     R8, [R0]                   \ Set R8 to the first word of the blueprint,
                                    \ which contains the number of vertices

 ADD     R9, R0, #16                \ Set R9 to the address of the vertices data
                                    \ in the blueprint, which appears from the
                                    \ 16th byte onwards

 ADD     R10, R11, #vertexProjected \ Set R10 to the address of vertexProjected
                                    \ which is where we will store the screen
                                    \ coordinates of the projected vertices

 LDRB    R1, [R0, #12]              \ Set objectFlags to the fourth word of the
 STRB    R1, [R11, #objectFlags]    \ blueprint, which contains the object's
                                    \ flags

                                    \ We now iterate through the vertices in the
                                    \ blueprint, using R8 as a loop counter [as
                                    \ we set it above to the number of vertices]
                                    \
                                    \ As we iterate through the vertices we
                                    \ rotate each one by the object's rotation
                                    \ matrix [to orientate the object properly]
                                    \ and project it onto the screen, saving the
                                    \ results in the vertexProjected table

.dobj2

 LDMIA   R9!, {R2-R4}               \ Load the coordinates of the next vertex
                                    \ from R9 into [R2, R3, R4], and update R9
                                    \ to point to the vertex after that, ready
                                    \ for the next iteration

 ADD     R0, R11, #xVertex          \ Set R0 to the address of xVertex

 ADD     R1, R11, #xVertexRotated   \ Set R1 to the address of xVertexRotated

 STMIA   R0, {R2-R4}                \ Store the vertex coordinates in xVertex,
                                    \ so [xVertex, yVertex, zVertex] contains
                                    \ the vertex coordinates

 BL      MultiplyVectorByMatrix     \ If this object is a static object, then
                                    \ simply copy the vertex into xVertexRotated
                                    \ as follows;
                                    \
                                    \   [xVertexRotated]   [xVertex]
                                    \   [yVertexRotated] = [yVertex]
                                    \   [zVertexRotated]   [zVertex]
                                    \
                                    \ If this is a rotating object, then
                                    \ multiply the coordinates at R0 [i.e. the
                                    \ vertex coordinates at xVertex] by the
                                    \ rotation matrix in rotationMatrix, and
                                    \ store the results at R1 [i.e. the rotated
                                    \ coordinates at xVertexRotated];
                                    \
                                    \   [xVertexRotated]               [xVertex]
                                    \   [yVertexRotated] = rotMatrix . [yVertex]
                                    \   [zVertexRotated]               [zVertex]
                                    \
                                    \ So this rotates the vertex coordinates by
                                    \ the object's rotation matrix

 ADD     R0, R11, #xObject          \ Set R0 to the address of xObject

 BL      AddVectorToVertices        \ Call AddVectorToVertices to set;
                                    \
                                    \   [xCoord]   [xObject]   [xVertexRotated]
                                    \   [yCoord] = [yObject] + [yVertexRotated]
                                    \   [zCoord]   [zObject]   [zVertexRotated]
                                    \
                                    \ So [xCoord, yCoord, zCoord] contains the
                                    \ coordinates of this vertex in 3D space,
                                    \ using the game's coordinate system

 ADD     R0, R11, #xCoord           \ Set R0 to the address of xCoord

 BL      ProjectVertexOntoScreen    \ Project [xCoord, yCoord, zCoord] onto the
                                    \ screen, returning the results in [R0, R1]

 STMIA   R10!, {R0-R1}              \ Store [R0, R1] in vertexProjected and
                                    \ update R10 to point to the next coordinate
                                    \ in vertexProjected, ready for us to add
                                    \ the shadow's projected vertex next

 ADD     R0, R11, #xCoord           \ Set R0 to the address of xCoord

 BL      GetLandscapeBelowVertex    \ Get the landscape altitude below the
                                    \ vertex and return it in R0

 STR     R0, [R11, #yCoord]         \ Set yCoord = R0, so [xCoord, yCoord,
                                    \ zCoord] now contains the coordinate on the
                                    \ landscape directly below the vertex
                                    \
                                    \ We use this coordinate for the object's
                                    \ shadow, which we store in vertexProjected
                                    \ just after the normally projected vertex

 ADD     R0, R11, #xCoord           \ Set R0 to the address of xCoord

 BL      ProjectVertexOntoScreen    \ Project [xCoord, yCoord, zCoord] onto the
                                    \ screen, returning the results in [R0, R1]

 STMIA   R10!, {R0-R1}              \ Store [R0, R1] in vertexProjected and
                                    \ update R10 to point to the next coordinate
                                    \ in vertexProjected, ready for the next
                                    \ iteration

 LDR     R14, [R10, #-12]           \ Set R14 to the second coordinate from the
                                    \ previous projection, i.e. the y-coordinate
                                    \ of the projected vertex [so that's the
                                    \ object rather than the shadow]

 CMP     R14, R1                    \ If R14 >= R1 then the y-coordinate of the
 MVNHS   R14, #0                    \ object is bigger than the y-coordinate of
 STRHSB  R14, [R11, #crashedFlag]   \ the shadow, which means the object is
                                    \ lower down the screen than its shadow
                                    \
                                    \ This can only happen if the object has
                                    \ 'passed through' the ground, so set
                                    \ crashedFlag to &FF to indicate that the
                                    \ object has crashed

 SUBS    R8, R8, #1                 \ Decrement the loop counter, which keeps
                                    \ track of the number of vertices we have
                                    \ processed

 BNE     dobj2                      \ Loop back to dobj2 to move on to the next
                                    \ vertex, until we have processed them all

                                    \ We now move on to processing the object's
                                    \ face data so we can draw the visible faces

\ ******************************************************************************
\
\       Name; DrawObject [Part 3 of 5]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Calculate the visibility of each of the object's faces
\  Deep dive; Drawing 3D objects
\             Object blueprints
\
\ ******************************************************************************

 LDR     R1, [R11, #objectData]     \ Set R1 to the address of the blueprint
                                    \ for the object that is being drawn

 LDR     R0, [R1, #8]               \ Set R0 to the third word of the blueprint,
                                    \ which contains the offset from the start
                                    \ of the blueprint to the face data

 ADD     R9, R1, R0                 \ Set R9 to the address of the face data in
                                    \ the blueprint

 LDR     R8, [R1, #4]               \ Set R8 to the second word of the
                                    \ blueprint, which contains the number of
                                    \ faces

                                    \ We now iterate through the faces in the
                                    \ blueprint, using R8 as a loop counter [as
                                    \ we just set it to the number of faces]
                                    \
                                    \ As we iterate through the faces we check
                                    \ their visibility, and draw the visible
                                    \ faces and [if applicable] their shadows
                                    \ into the graphics buffers

.dobj3

 MOV     R0, R9                     \ Copy the value of R9 into R0, so R0 points
                                    \ to the start of the current face data,
                                    \ which is where the face's normal vector is
                                    \ stored

 ADD     R9, R9, #12                \ Add 12 to R9 so it points to the fourth
                                    \ word in the current face data, which is
                                    \ where the vertex numbers are stored

 ADD     R1, R11, #xVertex          \ Set R1 to the address of xVertex, so we
                                    \ store the results of the following
                                    \ calculation here

 BL      MultiplyVectorByMatrix     \ If this object is a static object, then
                                    \ simply copy the normal vector into xVertex
                                    \ as follows;
                                    \
                                    \   [xVertex]   [xNormal]
                                    \   [yVertex] = [yNormal]
                                    \   [zVertex]   [zNormal]
                                    \
                                    \ If this is a rotating object, then
                                    \ calculate the following, multiplying the
                                    \ vector in R0 by the rotation matrix in
                                    \ rotationMatrix;
                                    \
                                    \   [xVertex]                    [xNormal]
                                    \   [yVertex] = rotationMatrix . [yNormal]
                                    \   [zVertex]                    [zNormal]
                                    \
                                    \ So this rotates the normal vector in R0 by
                                    \ the object's rotation matrix, so the
                                    \ normal has now been rotated into the same
                                    \ frame of reference as the object in the 3D
                                    \ world, and we can check the orientation of
                                    \ that rotated normal to see if the face is
                                    \ visible

 LDR     R1, [R11, #yVertex]        \ Set R1 to yVertex, the y-coordinate of the
                                    \ rotated normal

 LDRB    R14, [R11, #objectFlags]   \ If bit 0 of objectFlags is zero then this
 TST     R14, #%00000001            \ object is static and does not rotate, so
 MVNEQ   R3, #0                     \ set R1 and R3 to -1 so this face is always
 MVNEQ   R1, #0                     \ visible [as R3 is negative] and always
                                    \ casts a shadow in objects with shadows
                                    \ as [R1 is negative]

 ADDNE   R2, R11, #xObjectScaled    \ Otherwise bit 0 of objectFlags is set, so
 ADDNE   R0, R11, #xVertex          \ calculate the dot product of the vectors
 BLNE    GetDotProduct              \ at xObjectScaled and xVertex, storing the
                                    \ result in R3
                                    \
                                    \ The sign of the dot product depends on the
                                    \ angle between the two vectors, so R3 is;
                                    \
                                    \   * Negative if angle < 90 degrees
                                    \
                                    \   * Positive if angle >= 90 degrees
                                    \
                                    \ The vector at xObjectScaled is the vector
                                    \ from the camera to the object, so we can
                                    \ see faces with normals that are less than
                                    \ 90 degrees off this vector, as they point
                                    \ towards the camera, while hidden faces
                                    \ point away from the camera at angles of
                                    \ more than 90 degrees
                                    \
                                    \ So if R3 is positive, the face is facing
                                    \ away from us and is not visible, and if
                                    \ it's negative, the opposite is true

 STMFD   R13!, {R8-R9, R11-R12}     \ Store the registers that we want to use on
                                    \ the stack so they can be retrieved at
                                    \ dobj5 after the face has been processed

\ ******************************************************************************
\
\       Name; DrawObject [Part 4 of 5]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw the shadow for each of the object's faces
\  Deep dive; Drawing 3D objects
\             Object blueprints
\
\ ******************************************************************************

 CMP     R1, #0                     \ If R1 is positive, then the y-coordinate
 BPL     dobj4                      \ of the rotated normal vector is positive,
                                    \ which means it is pointing down, so jump
                                    \ to dobj4 to skip drawing the shadow for
                                    \ this face, as we only draw shadows for
                                    \ faces that point up, like the roof of the
                                    \ gazebo

 LDRB    R14, [R11, #objectFlags]   \ If bit 1 of objectFlags is zero then this
 TST     R14, #%00000010            \ object is configured not to have a shadow,
 BEQ     dobj4                      \ so jump to dobj4 to skip the following

                                    \ We now draw the object's shadow into the
                                    \ graphics buffers

 STMFD   R13!, {R3, R9, R11}        \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDMIA   R9, {R5-R7}                \ Set R5, R6, R7 to the fourth, fifth and
                                    \ sixth words of the face data, which
                                    \ contain the numbers of the vertices that
                                    \ make up the face

 MOV     R8, #0                     \ Set R8 = 0, which we will pass to the
                                    \ DrawTriangleShadowToBuffer routine as the
                                    \ face colour, so the shadow is drawn in
                                    \ black

 ADD     R10, R11, #vertexProjected \ Set R10 to the address of the third word
 ADD     R10, R10, #8               \ in vertexProjected
                                    \
                                    \ For each vertex we wrote two coordinates
                                    \ to vertexProjected, with the second being
                                    \ the shadow's projected coordinates, so
                                    \ this sets R10 to the address of the second
                                    \ projected coordinate's pixel x-coordinate,
                                    \ i.e. the pixel coordinate for the shadow
                                    \ of the first object vertex

 ADD     R5, R10, R5, LSL #4        \ Set [R0, R1] to the two words at offset
 LDMIA   R5, {R0-R1}                \ R5 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the shadow for the
                                    \ vertex number in R5, i.e. the first vertex
                                    \ in this face
                                    \
                                    \ We multiply the vertex number by 16 as
                                    \ there are four words per vertex in
                                    \ vertexProjected, which is 16 bytes per
                                    \ face

 ADD     R6, R10, R6, LSL #4        \ Set [R2, R3] to the two words at offset
 LDMIA   R6, {R2-R3}                \ R6 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the shadow for the
                                    \ vertex number in R6, i.e. the second
                                    \ vertex in this face

 ADD     R7, R10, R7, LSL #4        \ Set [R4, R5] to the two words at offset
 LDMIA   R7, {R4-R5}                \ R7 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the shadow for the
                                    \ vertex number in R7, i.e. the third vertex
                                    \ in this face

 BL      DrawTriangleShadowToBuffer \ Draw the triangle that shows the shadow
                                    \ for this face, which uses the vertices of
                                    \ the face, dropped directly down onto the
                                    \ landscape, all drawn in black

 LDMFD   R13!, {R3, R9, R11}        \ Retrieve the registers that we stored on
                                    \ the stack

\ ******************************************************************************
\
\       Name; DrawObject [Part 5 of 5]
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Draw each of the object's faces
\  Deep dive; Drawing 3D objects
\             Object blueprints
\             Screen memory in the Archimedes
\
\ ******************************************************************************

.dobj4

 CMP     R3, #0                     \ If R3 is positive then the face is facing
 BPL     dobj5                      \ away from us and isn't visible, so jump to
                                    \ dobj5 to skip drawing the face

                                    \ We now calculate the colour of the face,
                                    \ setting the brightness according to the
                                    \ direction that the face is pointing [the
                                    \ light source is directly above and
                                    \ slightly to the left]

 LDMIA   R9, {R4-R7}                \ Set R4, R5, R6, R7 to the fourth, fifth,
                                    \ sixth and seventh words of face data,
                                    \ which contain the numbers of the vertices
                                    \ that make up the face [in R4, R5 and R6],
                                    \ and the face colour [in R7]

 ADD     R10, R11, #vertexProjected \ Set R10 to the address of vertexProjected
                                    \
                                    \ For each vertex we wrote two coordinates
                                    \ to vertexProjected, with the first being
                                    \ the face's projected coordinates, so this
                                    \ sets R10 to the address of the first
                                    \ projected coordinate's pixel x-coordinate,
                                    \ i.e. the pixel coordinate for the first
                                    \ object vertex

 LDR     R1, [R11, #yVertex]        \ Set R1 to yVertex, the y-coordinate of the
                                    \ rotated normal

 LDR     R0, [R11, #xVertex]        \ Set R0 to xVertex, the x-coordinate of the
                                    \ rotated normal

 RSB     R1, R1, #&80000000         \ Set R1 = [&80000000 - R1] >> 28
 MOV     R1, R1, LSR #28            \        = [&80000000 - yVertex] >> 28
                                    \
                                    \ So R1 is in the range 0 to 8 and is
                                    \ bigger when the normal is pointing more
                                    \ vertically [i.e. when the normal vector
                                    \ has a negative y-coordinate with a larger
                                    \ magnitude, as the y-axis points down the
                                    \ screen]
                                    \
                                    \ So a bigger, more negative R1 means
                                    \ brighter colours 

 CMP     R0, #0                     \ If R0 is negative then the normal is
 ADDMI   R1, R1, #1                 \ pointing more towards the left then the
                                    \ right, so increment R1 to make the face
                                    \ slightly brighter [as the light source is
                                    \ a little bit to the left]

 SUBS    R1, R1, #5                 \ Set R1 = max[0, R1 - 5]
 MOVMI   R1, #0                     \
                                    \ So R1 is now between 0 and 3, and can be
                                    \ added to the colour numbers below to add
                                    \ the correct level of brightness

 MOV     R8, #%00001111             \ Set R0 = bits 8-11 of the colour in R7, so
 AND     R0, R8, R7, LSR #8         \ if the face colour is &rgb, this is &r

 AND     R2, R8, R7, LSR #4         \ Set R2 = bits 4-7 of the colour in R7, so
                                    \ if the face colour is &rgb, this is &g

 AND     R7, R7, R8                 \ Set R7 = bits 0-3 of the colour in R7, so
                                    \ if the face colour is &rgb, this is &b

 ADD     R0, R0, R1                 \ Set R0 = R0 + R1 and clip to a maximum
 CMP     R0, #16                    \ value of 15, so this adds the face
 MOVHS   R0, #15                    \ brightness to the red channel and ensures
                                    \ the result fits into four bits

 ADD     R2, R2, R1                 \ Set R2 = R2 + R1 and clip to a maximum
 CMP     R2, #16                    \ value of %00001111, so this adds the face
 MOVHS   R2, #15                    \ brightness to the green channel and
                                    \ ensures the result fits into four bits

 ADD     R7, R7, R1                 \ Set R7 = R7 + R1 and clip to a maximum
 CMP     R7, #16                    \ value of %00001111, so this adds the face
 MOVHS   R7, #15                    \ brightness to the blue channel and ensures
                                    \ the result fits into four bits

                                    \ We now build a VIDC colour number in R8
                                    \ by combining the three channels into one
                                    \ byte, which we then replicate four times
                                    \ to get a 32-bit colour number
                                    \
                                    \ The byte is of the form;
                                    \
                                    \   * Bit 7 = blue bit 3
                                    \   * Bit 6 = green bit 3
                                    \   * Bit 5 = green bit 2
                                    \   * Bit 4 = red bit 3
                                    \   * Bit 3 = blue bit 2
                                    \   * Bit 2 = red bit 2
                                    \   * Bit 1 = sum of red/green/blue bit 1
                                    \   * Bit 0 = sum of red/green/blue bit 0
                                    \
                                    \ We now build this colour number in R8 from
                                    \ the red, green and blue values in R0, R2
                                    \ and R7

 ORR     R8, R2, R7                 \ Set R8 to the bottom three bits of;
 AND     R8, R8, #%00000011         \
 ORR     R8, R8, R0                 \ [the bottom two bits of R2 OR R7] OR R0
 AND     R8, R8, #%00000111         \
                                    \ So this sets bits 0, 1 and 2 of R8 as
                                    \ required

 TST     R0, #%00001000             \ If bit 3 of the red channel in R0 is set,
 ORRNE   R8, R8, #%00010000         \ set bit 4 of R8

 AND     R2, R2, #%00001100         \ Clear all bits of the green channel in R2
                                    \ except bits 2-3

 ORR     R8, R8, R2, LSL #3         \ And stick them into bits 5-6 of R8

 TST     R7, #%00000100             \ If bit 2 of the blue channel in R7 is set,
 ORRNE   R8, R8, #%00001000         \ set bit 3 of R8

 TST     R7, #%00001000             \ If bit 3 of the blue channel in R7 is set,
 ORRNE   R8, R8, #%10000000         \ set bit 7 of R8

 ORR     R8, R8, R8, LSL #8         \ Duplicate the lower byte of R8 into the
 ORR     R8, R8, R8, LSL #16        \ other three bytes in the word to produce
 ORR     R8, R8, R8, LSL #24        \ a four-pixel colour word containing four
                                    \ pixels of this colour

                                    \ Finally we can draw the face, using the
                                    \ colour in R8

 ADD     R4, R10, R4, LSL #4        \ Set [R0, R1] to the two words at offset
 LDMIA   R4, {R0-R1}                \ R4 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the vertex number
                                    \ in R4, i.e. the first vertex in this face
                                    \
                                    \ We multiply the vertex number by 16 as
                                    \ there are four words per vertex in
                                    \ vertexProjected, which is 16 bytes per
                                    \ face

 ADD     R5, R10, R5, LSL #4        \ Set [R2, R3] to the two words at offset
 LDMIA   R5, {R2-R3}                \ R5 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the vertex number
                                    \ in R5, i.e. the second vertex in this face

 ADD     R6, R10, R6, LSL #4        \ Set [R4, R5] to the two words at offset
 LDMIA   R6, {R4-R5}                \ R6 * 16 from the table at R10, which is
                                    \ the pixel coordinate of the vertex number
                                    \ in R6, i.e. the third vertex in this face

 BL      DrawTriangleToBuffer       \ Draw the triangle for this face using the
                                    \ projected vertices for the face, drawn in
                                    \ the colour in R8, and draw it one buffer
                                    \ nearer the camera than the shadow, so the
                                    \ shadow never overlaps the object

.dobj5

 LDMFD   R13!, {R8-R9, R11-R12}     \ Retrieve the registers that we stored on
                                    \ the stack before processing the face

 ADD     R9, R9, #16                \ Add 16 to R9 so it points to the next
                                    \ batch of face data [R9 already points to
                                    \ the fourth word in the current face data,
                                    \ so this skips the next four, thereby
                                    \ skipping all seven words of face data]

 SUBS    R8, R8, #1                 \ Decrement the loop counter, which keeps
                                    \ track of the number of faces we have
                                    \ processed

 BNE     dobj3                      \ Loop back to dobj3 to move on to the next
                                    \ face, until we have processed them all

 LDMFD   R13!, {R6-R12, PC}         \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; PrintCurrentScore
\       Type; Subroutine
\   Category; Score bar
\    Summary; Print the current score at the left end of the score bar
\
\ ******************************************************************************

.PrintCurrentScore

 LDR     R0, [R11, #currentScore]   \ Set R0 to the current score

 ADD     R1, R11, #stringBuffer     \ Set R1 = stringBuffer to use as a string
                                    \ buffer for the following call

 MOV     R2, #19                    \ Set R2 = 19, to use as the size of the
                                    \ string buffer in the following call

 CMP     R0, #1024                  \ If R0 >= 1024, set gravity = &50000, so
 MOVHS   R4, #&50000                \ gravity increases from the starting value
 STRHS   R4, [R11, #gravity]        \ of &30000 when we reach a score of 1024

 CMP     R0, #1488                  \ If R0 >= 1488, set gravity = &70000, so
 MOVHS   R4, #&70000                \ gravity increases again when we reach a
 STRHS   R4, [R11, #gravity]        \ score of 1488

 SWI     OS_BinaryToDecimal         \ Convert the unsigned number in R0 to a
                                    \ string and store it in the buffer at R1
                                    \ with a maximum string size of R2

 MOV     R0, #30                    \ Print a VDU 30 command to move the text
 SWI     OS_WriteC                  \ cursor to the top-left corner of the
                                    \ screen

 MOV     R0, #&0A                   \ Print a line feed [ASCII &0A] to move the
 SWI     OS_WriteC                  \ cursor down one line, to the start of the
                                    \ second line, which is where we print the
                                    \ score bar

                                    \ We now print the contents of the string
                                    \ buffer, which updates the number of
                                    \ remaining bullets on-screen

.prsc1

 LDRB    R0, [R1], #1               \ Print the character in the buffer at R1,
 SWI     OS_WriteC                  \ incrementing R1 as we go

 SUBS    R2, R2, #1                 \ Decrement the loop counter in R2

 BNE     prsc1                      \ Loop back to print the next character from
                                    \ the buffer until we have printed all R2 of
                                    \ them

 MOV     R0, #&20                   \ Print two spaces [ASCII &20] to ensure we
 SWI     OS_WriteC                  \ remove any characters from the previous
 SWI     OS_WriteC                  \ bullet count

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; PrintScoreInBothBanks
\       Type; Subroutine
\   Category; Score bar
\    Summary; Print a number at a specified text column in the score bar
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The unsigned number to print
\
\   R1                              Text column [x-coordinate]
\
\   R2                              Text row [y-coordinate]
\
\ ******************************************************************************

.PrintScoreInBothBanks

 STMFD   R13!, {R1-R2}              \ Store the arguments in R1 and R2 on the
                                    \ stack so can retrieve them below

 ADD     R1, R11, #stringBuffer     \ Set R1 = stringBuffer to use as a string
                                    \ buffer for the following call

 MOV     R2, #19                    \ Set R2 = 19, to use as the size of the
                                    \ string buffer in the following call

 SWI     OS_BinaryToDecimal         \ Convert the unsigned number in R0 to a
                                    \ string and store it in the buffer at R1
                                    \ with a maximum string size of R2

 MOV     R0, #31                    \ Start printing the following VDU command;
 SWI     OS_WriteC                  \
                                    \   VDU 31, x, y
                                    \
                                    \ which moves the text cursor to column x
                                    \ on row y

 LDMFD   R13!, {R3-R4}              \ Fetch R1 and R2 into R3 and R4 and write
 MOV     R0, R3                     \ them to complete the VDU 31 command, so
 SWI     OS_WriteC                  \ this moves the text cursor to the position
 MOV     R0, R4                     \ defined in the subroutine arguments,
 SWI     OS_WriteC                  \ leaving the text coordinates in [R3, R4]

 STMFD   R13!, {R1-R2}              \ Store R1 and R2 on the stack so we can
                                    \ preserve them through the following
                                    \ OS_Byte call

 MOV     R0, #112                   \ Set the VDU driver screen bank to bank 1
 MOV     R1, #1
 SWI     OS_Byte

 LDMFD   R13, {R1-R2}               \ Retrieve R1 and R2 from the stack, so R1
                                    \ points to the string buffer and R2 is the
                                    \ buffer size
                                    \
                                    \ Note that we don't update the stack
                                    \ pointer, so we can retrieve them again
                                    \ below

.prsb1

 LDRB    R0, [R1], #1               \ Print the character in the buffer at R1,
 SWI     OS_WriteC                  \ incrementing R1 as we go

 SUBS    R2, R2, #1                 \ Decrement the loop counter in R2

 BNE     prsb1                      \ Loop back to print the next character from
                                    \ the buffer until we have printed all R2 of
                                    \ them

 MOV     R0, #112                   \ Set the hardware and VDU screen banks to
 MOV     R1, #2                     \ screen bank 1
 SWI     OS_Byte

 MOV     R0, #31                    \ Print the following VDU command;
 SWI     OS_WriteC                  \
 MOV     R0, R3                     \   VDU 31, R3, R4
 SWI     OS_WriteC                  \
 MOV     R0, R4                     \ which moves the text cursor to column R3
 SWI     OS_WriteC                  \ on row R4, i.e. the same text coordinates
                                    \ as the text we printed in screen bank 1
                                    \ above

 LDMFD   R13!, {R1-R2}              \ Retrieve R1 and R2 from the stack, so R1
                                    \ points to the string buffer and R2 is the
                                    \ buffer size

.prsb2

 LDRB    R0, [R1], #1               \ Print the character in the buffer at R1,
 SWI     OS_WriteC                  \ incrementing R1 as we go

 SUBS    R2, R2, #1                 \ Decrement the loop counter in R2

 BNE     prsb2                      \ Loop back to print the next character from
                                    \ the buffer until we have printed all R2 of
                                    \ them

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; fuelBarColour
\       Type; Variable
\   Category; Score bar
\    Summary; A four-pixel colour word for the colour of the fuel bar
\
\ ******************************************************************************

.fuelBarColour

 EQUD    &37373737

\ ******************************************************************************
\
\       Name; sinTableAddr 
\       Type; Variable
\   Category; Maths [Geometry]
\    Summary; The address of the sine/cosine lookup table
\
\ ******************************************************************************

.sinTableAddr 

 EQUD    sinTable

\ ******************************************************************************
\
\       Name; arctanTableAddr
\       Type; Variable
\   Category; Maths [Geometry]
\    Summary; The address of the arctan lookup table
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

.arctanTableAddr

 EQUD    arctanTable

\ ******************************************************************************
\
\       Name; squareRootTableAddr
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; The address of the square root lookup table
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

.squareRootTableAddr

 EQUD    squareRootTable

\ ******************************************************************************
\
\       Name; DrawFuelLevel
\       Type; Subroutine
\   Category; Score bar
\    Summary; Draw the bar at the top of the screen showing the current fuel
\             level
\
\ ******************************************************************************

.DrawFuelLevel

 STMFD   R13!, {R10-R12, R14}       \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R1, [R11, #fuelLevel]      \ Set R1 to the current fuel level in
                                    \ fuelLevel

 LDRB    R2, [R11, #fuelBurnRate]   \ Set R2 to the current fuel burn rate in
                                    \ fuelBurnRate

 SUBS    R1, R1, R2                 \ Subtract the fuel burn rate from the fuel
 MOVMI   R1, #0                     \ level, making sure it doesn't fall below
 STR     R1, [R11, #fuelLevel]      \ zero, and store the updated fuel level in
                                    \ fuelLevel [and leaving the value in R1]

 LDR     R7, screenAddr             \ Set R7 to the address of the screen bank
                                    \ we are drawing in, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

 ADD     R11, R7, #320              \ Set R11 to the address in R7, plus 320 to
                                    \ move it down by one pixel line

 LDR     R8, fuelBarColour          \ Set R8 to the four-pixel colour word that
                                    \ we need to poke into screen memory to draw
                                    \ four pixels in the correct colour for the
                                    \ fuel bar

 MOV     R10, R1, LSR #4            \ Set R10 = R1 >> 4
                                    \         = fuel level / 16

 BL      DrawHorizontalLine         \ Draw a horizontal line of length R10,
                                    \ starting at screen address R11 and drawing
                                    \ to the right in the colour in R8, so
                                    \ that's the top pixel row of the bar

 ADD     R11, R7, #2*320            \ Set R11 to the address in R7, plus 640 to
                                    \ move it down by two pixel lines

 LDR     R8, fuelBarColour          \ Set R8 to the four-pixel colour word that
                                    \ we need to poke into screen memory to draw
                                    \ four pixels in the correct colour for the
                                    \ fuel bar

 MOV     R10, R1, LSR #4            \ Set R10 = R1 >> 4
                                    \         = fuel level / 16

 BL      DrawHorizontalLine         \ Draw a horizontal line of length R10,
                                    \ starting at screen address R11 and drawing
                                    \ to the right in the colour in R8, so
                                    \ that's the middle pixel row of the bar

 ADD     R11, R7, #3*320            \ Set R11 to the address in R7, plus 960 to
                                    \ move it down by two pixel lines

 LDR     R8, fuelBarColour          \ Set R8 to the four-pixel colour word that
                                    \ we need to poke into screen memory to draw
                                    \ four pixels in the correct colour for the
                                    \ fuel bar

 MOV     R10, R1, LSR #4            \ Set R10 = R1 >> 4
                                    \         = fuel level / 16

 BL      DrawHorizontalLine         \ Draw a horizontal line of length R10,
                                    \ starting at screen address R11 and drawing
                                    \ to the right in the colour in R8, so
                                    \ that's the bottom pixel row of the bar

 LDMFD   R13!, {R10-R12, PC}        \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; graphicsBufferEndAddr
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The address of the table containing the end addresses of the
\             graphics buffers
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.graphicsBufferEndAddr

 EQUD    graphicsBuffersEnd

\ ******************************************************************************
\
\       Name; graphicsBufferAddr
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The address of the table containing the addresses of the graphics
\             buffers
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.graphicsBufferAddr

 EQUD    graphicsBuffers

\ ******************************************************************************
\
\       Name; MultiplyVectorByMatrix
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Multiply a 3D vector by the rotation matrix in rotationMatrix, if
\             the object is a rotating object
\
\ ------------------------------------------------------------------------------
\
\ If the object currently being processed is static [i.e. bit 0 of objectFlags
\ is clear], then this routine simply returns the vector in R0 unchanged.
\
\ If the object is a rotating object, then this routine multiplies the vector at
\ R0 by the rotation matrix at rotationMatrix and store the result at the
\ address in R1;
\
\   [ R1   ]                    [ R0   ]
\   [ R1+4 ] = rotationMatrix . [ R0+4 ]
\   [ R1+8 ]                    [ R0+8 ]
\
\              [ xNoseV xRoofV xSideV ]   [ R0   ]
\            = [ yNoseV yRoofV ySideV ] . [ R0+4 ]
\              [ zNoseV zRoofV zSideV ]   [ R0+8 ]
\
\ So this rotates a coordinate by the object's rotation matrix [i.e. by each
\ of the object's orientation vectors].
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The address of the vector to multiply
\
\   R1                              The address to store the result
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   [R1 R1+4 R1+8]                  The result of the multiplication as a vector
\
\ ******************************************************************************

.MultiplyVectorByMatrix

 LDRB    R2, [R11, #objectFlags]    \ If bit 0 of objectFlags is clear then this
 TST     R2, #%00000001             \ object is static and does not rotate, so
 LDMEQIA R0, {R2-R4}                \ simply set the following so we do not
 STMEQIA R1, {R2-R4}                \ apply the rotation matrix;
 MOVEQ   PC, R14                    \
                                    \   [ R1   ]   [ R0   ]
                                    \   [ R1+4 ] = [ R0+4 ]
                                    \   [ R1+8 ]   [ R0+8 ]
                                    \
                                    \ and return from the subroutine
 
 STMFD   R13!, {R6-R7, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved
 
 ADD     R2, R11, #rotationMatrix   \ Set R2 to the address of the rotation
                                    \ matrix at rotationMatrix, which contains
                                    \ the object's rotation matrix

 BL      GetDotProduct              \ Set R1 = R2 . R0, so;
 STR     R3, [R1]                   \ 
 BL      GetDotProduct              \   [R1  ]   [ R2    R2+4  R2+8  ]   [R0  ]
 STR     R3, [R1, #4]               \   [R1+4] = [ R2+12 R2+16 R2+20 ] . [R0+4]
 BL      GetDotProduct              \   [R1+8]   [ R2+24 R2+28 R2+32 ]   [R0+8]
 STR     R3, [R1, #8]               \
                                    \ which is the result we want
 
 LDMFD   R13!, {R6-R7, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; GetDotProduct
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Calculate the dot product of two 3D vectors
\  Deep dive; Drawing 3D objects
\
\ ------------------------------------------------------------------------------
\
\ Calculate the dot product of the 3D vectors at R0 and R2 as follows;
\
\        [ R0   ]   [ R2   ]
\   R3 = [ R0+4 ] . [ R2+4 ] = [R0 * R2] + [R0+4 * R2+4] + [R0+8 * R2*8]
\        [ R0+8 ]   [ R2+8 ]
\
\ and set the flags according to the result. R2 is updated to the next vector in
\ memory, so repeated calls will work through the following multiplication,
\ returning one row at a time;
\
\   [ R2    R2+4  R2+8  ]   [ R0   ]
\   [ R2+12 R2+16 R2+20 ] . [ R0+4 ]
\   [ R2+24 R2+28 R2+32 ]   [ R0+8 ]
\
\ See the MultiplyVectorByMatrix routine to see this calculation in use.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The address of the first vector
\
\   R2                              The address of the second vector
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R2                              Updated to the address of the next R2 vector
\
\   R3                              The dot product
\
\   Flags                           Set according to the result in R3
\
\ ******************************************************************************

.GetDotProduct

 LDR     R4, [R2], #4               \ Set R4 to the x-coordinate of R2 [let's
                                    \ call it xR2], and increment R2 to point to
                                    \ the y-coordinate of R2

 LDR     R5, [R0]                   \ Set R5 to the x-coordinate of R0 [let's
                                    \ call it xR0]

                                    \ We now calculate R3 = R4 * R5 using the
                                    \ shift-and-add multiplication algorithm

 EOR     R7, R4, R5                 \ Set the sign of the result in R7

 TEQ     R4, #0                     \ Set R4 = 4 * |R4|
 RSBMI   R4, R4, #0
 MOVS    R4, R4, LSL #2

 TEQ     R5, #0                     \ Set R5 = 2 * |R5|
 RSBMI   R5, R5, #0
 MOV     R5, R5, LSL #1

 AND     R4, R4, #&FE000000         \ Zero all but the top byte of R4, ensuring
 ORR     R4, R4, #&01000000         \ that bit 0 of the top byte is set so the
                                    \ value of the fractional part is set to 0.5

 MOV     R3, #0                     \ Set R3 = 0 to use for building the sum in
                                    \ our shift-and-add multiplication result

.dotp1

 MOV     R5, R5, LSR #1             \ If bit 0 of R5 is set, add R5 to the
 ADDCS   R3, R3, R5                 \ result in R3, shifting R5 to the right

 MOVS    R4, R4, LSL #1             \ Shift R4 left by one place

 BNE     dotp1                      \ Loop back if R4 is non-zero

 MOV     R3, R3, LSR #1             \ Set R3 = R3 / 2

 TEQ     R7, #0                     \ Apply the sign from R7 to R3 to get the
 RSBMI   R3, R3, #0                 \ final result, so;
                                    \
                                    \   R3 = R4 * R5
                                    \      = xR0 * xR2

 LDR     R4, [R2], #4               \ Set R4 to the y-coordinate at R2+4 [let's
                                    \ call it yR2], and increment R2 to point to
                                    \ the z-coordinate of R2

 LDR     R5, [R0, #4]               \ Set R5 to the y-coordinate at R0+4 [let's
                                    \ call it yR0]

 EOR     R7, R4, R5                 \ Set R6 = R4 * R5 using the same algorithm
 TEQ     R4, #0                     \ as above
 RSBMI   R4, R4, #0                 \
 MOVS    R4, R4, LSL #2             \ This is the first part of the algorithm
 TEQ     R5, #0
 RSBMI   R5, R5, #0
 MOV     R5, R5, LSL #1
 AND     R4, R4, #&FE000000
 ORR     R4, R4, #&01000000
 MOV     R6, #0

.dotp2

 MOV     R5, R5, LSR #1             \ This is the second part of the algorithm,
 ADDHS   R6, R6, R5                 \ so we now have;
 MOVS    R4, R4, LSL #1             \
 BNE     dotp2                      \   R6 = R4 * R5
 MOV     R6, R6, LSR #1             \      = yR0 * yR2
 TEQ     R7, #0
 RSBMI   R6, R6, #0

 ADD     R3, R3, R6                 \ Set R3 = R3 + R6
                                    \        = xR0 * xR2 + yR0 * yR2

 LDR     R4, [R2], #4               \ Set R4 to the z-coordinate at R2+8 [let's
                                    \ call it zR2], and increment R2 to point to
                                    \ the next coordinate at R2+12

 LDR     R5, [R0, #8]               \ Set R5 to the z-coordinate at R0+8 [let's
                                    \ call it zR0]

 EOR     R7, R4, R5                 \ Set R6 = R4 * R5 using the same algorithm
 TEQ     R4, #0                     \ as above
 RSBMI   R4, R4, #0                 \
 MOVS    R4, R4, LSL #2             \ This is the first part of the algorithm
 TEQ     R5, #0
 RSBMI   R5, R5, #0
 MOV     R5, R5, LSL #1
 AND     R4, R4, #&FE000000
 ORR     R4, R4, #&01000000
 MOV     R6, #0

.dotp3

 MOV     R5, R5, LSR #1             \ This is the second part of the algorithm,
 ADDHS   R6, R6, R5                 \ so we now have;
 MOVS    R4, R4, LSL #1             \
 BNE     dotp3                      \   R6 = R4 * R5
 MOV     R6, R6, LSR #1             \      = zR0 * zR2
 TEQ     R7, #0
 RSBMI   R6, R6, #0

 ADDS    R3, R3, R6                 \ Set R3 = R3 + R6
                                    \        = xR0 * xR2 + yR0 * yR2 + zR0 * zR2
                                    \
                                    \ which is the result we want
                                    \
                                    \ We also set the flags depending on the
                                    \ result

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; TransposeRotationMatrix
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; An unused routine that transposes the rotation matrix
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.TransposeRotationMatrix

 ADD     R2, R11, #rotationMatrix   \ Set R2 to the address of the rotation
                                    \ matrix

 LDR     R0, [R2, #4]               \ Transpose it by swapping coordinates on
 LDR     R1, [R2, #12]              \ either side of the diagonal from the
 STR     R0, [R2, #12]              \ top-left to bottom-right
 STR     R1, [R2, #4]               \
 LDR     R0, [R2, #8]               \   [ x1 x2 x3 ]      [ x1 y1 z1 ]
 LDR     R1, [R2, #24]              \   [ y1 y2 y3 ]  ->  [ x2 y2 z2 ]
 STR     R0, [R2, #24]              \   [ z1 z2 z3 ]      [ x3 y3 z3 ]
 STR     R1, [R2, #8]               \
 LDR     R0, [R2, #20]              \ This converts the rotation matrix into the
 LDR     R1, [R2, #28]              \ inverse rotation
 STR     R0, [R2, #28]
 STR     R1, [R2, #20]

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; AddVectorsWithFeedback
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; An unused routine that adds a delta vector to a coordinate and
\             updates the delta with feedback from the coordinate value
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.AddVectorsWithFeedback

 STMFD   R13!, {R5-R6, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDMIA   R0, {R2-R4}                \ Fetch the coordinate into [R2, R3, R4]

 LDMIA   R1, {R5-R6, R14}           \ Fetch the delta vector into [R5, R6, R14]

 ADD     R2, R2, R5, ASR #4         \ Set R2 = R2 + R5 / 16
                                    \
                                    \ So this adds a small amount of the x-delta
                                    \ to the x-coordinate

 SUB     R5, R5, R2, ASR #4         \ Set R5 = R5 - R2 / 16
                                    \
                                    \ So this updates the x-delta with feedback
                                    \ from the x-coordinate

 ADD     R3, R3, R6, ASR #4         \ Set R3 = R3 + R6 / 16
                                    \
                                    \ So this adds a small amount of the y-delta
                                    \ to the y-coordinate

 SUB     R6, R6, R3, ASR #4         \ Set R6 = R6 - R3 / 16
                                    \
                                    \ So this updates the y-delta with feedback
                                    \ from the y-coordinate

 ADD     R4, R4, R14, ASR #4        \ Set R4 = R4 + R14 / 16
                                    \
                                    \ So this adds a small amount of the z-delta
                                    \ to the  z-coordinate

 SUB     R14, R14, R4, ASR #4       \ Set R14 = R14 - R4 / 16
                                    \
                                    \ So this updates the z-delta with feedback
                                    \ from the z-coordinate

 STMIA   R0, {R2-R4}                \ Store the updated coordinate

 STMIA   R1, {R5-R6, R14}           \ Store the updated delta vector

 LDMFD   R13!, {R5-R6, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; CalculateRotationMatrix
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Calculate the rotation matrix
\  Deep dive; Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ The rotation matrix is of the form;
\
\   [ xNoseV xRoofV xSideV ]
\   [ yNoseV yRoofV ySideV ]
\   [ zNoseV zRoofV zSideV ]
\
\ where the nose, roof and side vectors are the orientation vectors.
\
\ This routine sets the rotation matrix to the following;
\
\   [  cos[a] * cos[b]   -sin[a] * cos[b]   sin[b] ]
\   [       sin[a]             cos[a]          0   ]
\   [ -cos[a] * sin[b]    sin[a] * sin[b]   cos[b] ]
\
\ This matrix represents a combination of two rotations, one with angle a and
\ the other with angle b.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The first rotation angle, a
\
\   R1                              The second rotation angle, b
\
\ ******************************************************************************

.CalculateRotationMatrix

 STMFD   R13!, {R0-R1, R14}         \ Store the arguments and the return address
                                    \ on the stack

 ADD     R0, R0, #&40000000         \ Set R0 = R0 + &40000000
                                    \        = a + &40000000
                                    \
                                    \ As we shift R0 to the right by 20 places
                                    \ in the sine lookup below, this is the same
                                    \ as adding a value of &40000000 >> 20 to
                                    \ the index
                                    \
                                    \ &40000000 >> 20 = 1024, so when we add
                                    \ this to the index, it skips 256 four-byte
                                    \ words compared to looking up R0 without
                                    \ the addition
                                    \
                                    \ The sine table contains 1024 entries that
                                    \ cover the whole circle, so adding 256 to
                                    \ the index changes the lookup from sine to
                                    \ cosine, as it is effectively adding an
                                    \ extra 90-degrees to the lookup index

 LDR     R14, sinTableAddr          \ Set R14 to the address of the sine lookup
                                    \ table

 BIC     R0, R0, #&00300000         \ Clear bits 21 and 22 of R0 so when we
                                    \ shift R0 to the right by 20 places in the
                                    \ following lookup, the address is aligned
                                    \ to the words in the lookup table

 LDR     R2, [R14, R0, LSR #20]     \ Set
                                    \
                                    \   R2 =   [2^31 - 1]
                                    \        * SIN[2 * PI * [[R0 >> 20] / 1024]]
                                    \
                                    \ So R2 = sin[a + &40000000]
                                    \       = cos[a]

 ADD     R1, R1, #&40000000         \ Using the same approach as above, set;
 LDR     R14, sinTableAddr          \
 BIC     R1, R1, #&00300000         \   R3 = sin[b + &40000000]
 LDR     R3, [R14, R1, LSR #20]     \      = cos[b] 

 LDMFD   R13!, {R4-R5}              \ Set R4 and R5 to the original arguments in
                                    \ R0 and R1, so R4 = a and R5 = b

 LDR     R14, sinTableAddr          \ Using the same approach as above, set;
 BIC     R4, R4, #&00300000         \
 LDR     R0, [R14, R4, LSR #20]     \   R0 = sin[R4]
                                    \      = sin[a]

 LDR     R14, sinTableAddr          \ Using the same approach as above, set;
 BIC     R5, R5, #&00300000         \
 LDR     R1, [R14, R5, LSR #20]     \   R1 = sin[R4]
                                    \      = sin[b]

 STMFD   R13!, {R0-R3}              \ Store R0 to R3 on the stack, so the stack
                                    \ contains;
                                    \
                                    \   R0 = sin[a]
                                    \   R1 = sin[b]
                                    \   R2 = cos[a]
                                    \   R3 = cos[b]

                                    \ We now calculate R4 = R2 * R3 using the
                                    \ shift-and-add multiplication algorithm

 EOR     R14, R2, R3                \ Set the sign of the result in R14

 TEQ     R2, #0                     \ Set R2 = 4 * |R2|
 RSBMI   R2, R2, #0
 MOVS    R2, R2, LSL #2

 TEQ     R3, #0                     \ Set R3 = 2 * |R3|
 RSBMI   R3, R3, #0
 MOV     R3, R3, LSL #1

 AND     R2, R2, #&FE000000         \ Zero all but the top byte of R2, ensuring
 ORR     R2, R2, #&01000000         \ that bit 0 of the top byte is set so the
                                    \ value of R2 is non-zero

 MOV     R4, #0                     \ Set R4 = 0 to use for building the sum in
                                    \ our shift-and-add multiplication result

.rmat1

 MOV     R3, R3, LSR #1             \ If bit 0 of R3 is set, add R3 to the
 ADDHS   R4, R4, R3                 \ result in R4, shifting R3 to the right

 MOVS    R2, R2, LSL #1             \ Shift R2 left by one place

 BNE     rmat1                      \ Loop back if R4 is non-zero

 MOV     R4, R4, LSR #1             \ Set R4 = R4 / 2

 TEQ     R14, #0                    \ Apply the sign from R14 to R4 to get the
 RSBMI   R4, R4, #0                 \ final result, so;
                                    \
                                    \   R4 = R2 * R3
                                    \      = cos[a] * cos[b]

 STR     R4, [R11, #xNoseV]         \ Store the result in xNoseV, so;
                                    \
                                    \   xNoseV = cos[a] * cos[b]

 EOR     R14, R0, R1                \ Set R4 = R0 * R1 using the same algorithm
 TEQ     R0, #0                     \ as above
 RSBMI   R0, R0, #0                 \
 MOVS    R0, R0, LSL #2             \ This is the first part of the algorithm
 TEQ     R1, #0
 RSBMI   R1, R1, #0
 MOV     R1, R1, LSL #1
 AND     R0, R0, #&FE000000
 ORR     R0, R0, #&01000000
 MOV     R4, #0

.rmat2

 MOV     R1, R1, LSR #1             \ This is the second part of the algorithm,
 ADDHS   R4, R4, R1                 \ so we now have;
 MOVS    R0, R0, LSL #1             \
 BNE     rmat2                      \    R4 = R0 * R1
 MOV     R4, R4, LSR #1             \       = sin[a] * sin[b]
 TEQ     R14, #0
 RSBMI   R4, R4, #0

 STR     R4, [R11, #zRoofV]         \ Store the result in zRoofV, so;
                                    \
                                    \   zRoofV = sin[a] * sin[b]

 LDMFD   R13, {R0-R3}               \ Retrieve R0 to R3 from the stack, leaving
                                    \ the stack pointer alone so we can repeat
                                    \ the process later, so R0 to R3 once again
                                    \ contain the following;
                                    \
                                    \   R0 = sin[a]
                                    \   R1 = sin[b]
                                    \   R2 = cos[a]
                                    \   R3 = cos[b]

 EOR     R14, R1, R2                \ Set R4 = R1 * R2 using the same algorithm
 TEQ     R1, #0                     \ as above
 RSBMI   R1, R1, #0                 \
 MOVS    R1, R1, LSL #2             \ This is the first part of the algorithm
 TEQ     R2, #0
 RSBMI   R2, R2, #0
 MOV     R2, R2, LSL #1
 AND     R1, R1, #&FE000000
 ORR     R1, R1, #&01000000
 MOV     R4, #0

.rmat3

 MOV     R2, R2, LSR #1             \ This is the second part of the algorithm,
 ADDHS   R4, R4, R2                 \ so we now have;
 MOVS    R1, R1, LSL #1             \
 BNE     rmat3                      \   R4 = R1 * R2
 MOV     R4, R4, LSR #1             \      = sin[b] * cos[a]
 TEQ     R14, #0                    \      = cos[a] * sin[b]
 RSBMI   R4, R4, #0

 RSB     R4, R4, #0                 \ Negate the result and store it in zNoseV,
 STR     R4, [R11, #zNoseV]         \ so;
                                    \
                                    \   zNoseV = -cos[a] * sin[b]

 EOR     R14, R0, R3                \ Set R4 = R0 * R3 using the same algorithm
 TEQ     R0, #0                     \ as above
 RSBMI   R0, R0, #0                 \
 MOVS    R0, R0, LSL #2             \ This is the first part of the algorithm
 TEQ     R3, #0
 RSBMI   R3, R3, #0
 MOV     R3, R3, LSL #1
 AND     R0, R0, #&FE000000
 ORR     R0, R0, #&01000000
 MOV     R4, #0

.rmat4

 MOV     R3, R3, LSR #1             \ This is the second part of the algorithm,
 ADDHS   R4, R4, R3                 \ so we now have;
 MOVS    R0, R0, LSL #1             \
 BNE     rmat4                      \   R4 = R0 * R3
 MOV     R4, R4, LSR #1             \      = sin[a] * cos[b]
 TEQ     R14, #0
 RSBMI   R4, R4, #0

 RSB     R4, R4, #0                 \ Negate the result and store it in xRoofV,
 STR     R4, [R11, #xRoofV]         \ so;
                                    \
                                    \   xRoofV = -sin[a] * cos[b]

 LDMFD   R13!, {R0-R3, R14}         \ Retrieve R0 to R3 from the stack, so R0 to
                                    \ R3 once again contain the following;
                                    \
                                    \   R0 = sin[a]
                                    \   R1 = sin[b]
                                    \   R2 = cos[a]
                                    \   R3 = cos[b]
                                    \
                                    \ We also retrieve the subroutine return
                                    \ address into R14

 STR     R0, [R11, #yNoseV]         \ Set yNoseV = sin[a]

 STR     R1, [R11, #xSideV]         \ Set xSideV = sin[b]

 STR     R2, [R11, #yRoofV]         \ Set yRoofV = cos[a]

 STR     R3, [R11, #zSideV]         \ Set zSideV = cos[b]

 MOV     R0, #0                     \ Set ySideV = 0
 STR     R0, [R11, #ySideV]

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; GetMouseInPolarCoordinates [Part 1 of 2]
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Convert the mouse x- and y-coordinates into polar coordinates,
\             starting by calculating the polar angle
\  Deep dive; Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ This routine converts mouse coordinates [x, y] into polar coordinates, as in
\ this example;
\
\           .|
\      R0 .´ |
\       .´   | y
\     .´R1   |
\   +´-------+
\        x
\
\ R1 is the angle and R0 is the distance.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The scaled-up mouse x-coordinate
\
\   R1                              The scaled-up mouse y-coordinate
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R0                              The distance of the polar coordinate
\
\   R1                              The angle of the polar coordinate
\
\ ******************************************************************************

.GetMouseInPolarCoordinates

                                    \ In the following, let's call the mouse
                                    \ x- and y-coordinates xMouse and yMouse

 STMFD   R13!, {R14}                \ Store the return address on the stack

 MOV     R3, #0                     \ Set R3 = 0

 CMP     R0, #0                     \ If the mouse x-coordinate in R0 is
 EORMI   R3, R3, #%00000011         \ negative, negate R0 to get |xMouse| and
 RSBMI   R0, R0, #0                 \ flip bits 0 and 1 of R3

 CMP     R1, #0                     \ If the mouse y-coordinate in R1 is
 EORMI   R3, R3, #%00000111         \ negative, negate R1 to get |yMouse| and
 RSBMI   R1, R1, #0                 \ flip bits 0, 1 and 2 of R3

 STMFD   R13!, {R0-R1}              \ Store |xMouse| and |yMouse| on the stack

                                    \ We now divide one coordinate by the other,
                                    \ with the smaller value as the numerator,
                                    \ so the result is between 0 and 1
                                    \
                                    \ We do this so we can take the arctan of
                                    \ the result to calculate the angle in our
                                    \ polar coordinate

 CMP     R0, R1                     \ If |xMouse| < |yMouse|, flip bit 0 of R3
 EORLO   R3, R3, #%00000001         \ so we can make sure the angle is in the
                                    \ correct quadrant later

 BHS     pole2                      \ If |xMouse| >= |yMouse|, jump to pole2 to
                                    \ calculate the division with |yMouse| as
                                    \ the numerator

                                    \ If we get here then |xMouse| < |yMouse|
                                    \ so we calculate the division with |xMouse|
                                    \ as the numerator

                                    \ We now calculate R2 = R0 / R1 using the
                                    \ shift-and-subtract division algorithm

 MOV     R2, #0                     \ Set R2 = 0 to contain the result

 MOV     R14, #%10000000            \ Set bit 7 of R14 so we can shift it to the
                                    \ right in each iteration, using it as a
                                    \ counter

.pole1

 MOVS    R0, R0, LSL #1             \ Shift R0 left, moving the top bit into the
                                    \ C flag

 CMPCC   R0, R1                     \ If we shifted a 0 out of the top of R0,
                                    \ test for a possible subtraction

 SUBCS   R0, R0, R1                 \ If we shifted a 1 out of the top of R0 or
 ORRCS   R2, R2, R14                \ R0 >= R1, then do the subtraction;
                                    \
                                    \   R0 = R0 - R1
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R14 to the
                                    \ result in R2]

 MOVS    R14, R14, LSR #1           \ Shift R14 to the right, moving bit 0 into
                                    \ the C flag

 BCC     pole1                      \ Loop back until we shift the 1 out of the
                                    \ right end of R14 [after eight shifts]

 MOVS    R2, R2, LSL #24            \ Scale up the result, so now we have;
                                    \
                                    \   R2 = 2^24 * [|xMouse| / |yMouse|]

 B       pole4                      \ Jump to pole4 to skip the following

.pole2

                                    \ If we get here then |xMouse| >= |yMouse|
                                    \ so we calculate the division with |yMouse|
                                    \ as the numerator

 MOV     R2, #0                     \ Set R2 = R1 / R0 using the same algorithm
 MOV     R14, #&80                  \ as above
                                    \
                                    \ This is the first part of the algorithm

.pole3

 MOVS    R1, R1, LSL #1             \ This is the second part of the algorithm,
 CMPLO   R1, R0                     \ so now we have;
 SUBHS   R1, R1, R0                 \
 ORRHS   R2, R2, R14                \   R2 = 2^24 * [|yMouse| / |xMouse|]
 MOVS    R14, R14, LSR #1
 BLO     pole3
 MOVS    R2, R2, LSL #24

.pole4

                                    \ At this point we have calculated one of
                                    \ the following, with the smaller coordinate
                                    \ on the top of the division;
                                    \
                                    \   R2 = 2^24 * [|xMouse| / |yMouse|]
                                    \
                                    \ or;
                                    \
                                    \   R2 = 2^24 * [|yMouse| / |xMouse|]
                                    \
                                    \ So R2 is in the range 0 to 2^24

 LDR     R14, arctanTableAddr       \ Set R14 to the address of the arctan
                                    \ lookup table

 BIC     R2, R2, #&01800000         \ Clear bits 23 and 24 of R2 so when we
                                    \ shift R2 to the right by 23 places in the
                                    \ following lookup, the address is aligned
                                    \ to the words in the lookup table

 LDR     R1, [R14, R2, LSR #23]     \ Look up the arctan to get the following;
                                    \
                                    \   R1 = [[2^31 - 1] / PI] * ATAN[R2 / 128]
                                    \
                                    \ So R1 contains the smaller of the two
                                    \ angles in the triangle formed by xMouse
                                    \ and yMouse, like this [in this example,
                                    \ xMouse >= yMouse, though the ASCII art
                                    \ might not make that obvious];
                                    \
                                    \           .|
                                    \         .´ |
                                    \       .´   | y
                                    \     .´R1   |
                                    \   +´-------+
                                    \        x
                                    \
                                    \ So we now have;
                                    \
                                    \   R1 = arctan[|xMouse| / |yMouse|]
                                    \
                                    \ or;
                                    \
                                    \   R1 = arctan[|yMouse| / |xMouse|]

 TST     R3, #%00000001             \ If bit 0 of R3 is set then;
 ADDEQ   R1, R1, R3, LSL #29        \
 ADDNE   R3, R3, #1                 \    R1 = R1 + R3 << 29
 RSBNE   R1, R1, R3, LSL #29        \
                                    \ otherwise bit 0 of R3 is clear, so;
                                    \
                                    \    R1 = [R3 + 1] << 29 - R1
                                    \
                                    \ So this moves the arctan angle into the
                                    \ correct circle quadrant, depending on the
                                    \ relative sizes and signs of xMouse and
                                    \ yMouse

                                    \ We now have the angle for our polar
                                    \ coordinate in R1, so next we need to
                                    \ calculate the distance

\ ******************************************************************************
\
\       Name; GetMouseInPolarCoordinates [Part 2 of 2]
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Calculate the polar distance
\  Deep dive; Flying by mouse
\
\ ******************************************************************************

 LDMFD   R13!, {R0, R2}             \ Retrieve the values we put on the stack
                                    \ earlier, so;
                                    \
                                    \   R0 = |xMouse|
                                    \
                                    \   R2 = |yMouse|

                                    \ We now calculate R4 = R0 * R0 using the
                                    \ shift-and-add multiplication algorithm,
                                    \ which calculates;
                                    \
                                    \   R4 = R0 ^ 2
                                    \      = |xMouse| ^ 2

 MOV     R3, R0                     \ Set R3 = 2 * R0
 MOVS    R3, R3, LSL #1             \        = 2 * |xMouse|
                                    \
                                    \ So now we calculate R4 = R3 * R0 to get
                                    \ our result

 AND     R3, R3, #&FE000000         \ Zero all but the top byte of R3, ensuring
 ORR     R3, R3, #&01000000         \ that bit 0 of the top byte is set so the
                                    \ value of the fractional part is set to 0.5

 MOV     R4, #0                     \ Set R4 = 0 to use for building the sum in
                                    \ our shift-and-add multiplication result

.pole5

 MOV     R0, R0, LSR #1             \ If bit 0 of R0 is set, add R0 to the
 ADDHS   R4, R4, R0                 \ result in R4, shifting R0 to the right

 MOVS    R3, R3, LSL #1             \ Shift R3 left by one place

 BNE     pole5                      \ Loop back if R3 is non-zero

                                    \ Next we calculate R14 = R2 * R2 using the
                                    \ shift-and-add multiplication algorithm,
                                    \ which calculates;
                                    \
                                    \   R14 = R2 ^ 2
                                    \       = |yMouse| ^ 2

 MOV     R3, R2                     \ Set R3 = 2 * R2
 MOVS    R3, R3, LSL #1             \        = 2 * |yMouse|
                                    \
                                    \ So now we calculate R14 = R3 * R2 to get
                                    \ our result

 AND     R3, R3, #&FE000000         \ Zero all but the top byte of R3, ensuring
 ORR     R3, R3, #&01000000         \ that bit 0 of the top byte is set so the
                                    \ value of the top byte is at least 1

 MOV     R14, #0                    \ Set R14 = 0 to use for building the sum in
                                    \ our shift-and-add multiplication result

.pole6

 MOV     R2, R2, LSR #1             \ If bit 0 of R2 is set, add R2 to the
 ADDHS   R14, R14, R2               \ result in R14, shifting R2 to the right

 MOVS    R3, R3, LSL #1             \ Shift R3 left by one place

 BNE     pole6                      \ Loop back if R3 is non-zero

                                    \ So by this point we have the following;
                                    \
                                    \   R4 = |xMouse| ^ 2
                                    \
                                    \   R14 = |yMouse| ^ 2
                                    \
                                    \ So now we can apply Pythagoras to find the
                                    \ length of the hypotenuse, which is the
                                    \ distance in our polar coordinate, as in
                                    \ the example where x >= y;
                                    \
                                    \           .|
                                    \      R0 .´ |
                                    \       .´   | y
                                    \     .´R1   |
                                    \   +´-------+
                                    \        x
                                    \
                                    \ We calculate the distance in R0

 ADD     R2, R14, R4                \ Set R2 = R14 + R4
                                    \
                                    \        = |xMouse| ^ 2 + |yMouse| ^ 2

 LDR     R14, squareRootTableAddr   \ Set R14 to the address of the square root
                                    \ lookup table

 BIC     R2, R2, #&00300000         \ Clear bits 20 and 21 of R2 so when we
                                    \ shift R2 to the right by 20 places in the
                                    \ following lookup, the address is aligned
                                    \ to the words in the lookup table

 LDR     R0, [R14, R2, LSR #20]     \ Set R0 = SQRT[R2]
                                    \        = SQRT[|xMouse| ^ 2 + |yMouse| ^ 2]
                                    \
                                    \ which is the length of the hypotenuse and
                                    \ the distance in our polar coordinate

 LDMFD   R13!, {PC}                 \ Return from the subroutine

 LDMIA   R0, {R0-R2}                \ This instruction appears to be unused

\ ******************************************************************************
\
\       Name; ProjectParticleOntoScreen
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Project a 3D particle coordinate onto the screen
\  Deep dive; Particles and particle clouds
\             Projecting onto the screen
\
\ ------------------------------------------------------------------------------
\
\ This routine projects a 3D coordinate [x, y, z] into screen coordinates, as
\ follows;
\
\   screen x-coordinate = 160 + x / z
\
\   screen y-coordinate = 64 + y / z
\
\ The screen coordinate is deemed on-screen when it is within the following
\ valid ranges;
\
\   screen x is in the range 0 to 319
\
\   screen y is in the range 0 to 238
\
\ and is at less than a 45-degree viewing angle [i.e. within a viewing cone of
\ 45 degrees].
\
\ The particle is deemed visible if its z-coordinate is less than &80000000, and
\ is not projected if it is further away than this.
\
\ The y-coordinate is the screen height [256 pixels] minus the two text lines
\ that are reserved for the score bar at the top of the screen [16 pixels].
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates in [x, y, z]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   [R0, R1]                        Projected screen coordinates [x, y]
\
\   C flag                          C flag is set if the particle is off-screen,
\                                   clear if it is on-screen
\
\ ******************************************************************************

.ProjectParticleOntoScreen

 CMN     R2, #&80000000             \ If R2 + &80000000 produces a carry, then
 MOVCS   PC, R14                    \ the particle is too far away to be
                                    \ visible, so return from the subroutine
                                    \ with the C flag set

 MOVS    R3, R0                     \ Set R3 = |R0|
 RSBMI   R3, R3, #0                 \        = |x|

 MVNMI   R5, R3                     \ If R0 is negative, set R5 = ~R3

 MOVPL   R5, R3                     \ If R0 is positive, set R5 = R3

 MOVS    R4, R1                     \ Set R4 = |R1|
 RSBMI   R4, R4, #0                 \        = |y|

 MVNMI   R6, R4                     \ If R1 is negative, set R6 = ~R4

 MOVPL   R6, R4                     \ If R1 is positive, set R6 = R4

 ORR     R5, R5, R6                 \ Set R5 = R5 OR R6 OR R2
 ORR     R5, R5, R2                 \        = |R0| OR |R1| OR R2
 ORR     R5, R5, #1                 \        = |x| OR |y| OR z
                                    \
                                    \ And round it up so it is non-zero
                                    \
                                    \ So this sets R5 to a number that is
                                    \ greater than |x|, |y| and z, so R5 is
                                    \ therefore an upper bound on the values of
                                    \ all three coordinates

                                    \ We now work out how many times we can
                                    \ scale up R5 so that it is as large as
                                    \ possible but still fits within a 32-bit
                                    \ word

 MOV     R6, #0                     \ Set R6 = 0 to use as the scale factor in
                                    \ the following loop

.ppar1

 MOVS    R5, R5, LSL #1             \ Shift R5 to the left until the top bit is
 ADDPL   R6, R6, #1                 \ set, incrementing R6 for each shift so R6
 BPL     ppar1                      \ contains the scale factor we have applied
                                    \ to R5
                                    \
                                    \ So this scales R5 as high as possible
                                    \ while still staying within 32 bits, with
                                    \ the scale factor given in R6

 MOV     R2, R2, LSL R6             \ We now scale up the updated coordinate
 MOV     R3, R3, LSL R6             \ [|x|, |y|, z] in [R3, R4, R2] by the scale
 MOV     R4, R4, LSL R6             \ factor in R6, as we know the result will
                                    \ stay within 32-bit words

 CMP     R3, R2                     \ If R3 >= R2 or R4 >= R2, then at least one
 CMPCC   R4, R2                     \ of these is true;
 MOVCS   PC, R14                    \
                                    \   |x| >= z or |y| >= z
                                    \
                                    \ so return from the subroutine with C flag
                                    \ set to indicate the particle is off-screen
                                    \
                                    \ This is the case when the particle has a
                                    \ greater viewing angle then 45 degrees in
                                    \ either direction, in which case we don't
                                    \ draw the particle on-screen

 STMFD   R13!, {R7, R14}            \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOV     R7, R2                     \ Set R7 = R2
                                    \        = z
                                    \
                                    \ where the value of z is scaled up

                                    \ We now calculate R6 = R4 / R7 using the
                                    \ shift-and-subtract algorithm

 MOV     R6, #0                     \ Set R6 = 0 to use for building the sum in
                                    \ our shift-and-subtract division result

 MOV     R14, #&200                 \ Set bit 9 in R14 to act as our shifted
                                    \ division bit, so we populate ten bits of
                                    \ the result

.ppar2

 MOVS    R4, R4, LSL #1             \ Shift R4 left by one place

 CMPCC   R4, R7                     \ If we just shifted a zero out of R4, set
                                    \ the C flag if R4 >= R7

 SUBCS   R4, R4, R7                 \ The numerator in R4 is bigger than the
 ORRCS   R6, R6, R14                \ denominator in R7, so subtract the
                                    \ denominator from the numerator and set the
                                    \ corresponding bit in the result

 MOVS    R14, R14, LSR #1           \ Shift R14 right by one place, shifting bit
                                    \ 0 into the C flag

 BCC     ppar2                      \ Loop back until we shift a 1 out of R14

 MOVS    R6, R6, LSL #22            \ Shift the result in R6 left as far as
                                    \ possible so it still fits in to 32 bits
                                    \ [as the result contains ten bits], so now
                                    \ we have;
                                    \
                                    \   R6 = R4 / R7
                                    \      = |y| / z
                                    \
                                    \ where |y| and z are both scaled up by the
                                    \ same factor

 MOV     R5, #0                     \ Set R5 = R3 / R2 using the same algorithm
 MOV     R14, #&200                 \ as above
                                    \
                                    \ This is the first part of the algorithm

.ppar3

 MOVS    R3, R3, LSL #1             \ This is the second part of the algorithm,
 CMPCC   R3, R2                     \ so we now have;
 SUBCS   R3, R3, R2                 \
 ORRCS   R5, R5, R14                \    R5 = R3 / R2
 MOVS    R14, R14, LSR #1           \       = |x| / z
 BCC     ppar3                      \ 
 MOVS    R5, R5, LSL #22            \ where |x| and z are both scaled up by the
                                    \ same factor

 MOV     R5, R5, LSR #24            \ Shift both division results down so we
 MOV     R6, R6, LSR #24            \ only keep the top byte of each result

 TEQ     R0, #0                     \ If the original R0 argument was positive,
 ADDPL   R0, R5, #160               \ set;
 RSBMI   R0, R5, #160               \
                                    \   R0 = 160 + R5
                                    \      = 160 + |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R0 = 160 - R5
                                    \      = 160 - |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ So this sets R0 as follows, where x and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R0 = 160 + x / z

 TEQ     R1, #0                     \ If the original R1 argument was positive,
 ADDPL   R1, R6, #64                \ set;
 RSBMI   R1, R6, #64                \ 
                                    \   R1 = 64 + R6
                                    \      = 64 + |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R1 = 64 - R6
                                    \      = 64 - |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ So this sets R1 as follows, where y and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R1 = 64 + y / z

 CMP     R0, #320                   \ Set the C flag if either of these is true;
 CMPCC   R1, #239                   \
                                    \   screen x-coordinate in R0 >= 320
                                    \
                                    \   screen y-coordinate in R1 >= 239
                                    \
                                    \ to indicate that the particle is
                                    \ off-screen

 LDMFD   R13!, {R7, PC}             \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; ProjectVertexOntoScreen
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Project a vertex coordinate from a 3D object onto the screen
\  Deep dive; Drawing 3D objects
\             Drawing the landscape
\             Projecting onto the screen
\
\ ------------------------------------------------------------------------------
\
\ This routine projects a 3D coordinate [x, y, z] into screen coordinates, as
\ follows;
\
\   screen x-coordinate = 160 + x / z
\
\   screen y-coordinate = 64 + y / z
\
\ The vertex is deemed visible if its z-coordinate is less than &80000000, and
\ is not projected if it is further away than this.
\
\ This routine differs from the particle projection routine in that it projects
\ vertices irrespective of their viewing angle.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1, R2]                    Particle coordinates in [x, y, z]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   [R0, R1]                        Projected screen coordinates [x, y]
\
\   C flag                          C flag is set if the vertex is too far away
\                                   to draw, clear if it is close enough
\
\ ******************************************************************************

.ProjectVertexOntoScreen

 LDMIA   R0, {R0-R2}                \ Fetch the coordinates we want to project
                                    \ from [R0, R1, R2], which we'll call
                                    \ [x, y, z] in the following

 CMN     R2, #&80000000             \ If R2 + &80000000 produces a carry, then
 MOVCS   PC, R14                    \ the vertex is too far away to be visible,
                                    \ so return from the subroutine with the
                                    \ C flag set

 STMFD   R13!, {R5-R7, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOVS    R3, R0                     \ Set R3 = |R0|
 RSBMI   R3, R3, #0                 \        = |x|

 MVNMI   R5, R3                     \ If R0 is negative, set R5 = ~R3

 MOVPL   R5, R3                     \ If R0 is positive, set R5 = R3

 MOVS    R4, R1                     \ Set R4 = |R1|
 RSBMI   R4, R4, #0                 \        = |y|

 MVNMI   R6, R4                     \ If R1 is negative, set R6 = ~R4

 MOVPL   R6, R4                     \ If R1 is positive, set R6 = R4

 ORR     R5, R5, R6                 \ Set R5 = R5 OR R6 OR R2
 ORR     R5, R5, R2                 \        = |R0| OR |R1| OR R2
 ORR     R5, R5, #1                 \        = |x| OR |y| OR z
                                    \
                                    \ And round it up so it is non-zero
                                    \
                                    \ So this sets R5 to a number that is
                                    \ greater than |x|, |y| and z, so R5 is
                                    \ therefore an upper bound on the values of
                                    \ all three coordinates

                                    \ We now work out how many times we can
                                    \ scale up R5 so that it is as large as
                                    \ possible but still fits within a 32-bit
                                    \ word

 MOV     R6, #0                     \ Set R6 = 0 to use as the scale factor in
                                    \ the following loop

.pver1

 MOVS    R5, R5, LSL #1             \ Shift R5 to the left until the top bit is
 ADDPL   R6, R6, #1                 \ set, incrementing R6 for each shift so R6
 BPL     pver1                      \ contains the scale factor we have applied
                                    \ to R5
                                    \
                                    \ So this scales R5 as high as possible
                                    \ while still staying within 32 bits, with
                                    \ the scale factor given in R6

 MOV     R2, R2, LSL R6             \ We now scale up the updated coordinate
 MOV     R3, R3, LSL R6             \ [|x|, |y|, z] in [R3, R4, R2] by the scale
 MOV     R4, R4, LSL R6             \ factor in R6, as we know the result will
                                    \ stay within 32-bit words

 CMP     R2, R3                     \ If R2 < R3 or R2 < R4, then at least one
 CMPHS   R2, R4                     \ of these is true;
 BLO     pver4                      \
                                    \   |x| > z or |y| > z
                                    \
                                    \ so jump to pver4 to deal with this special
                                    \ case, when the vertex has a greater
                                    \ viewing angle then 45 degrees in either
                                    \ direction

 MOV     R7, R2                     \ Set R7 = R2
                                    \        = z
                                    \
                                    \ where the value of z is scaled up

                                    \ We now calculate R6 = R4 / R7 using the
                                    \ shift-and-subtract algorithm

 MOV     R6, #0                     \ Set R6 = 0 to use for building the sum in
                                    \ our shift-and-subtract division result

 MOV     R14, #&100                 \ Set bit 8 in R14 to act as our shifted
                                    \ division bit, so we populate nine bits of
                                    \ the result

.pver2

 MOVS    R4, R4, LSL #1             \ Shift R4 left by one place

 CMPCC   R4, R7                     \ If we just shifted a zero out of R4, set
                                    \ the C flag if R4 >= R7

 SUBCS   R4, R4, R7                 \ The numerator in R4 is bigger than the
 ORRCS   R6, R6, R14                \ denominator in R7, so subtract the
                                    \ denominator from the numerator and set the
                                    \ corresponding bit in the result

 MOVS    R14, R14, LSR #1           \ Shift R14 right by one place, shifting bit
                                    \ 0 into the C flag

 BCC     pver2                      \ Loop back until we shift a 1 out of R14

 MOVS    R6, R6, LSL #23            \ Shift the result in R6 left as far as
                                    \ possible so it still fits in to 32 bits,
                                    \ [as the result contains nine bits], so
                                    \ now we have;
                                    \
                                    \   R6 = R4 / R7
                                    \      = |y| / z
                                    \
                                    \ where |y| and z are both scaled up by the
                                    \ same factor

 MOV     R5, #0                     \ Set R5 = R3 / R2 using the same algorithm
 MOV     R14, #&200                 \ as above, but populating ten bits in the
                                    \ result
                                    \
                                    \ This is the first part of the algorithm

.pver3

 MOVS    R3, R3, LSL #1             \ This is the second part of the algorithm,
 CMPCC   R3, R2                     \ so we now have;
 SUBCS   R3, R3, R2                 \
 ORRCS   R5, R5, R14                \    R5 = R3 / R2
 MOVS    R14, R14, LSR #1           \       = |x| / z
 BCC     pver3                      \ 
 MOVS    R5, R5, LSL #22            \ where |x| and z are both scaled up by the
                                    \ same factor

 MOV     R5, R5, LSR #24            \ Shift both division results down so we
 MOV     R6, R6, LSR #24            \ only keep the top byte of each result

 TEQ     R0, #0                     \ If the original R0 argument was positive,
 ADDPL   R0, R5, #160               \ set;
 RSBMI   R0, R5, #160               \
                                    \   R0 = 160 + R5
                                    \      = 160 + |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R0 = 160 - R5
                                    \      = 160 - |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ So this sets R0 as follows, where x and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R0 = 160 + x / z

 TEQ     R1, #0                     \ If the original R1 argument was positive,
 ADDPL   R1, R6, #64                \ set;
 RSBMI   R1, R6, #64                \ 
                                    \   R1 = 64 + R6
                                    \      = 64 + |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R1 = 64 - R6
                                    \      = 64 - |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ So this sets R1 as follows, where y and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R1 = 64 + y / z

 CMN     R0, #0                     \ Clear the C flag to indicate that the
                                    \ vertex is on-screen [this CMN instruction
                                    \ is a hacky way of clearing the C flag]

 LDMFD   R13!, {R5-R7, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

.pver4

                                    \ If we get here then;
                                    \
                                    \   |x| > z or |y| > z
                                    \
                                    \ So the vertex has a greater viewing angle
                                    \ then 45 degrees in either direction
                                    \
                                    \ The following registers are also set;
                                    \
                                    \   [R3, R4, R2] = [|x|, |y|, z]

 MOV     R5, #24                    \ Set R5 = 24

 CMP     R3, R4                     \ Set R6 = R3 / 2
 MOVHS   R6, R3, LSR #1             \        = |x| / 2
 MOVLO   R6, R3, LSR #1             \
                                    \ The comparison seems to be unnecessary as
                                    \ both conditions do the same thing [if the
                                    \ shifts used the C flag then this would
                                    \ make more sense, but LSR overwrites the C
                                    \ flag rather than using it]

.pver5

 CMP     R2, R6                     \ Shift R6 to the right and increment R5
 ADDLO   R5, R5, #1                 \ until R2 >= R6, so this scales R6 down,
 MOV     R6, R6, LSR #1             \ counting the scale factor in R5
 BLO     pver5                      \
                                    \ Specifically, this scales |x| / 2 down
                                    \ until it is smaller than z

 MOV     R2, R2, LSR #23            \ We now scale down the updated coordinate
 MOV     R3, R3, LSR R5             \ [|x|, |y|, z] in [R3, R4, R2] by the scale
 MOV     R4, R4, LSR R5             \ factor in R5 [for x and y] and by 23
                                    \ places [for z]

 MOV     R7, R2                     \ Set R7 = R2

 STMFD   R13!, {R5}                 \ Store the scale factor in R5 on the stack

                                    \ We now calculate R6 = R4 / R7 using the
                                    \ shift-and-subtract algorithm, scaling both
                                    \ the numerator and denominator up by 24
                                    \ places before doing the division

 MOV     R6, #0                     \ Set R6 = 0 to use for building the sum in
                                    \ our shift-and-subtract division result

 MOV     R14, #&80                  \ Set bit 7 in R14 to act as our shifted
                                    \ division bit, so we populate eight bits of
                                    \ the result

 MOV     R4, R4, LSL #24            \ Scale the numerator and denominator up by
 MOV     R7, R7, LSL #24            \ 24 places to ensure more accuracy

.pver6

 MOVS    R4, R4, LSL #1             \ Shift R4 left by one place

 CMPCC   R4, R7                     \ If we just shifted a zero out of R4, set
                                    \ the C flag if R4 >= R7

 SUBCS   R4, R4, R7                 \ The numerator in R4 is bigger than the
 ORRCS   R6, R6, R14                \ denominator in R7, so subtract the
                                    \ denominator from the numerator and set the
                                    \ corresponding bit in the result

 MOVS    R14, R14, LSR #1           \ Shift R14 right by one place, shifting bit
                                    \ 0 into the C flag

 BCC     pver6                      \ Loop back until we shift a 1 out of R14

 MOVS    R6, R6, LSL #24            \ Shift the result in R6 left as far as
                                    \ possible so it still fits in to 32 bits,
                                    \ [as the result contains eight bits], so
                                    \ now we have;
                                    \
                                    \   R6 = R4 / R7
                                    \      = |y| / z
                                    \
                                    \ where |y| and z are both scaled up by the
                                    \ same factor

 MOV     R5, #0                     \ Set R5 = R3 / R2 using the same algorithm
 MOV     R14, #&200                 \ as above, but populating ten bits in the
 MOV     R3, R3, LSL #22            \ result
 MOV     R2, R2, LSL #22            \
                                    \ This is the first part of the algorithm

.pver7

 MOVS    R3, R3, LSL #1             \ This is the second part of the algorithm,
 CMPCC   R3, R2                     \ so we now have;
 SUBCS   R3, R3, R2                 \
 ORRCS   R5, R5, R14                \    R5 = R3 / R2
 MOVS    R14, R14, LSR #1           \       = |x| / z
 BCC     pver7                      \ 
 MOVS    R5, R5, LSL #22            \ where |x| and z are both scaled up by the
                                    \ same factor

 LDMFD   R13!, {R14}                \ Fetch the scale factor that we stored on
                                    \ the stack into R14

 SUB     R14, R14, #23              \ I have no idea what this does, as it
 RSB     R14, R14, #23              \ appears to have no effect at all

 MOV     R5, R5, LSR R14            \ Apply the scale factor in R14 to R5

 MOV     R6, R6, LSR R14            \ Apply the scale factor in R14+1 to R6
 MOV     R6, R6, LSR #1

 TEQ     R0, #0                     \ If the original R0 argument was positive,
 ADDPL   R0, R5, #160               \ set;
 RSBMI   R0, R5, #160               \
                                    \   R0 = 160 + R5
                                    \      = 160 + |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R0 = 160 - R5
                                    \      = 160 - |x| / z
                                    \      = 160 + x / z
                                    \
                                    \ So this sets R0 as follows, where x and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R0 = 160 + x / z

 TEQ     R1, #0                     \ If the original R1 argument was positive,
 ADDPL   R1, R6, #64                \ set;
 RSBMI   R1, R6, #64                \ 
                                    \   R1 = 64 + R6
                                    \      = 64 + |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ otherwise set;
                                    \ 
                                    \   R1 = 64 - R6
                                    \      = 64 - |y| / z
                                    \      = 64 + y / z
                                    \
                                    \ So this sets R1 as follows, where y and z
                                    \ are the original particle's coordinates;
                                    \
                                    \   R1 = 64 + y / z

 CMN     R0, #0                     \ Clear the C flag to indicate that the
                                    \ vertex is on-screen [this CMN instruction
                                    \ is a hacky way of clearing the C flag]

 LDMFD   R13!, {R5-R7, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; AddVectors
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; An unused routine that adds two vectors and uses self-modifying
\             code to store the results in a specified location
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.vmod1

 ADD     R4, R11, #xVertexRotated   \ Modification code to store the result of
                                    \ the vector addition in xVertexRotated

.vmod2

 ADD     R4, R11, #xCoord           \ Modification code to restore the result of
                                    \ the vector addition to xCoord

.vmod3

 MOV     R4, R12                    \ Modification code to store the result of
                                    \ the vector addition in R12

.vmod4

 ADD     R4, R11, #xLandscapeCol    \ Modification code to store the result of
                                    \ the vector addition in xLandscapeCol

.vmod5

 ADD     R4, R11, #xLandscapeRow    \ Modification code to store the result of
                                    \ the vector addition in xLandscapeRow

.AddVectors

 STMFD   R13!, {R14}                \ Store the return address on the stack

                                    \ We now modify the following instruction in
                                    \ the AddVectorToVertices routine to change
                                    \ the way it works;
                                    \
                                    \   ADD R3, R11, #xCoord
                                    \
                                    \ Interestingly, though, none of the
                                    \ modifications above will work properly, as
                                    \ they all change R4 rather than R3, so
                                    \ presumably the modified subroutine was
                                    \ refactored and this unused routine wasn't
                                    \ updated
                                    \
                                    \ If this routine worked, it would use the
                                    \ modifications to change the address where
                                    \ we stored the result

 LDR     R1, vmod3                  \ Modify the eighth instruction in the
 STR     R1, AddVectorToVertices+28 \ AddVectorToVertices routine to the
                                    \ instruction in vmod3

 LDMIA   R0, {R0-R2}                \ Fetch the coordinate from R0 into
                                    \ [R0, R1, R2]

 MOV     R3, R12                    \ Set R3 = R12 to use as the address of the
                                    \ second vector to add

 BL      AddVectorToVertices+8      \ Add the vector [R0, R1, R2] to the vector
                                    \ at the address in R3 [by skipping the
                                    \ first two instructions in the modified
                                    \ routine

 LDR     R1, vmod2                  \ Revert the eighth instruction in the
 STR     R1, AddVectorToVertices+28 \ AddVectorToVertices routine to the
                                    \ instruction in vmod2 [though note that
                                    \ this doesn't actually revert the
                                    \ instruction, it corrupts it, for the
                                    \ reasons noted above]

 LDMFD   R13!, {PC}                 \ Return from the subroutine

\ ******************************************************************************
\
\       Name; AddVectorToVertices
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; Add a vector to the rotated vertex coordinates to get the vertex
\             coordinates in 3D space
\
\ ------------------------------------------------------------------------------
\
\ Calculate the following;
\
\   [ xCoord ]   [ R0   ] + [ xVertexRotated ]
\   [ yCoord ] = [ R0+4 ] + [ yVertexRotated ]
\   [ zCoord ]   [ R0+8 ] + [ zVertexRotated ]
\
\ This routine is only ever called with R0 = xObject, so this adds the rotated
\ vertex coordinate to the object's coordinate, giving the vertex's coordinate
\ in the game's coordinate system.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The address of the 3D vector to add in the
\                                   above calculation
\
\ ------------------------------------------------------------------------------
\
\ Other entry points;
\
\   AddVectorToVertices+8           Enter the routine with R0 to R3 already set
\
\ ******************************************************************************

.AddVectorToVertices

 LDMIA   R0, {R0-R2}                \ Fetch the coordinate from R0 into
                                    \ [R0, R1, R2]

 ADD     R3, R11, #xVertexRotated   \ Set R3 to the address of the coordinates
                                    \ in xVertexRotated

 STMFD   R13!, {R5-R6}              \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDMIA   R3, {R3-R5}                \ Fetch the vector at xVertexRotated into
                                    \ [R3, R4, R5]

 ADD     R0, R0, R3                 \ Set R0 = R0 + R3
                                    \        = R0 + xVertexRotated

 ADD     R1, R1, R4                 \ Set R1 = R1 + R4
                                    \        = R0+4 + yVertexRotated

 ADD     R2, R2, R5                 \ Set R2 = R2 + R5
                                    \        = R0+8 + zVertexRotated

 ADD     R3, R11, #xCoord           \ Store [R0, R1, R2] in [xCoord, yCoord,
 STMIA   R3, {R0-R2}                \ zCoord]

 LDMFD   R13!, {R5-R6}              \ Retrieve the registers that we stored on
                                    \ the stack

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; MultiplyVectorByConstant
\       Type; Subroutine
\   Category; Maths [Geometry]
\    Summary; An unused routine that multiplies a vector by a constant value
\  Deep dive; Unused code in Lander
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The constant value to multiply the vector by
\
\   R1                              The address of the three-word vector, plus a
\                                   counter word
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   xVertexRotated                  The result of the multiplication, followed
\                                   by the incremented counter 
\
\ ******************************************************************************

.MultiplyVectorByConstant

 STMFD   R13!, {R5-R8, R14}         \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDMIA   R1, {R1-R3, R14}           \ Fetch R1, R2, R3 and R14 from address R1

 MOV     R8, R0                     \ Set R8 = R0

                                    \ We now calculate R5 = R8 * R1 using the
                                    \ shift-and-add multiplication algorithm

 EOR     R4, R8, R1                 \ Set the sign of the result in R4

 TEQ     R8, #0                     \ Set R8 = 4 * |R8|
 RSBMI   R8, R8, #0
 MOVS    R8, R8, LSL #2

 TEQ     R1, #0                     \ Set R1 = 2 * |R1|
 RSBMI   R1, R1, #0
 MOV     R1, R1, LSL #1

 AND     R8, R8, #&FE000000         \ Zero all but the top byte of R8, ensuring
 ORR     R8, R8, #&01000000         \ that bit 0 of the top byte is set so the
                                    \ value of the fractional part is set to 0.5

 MOV     R5, #0                     \ Set R5 = 0 to use for building the sum in
                                    \ our shift-and-add multiplication result

.vcon1

 MOV     R1, R1, LSR #1             \ If bit 0 of R1 is set, add R1 to the
 ADDCS   R5, R5, R1                 \ result in R5, shifting R1 to the right

 MOVS    R8, R8, LSL #1             \ Shift R8 left by one place

 BNE     vcon1                      \ Loop back if R8 is non-zero

 MOV     R5, R5, LSR #1             \ Set R5 = R5 / 2

 TEQ     R4, #0                     \ Apply the sign from R4 to R5 to get the
 RSBMI   R5, R5, #0                 \ final result, so;
                                    \
                                    \   R5 = R8 * R1
                                    \      = R0 * R1

 MOV     R8, R0                     \ Set R8 = R0

 EOR     R4, R8, R2                 \ Set R6 = R8 * R2 using the same algorithm
 TEQ     R8, #0                     \ as above
 RSBMI   R8, R8, #0                 \
 MOVS    R8, R8, LSL #2             \ This is the first part of the algorithm
 TEQ     R2, #0
 RSBMI   R2, R2, #0
 MOV     R2, R2, LSL #1
 AND     R8, R8, #&FE000000
 ORR     R8, R8, #&01000000
 MOV     R6, #0

.vcon2

 MOV     R2, R2, LSR #1             \ This is the second part of the algorithm,
 ADDCS   R6, R6, R2                 \ so we now have;
 MOVS    R8, R8, LSL #1             \
 BNE     vcon2                      \ R4 = R8 * R2
 MOV     R6, R6, LSR #1             \    = R0 * R2
 TEQ     R4, #0
 RSBMI   R6, R6, #0

 MOV     R8, R0                     \ Set R8 = R0

 EOR     R4, R8, R3                 \ Set R7 = R8 * R3 using the same algorithm
 TEQ     R8, #0                     \ as above
 RSBMI   R8, R8, #0                 \
 MOVS    R8, R8, LSL #2             \ This is the first part of the algorithm
 TEQ     R3, #0
 RSBMI   R3, R3, #0
 MOV     R3, R3, LSL #1
 AND     R8, R8, #&FE000000
 ORR     R8, R8, #&01000000
 MOV     R7, #0

.vcon3

 MOV     R3, R3, LSR #1             \ This is the second part of the algorithm,
 ADDCS   R7, R7, R3                 \ so we now have;
 MOVS    R8, R8, LSL #1             \
 BNE     vcon3                      \   R7 = R8 * R3
 MOV     R7, R7, LSR #1             \      = R0 * R3
 TEQ     R4, #0
 RSBMI   R7, R7, #0

 ADD     R14, R14, #2               \ Set R14 = R14 + 2

 ADD     R8, R11, #xVertexRotated   \ Store [R5, R6, R7] and the updated R14 in
 STMIA   R8, {R5-R7, R14}           \ xVertexRotated

 LDMFD   R13!, {R5-R8, PC}          \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; randomSeed1
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; The first random seed for the random number generator
\
\ ******************************************************************************

.randomSeed1

 EQUD    &4F9C3490

\ ******************************************************************************
\
\       Name; randomSeed2
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; The second random seed for the random number generator
\
\ ******************************************************************************

.randomSeed2

 EQUD    &DA0383CF

\ ******************************************************************************
\
\       Name; GetRandomNumbers
\       Type; Subroutine
\   Category; Maths [Arithmetic]
\    Summary; Generate pseudo-random numbers from the random number seeds
\  Deep dive; Random numbers
\
\ ------------------------------------------------------------------------------
\
\ This algorithm appears in the original 1986 ARM Assembler manual that came
\ with Acorn's ARM Evaluation System [it's in section 11.2 on page 96]. It also
\ appears in later Acorn manuals, such as Acorn Desktop Assembler [release 2],
\ where it's on page 186.
\
\ Here's the algorithm's description from the manual;
\
\ 'It is often necessary to generate [pseudo-]random numbers and the most
\ efficient algorithms are based on shift generators with exclusive-or feedback
\ rather like a cyclic redundancy check generator. Unfortunately the sequence of
\ a 32-bit generator needs more than one feedback tap to be maximal length [that
\ is 2^32-1 cycles before repetition]. A 33-bit shift generator with taps at
\ bits 20 and 33 is required. The basic algorithm is newbit ;= bit33 eor bit20,
\ shift left the 33 bit number and put in newbit at the bottom. Then do this for
\ all the newbits needed, that is 32 of them. Luckily this can all be done in
\ five S cycles.'
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R0                              A random number
\
\   R1                              A random number
\
\ ******************************************************************************

.GetRandomNumbers

 STMFD   R13!, {R14}                \ Store the return address on the stack

 LDR     R0, randomSeed1            \ Set R0 = randomSeed1

 LDR     R1, randomSeed2            \ Set R1 = randomSeed2

 TST     R1, R1, LSR #1             \ Set flags on R1 AND [R1 >> 1]

 MOVS    R14, R0, RRX               \ Rotate R0 right, incorporating the C flag,
                                    \ and store the result in R14, replacing the
                                    \ C flag with bit 0 from R0

 ADC     R1, R1, R1                 \ R1 = R1 + R1 + C

 EOR     R14, R14, R0, LSL #12      \ R14 = R14 EOR [R0 << 12]

 EOR     R0, R14, R14, LSR #20      \ R0 = R14 EOR [R14 >> 20]

 STR     R1, randomSeed1            \ Set randomSeed1 = R1

 STR     R0, randomSeed2            \ Set randomSeed2 = R0

 LDMFD   R13!, {PC}                 \ Return from the subroutine

\ ******************************************************************************
\
\       Name; screenAddr
\       Type; Variable
\   Category; Drawing the screen
\    Summary; The screen address for the start of the 17th pixel line in the
\             current bank [i.e. the line just below the two rows of text]
\  Deep dive; Screen memory in the Archimedes
\
\ ******************************************************************************

.screenAddr

 EQUD    &01FD8000 + 16 * 320

\ ******************************************************************************
\
\       Name; greyColourWords
\       Type; Variable
\   Category; Drawing the screen
\    Summary; An unused table of grey four-pixel colour words
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.greyColourWords

 EQUD    &00000000
 EQUD    &01010101
 EQUD    &02020202
 EQUD    &03030303
 EQUD    &2C2C2C2C
 EQUD    &2D2D2D2D
 EQUD    &2E2E2E2E
 EQUD    &2F2F2F2F
 EQUD    &D0D0D0D0
 EQUD    &D1D1D1D1
 EQUD    &D2D2D2D2
 EQUD    &D3D3D3D3
 EQUD    &FCFCFCFC
 EQUD    &FDFDFDFD
 EQUD    &FEFEFEFE
 EQUD    &FFFFFFFF
 EQUD    &FFFFFFFF
 EQUD    &FFFFFFFF
 EQUD    &FFFFFFFF
 EQUD    &FFFFFFFF

\ ******************************************************************************
\
\       Name; greyColourWordsAddr
\       Type; Variable
\   Category; Drawing the screen
\    Summary; The unused address of the unused table of grey four-pixel colour
\             words
\  Deep dive; Unused code in Lander
\
\ ******************************************************************************

.greyColourWordsAddr

 EQUD    greyColourWords

\ ******************************************************************************
\
\       Name; Draw1x1ParticleFromBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Process the 'draw 1x1-pixel specified' command from the graphics
\             buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ The 32-bit parameter word for this command is composed as follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]  
\
\ This command draws a 1x1-pixel particle at the given coordinates in the
\ specified colour.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the parameter word in the
\                                   graphics buffer
\
\   R12                             The address of the screen bank we are
\                                   drawing in, pointing just below the two
\                                   lines of text at the top of the screen,
\                                   as set in screenAddr
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\ ******************************************************************************

.Draw1x1ParticleFromBuffer

 LDR     R1, [R9], #4               \ Fetch the parameter word from the graphics
                                    \ buffer into R1, incrementing R9 to point
                                    \ to the next command in the buffer

 MOV     R0, R1, LSR #20            \ Set R0 to bits #20-31 of the parameter
                                    \ word, to extract the pixel x-coordinate

 MOV     R7, R1, LSR #12            \ Set R7 to bits #12-31 of the parameter
                                    \ word to extract the colour number, which
                                    \ we poke into memory as a byte containing
                                    \ bits #12-19

 AND     R1, R1, #&FF               \ Set R1 to bits #0-7 of the parameter word,
                                    \ to extract the pixel y-coordinate

 ADD     R3, R12, R0                \ Set R3 = R12 + R0
                                    \        = screenAddr + x-coordinate

 ADD     R1, R1, R1, LSL #2         \ Set R3 = R3 + [[R1 + R1 << 2] << 6]
 ADD     R3, R3, R1, LSL #6         \        = R3 + [[R1 + 4 * R1] * 64]
                                    \        = R3 + 320 * R1
                                    \        = screenAddr + x-coordinate
                                    \          + 320 * y-coordinate
                                    \
                                    \ So R3 contains the screen address of the
                                    \ coordinate in screen memory

 STRB    R7, [R3]                   \ Draw a 1x1-pixel particle in the colour
                                    \ specified in R7

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; Draw2x1ParticleFromBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Process the 'draw 2x1-pixel particle' command from the graphics
\             buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ The 32-bit parameter word for this command is composed as follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]
\
\ This command draws a 2x1-pixel particle at the given coordinates in the
\ specified colour, with the coordinates specifying the left end of the
\ particle.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the parameter word in the
\                                   graphics buffer
\
\   R12                             The address of the screen bank we are
\                                   drawing in, pointing just below the two
\                                   lines of text at the top of the screen,
\                                   as set in screenAddr
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\ ******************************************************************************

.Draw2x1ParticleFromBuffer

 LDR     R1, [R9], #4               \ Fetch the parameter word from the graphics
                                    \ buffer into R1, incrementing R9 to point
                                    \ to the next command in the buffer

 MOV     R0, R1, LSR #20            \ Set R0 to bits #20-31 of the parameter
                                    \ word, to extract the pixel x-coordinate

 MOV     R7, R1, LSR #12            \ Set R7 to bits #12-31 of the parameter
                                    \ word to extract the colour number, which
                                    \ we poke into memory as a byte containing
                                    \ bits #12-19

 AND     R1, R1, #&FF               \ Set R1 to bits #0-7 of the parameter word,
                                    \ to extract the pixel y-coordinate

 ADD     R3, R12, R0                \ Set R3 = R12 + R0
                                    \        = screenAddr + x-coordinate

 ADD     R1, R1, R1, LSL #2         \ Set R3 = R3 + [[R1 + R1 << 2] << 6]
 ADD     R3, R3, R1, LSL #6         \        = R3 + [[R1 + 4 * R1] * 64]
                                    \        = R3 + 320 * R1
                                    \        = screenAddr + x-coordinate
                                    \          + 320 * y-coordinate
                                    \
                                    \ So R3 contains the screen address of the
                                    \ coordinate in screen memory

 STRB    R7, [R3]                   \ Draw a 2x1-pixel particle in the colour
 STRB    R7, [R3, #1]               \ specified in R7

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; Draw2x2ParticleFromBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Process the 'draw 2x2-pixel particle' command from the graphics
\             buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ The 32-bit parameter word for this command is composed as follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]  
\
\ This command draws a 2x2-pixel particle at the given coordinates in the
\ specified colour, with the coordinates specifying the top-left corner of the
\ particle.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the parameter word in the
\                                   graphics buffer
\
\   R12                             The address of the screen bank we are
\                                   drawing in, pointing just below the two
\                                   lines of text at the top of the screen,
\                                   as set in screenAddr
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\ ******************************************************************************

.Draw2x2ParticleFromBuffer

 LDR     R1, [R9], #4               \ Fetch the parameter word from the graphics
                                    \ buffer into R1, incrementing R9 to point
                                    \ to the next command in the buffer

 MOV     R0, R1, LSR #20            \ Set R0 to bits #20-31 of the parameter
                                    \ word, to extract the pixel x-coordinate

 MOV     R7, R1, LSR #12            \ Set R7 to bits #12-31 of the parameter
                                    \ word to extract the colour number, which
                                    \ we poke into memory as a byte containing
                                    \ bits #12-19

 AND     R1, R1, #&FF               \ Set R1 to bits #0-7 of the parameter word,
                                    \ to extract the pixel y-coordinate

 ADD     R3, R12, R0                \ Set R3 = R12 + R0
                                    \        = screenAddr + x-coordinate

 ADD     R1, R1, R1, LSL #2         \ Set R3 = R3 + [[R1 + R1 << 2] << 6]
 ADD     R3, R3, R1, LSL #6         \        = R3 + [[R1 + 4 * R1] * 64]
                                    \        = R3 + 320 * R1
                                    \        = screenAddr + x-coordinate
                                    \          + 320 * y-coordinate
                                    \
                                    \ So R3 contains the screen address of the
                                    \ coordinate in screen memory

 STRB    R7, [R3]                   \ Draw a 2x2-pixel particle in the colour
 STRB    R7, [R3, #1]               \ specified in R7
 STRB    R7, [R3, #320]
 STRB    R7, [R3, #320+1]

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; Draw3x2ParticleFromBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Process the 'draw 3x2-pixel particle' command from the graphics
\             buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ The 32-bit parameter word for this command is composed as follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]  
\
\ This command draws a 3x2-pixel particle at the given coordinates in the
\ specified colour, with the coordinates specifying the pixel in the
\ bottom-middle of the particle.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the parameter word in the
\                                   graphics buffer
\
\   R12                             The address of the screen bank we are
\                                   drawing in, pointing just below the two
\                                   lines of text at the top of the screen,
\                                   as set in screenAddr
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\ ******************************************************************************

.Draw3x2ParticleFromBuffer

 LDR     R1, [R9], #4               \ Fetch the parameter word from the graphics
                                    \ buffer into R1, incrementing R9 to point
                                    \ to the next command in the buffer

 MOV     R0, R1, LSR #20            \ Set R0 to bits #20-31 of the parameter
                                    \ word, to extract the pixel x-coordinate

 MOV     R7, R1, LSR #12            \ Set R7 to bits #12-31 of the parameter
                                    \ word to extract the colour number, which
                                    \ we poke into memory as a byte containing
                                    \ bits #12-19

 AND     R1, R1, #&FF               \ Set R1 to bits #0-7 of the parameter word,
                                    \ to extract the pixel y-coordinate

 ADD     R3, R12, R0                \ Set R3 = R12 + R0
                                    \        = screenAddr + x-coordinate

 ADD     R1, R1, R1, LSL #2         \ Set R3 = R3 + [[R1 + R1 << 2] << 6]
 ADD     R3, R3, R1, LSL #6         \        = R3 + [[R1 + 4 * R1] * 64]
                                    \        = R3 + 320 * R1
                                    \        = screenAddr + x-coordinate
                                    \          + 320 * y-coordinate
                                    \
                                    \ So R3 contains the screen address of the
                                    \ coordinate in screen memory

 STRB    R7, [R3, #-1]              \ Draw a 3x2-pixel particle in the colour
 STRB    R7, [R3]                   \ specified in R7
 STRB    R7, [R3, #1]
 STRB    R7, [R3, #320-1]
 STRB    R7, [R3, #320]
 STRB    R7, [R3, #320+1]

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; Draw3x1ParticleFromBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Process the 'draw 3x1-pixel particle' command from the graphics
\             buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ The 32-bit parameter word for this command is composed as follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]  
\
\ This command draws a 3x1-pixel particle at the given coordinates in the
\ specified colour, with the coordinates specifying the pixel in the middle of
\ the particle.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the parameter word in the
\                                   graphics buffer
\
\   R12                             The address of the screen bank we are
\                                   drawing in, pointing just below the two
\                                   lines of text at the top of the screen,
\                                   as set in screenAddr
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\ ******************************************************************************

.Draw3x1ParticleFromBuffer

 LDR     R1, [R9], #4               \ Fetch the parameter word from the graphics
                                    \ buffer into R1, incrementing R9 to point
                                    \ to the next command in the buffer

 MOV     R0, R1, LSR #20            \ Set R0 to bits #20-31 of the parameter
                                    \ word, to extract the pixel x-coordinate

 MOV     R7, R1, LSR #12            \ Set R7 to bits #12-31 of the parameter
                                    \ word to extract the colour number, which
                                    \ we poke into memory as a byte containing
                                    \ bits #12-19

 AND     R1, R1, #&FF               \ Set R1 to bits #0-7 of the parameter word,
                                    \ to extract the pixel y-coordinate

 ADD     R3, R12, R0                \ Set R3 = R12 + R0
                                    \        = screenAddr + x-coordinate

 ADD     R1, R1, R1, LSL #2         \ Set R3 = R3 + [[R1 + R1 << 2] << 6]
 ADD     R3, R3, R1, LSL #6         \        = R3 + [[R1 + 4 * R1] * 64]
                                    \        = R3 + 320 * R1
                                    \        = screenAddr + x-coordinate
                                    \          + 320 * y-coordinate
                                    \
                                    \ So R3 contains the screen address of the
                                    \ coordinate in screen memory

 STRB    R7, [R3, #-1]              \ Draw a 3x1-pixel particle in the colour
 STRB    R7, [R3]                   \ specified in R7
 STRB    R7, [R3, #1]

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; DrawTriangleFromBuffer
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Process the 'draw triangle' command from the graphics buffer
\  Deep dive; Depth-sorting with the graphics buffers
\             Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ The seven parameter words for this command match the arguments for the
\ DrawTriangle routine;
\
\   * Pixel coordinate of corner 1 [x1, y1]
\   * Pixel coordinate of corner 2 [x2, y2]
\   * Pixel coordinate of corner 3 [x3, y3]
\   * Colour
\
\ This command draws a triangle of the specified shape and colour.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R9                              The address of the seven parameter words in
\                                   the graphics buffer
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R9                              Updated to point to the next word in the
\                                   graphics buffer
\
\   R12                             R12 is preserved
\
\ ******************************************************************************

.DrawTriangleFromBuffer

 LDMIA   R9!, {R0-R5, R8}           \ Fetch the seven parameter words from the
                                    \ graphics buffer into R0 to R5 and R8,
                                    \ incrementing R9 to point to the next
                                    \ command in the buffer

 STMFD   R13!, {R9, R12}            \ Store R9 and R12 on the stack so they
                                    \ don't get corrupted by the following call
                                    \ to DrawTriangle

 BL      DrawTriangle               \ Draw a triangle at the coordinates
                                    \ specified in R0 to R5 and R8

 LDMFD   R13!, {R9, R12}            \ Retrieve the values of R9 and R12 that we
                                    \ stored above

 B       DrawNextFromGraphicsBuffer \ Draw the next command from the graphics
                                    \ buffer

\ ******************************************************************************
\
\       Name; DrawParticleToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Draw a large coloured particle into a slightly nearer graphics,
\             buffer according to its distance
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ This command draws a large coloured particle into a graphics buffer, with the
\ buffer being chosen according to the distance of the particle in the z-axis,
\ plus one [so this routine draws the particle into a slightly nearer graphics
\ buffer than DrawParticleShadowToBuffer, so shadows never overlap their
\ corresponding particles].
\
\ This command is used for drawing particles, while DrawParticleShadowToBuffer
\ is used for drawing their shadows.
\
\ The command numbers for drawing large particles are 0 to 8, with higher
\ numbers giving smaller particles [so particles are smaller when they are
\ further away, as the command number is based on the z-coordinate]. This is
\ followed by the pixel coordinates, composed into one 32-bit parameter word as
\ follows;
\
\          #20-31     #12-19    #8-11            #0-7
\    019876543210   98765432     1098        76543210
\               ^          ^        ^               ^
\               |          |        |               |
\   Pixel x-coord     Colour   Unused   Pixel y-coord
\         [0-319]    [0-255]                  [0-255]
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        The pixel coordinate of the particle
\
\   R7                              The particle colour
\
\   R8                              The 3D z-coordinate of the particle
\
\   R12                             The address of the table of containing the
\                                   end addresses of the graphics buffers
\                                   [i.e. graphicsBuffersEnd]
\
\ ******************************************************************************

.DrawParticleToBuffer

 STMFD   R13!, {R9, R14}            \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 RSB     R14, R8, #LANDSCAPE_Z      \ Set R14 = landscape offset - R8
                                    \
                                    \ We use the top byte of this number as the
                                    \ number of the graphics buffer to draw into
                                    \
                                    \ This ensures that more distant particles
                                    \ are drawn into lower-numbered graphics
                                    \ buffers, so higher z-coordinates map to
                                    \ lower buffer numbers and vice versa [so
                                    \ buffer 0 is for objects that are far away
                                    \ and buffer 10 is for the closest objects
                                    \ to the viewer]

 ADD     R14, R14, #TILE_SIZE       \ Add the size of one tile to R14 so we draw
                                    \ the particle one tile nearer than we would
                                    \ otherwise [i.e. one buffer nearer]

 BIC     R14, R14, #&00C00000       \ We are going to use the top byte of R14 as
                                    \ the buffer number, and we're going to look
                                    \ up the corresponding buffer address from a
                                    \ lookup table with four bytes per entry, so
                                    \ we clear bits 22 and 23 to ensure that
                                    \ R14 >> 22 contains the value of the top
                                    \ byte * 4, which we can then use as an
                                    \ offset into the lookup table to fetch the
                                    \ address of the end of the buffer, which is
                                    \ where we can store our particle-drawing
                                    \ command

 LDR     R9, [R12, R14, LSR #22]    \ Set R9 to the address of the next free
                                    \ byte in the graphics buffer whose number
                                    \ matches the top byte of R14, fetching the
                                    \ address from the lookup table at R12

 MOV     R8, R8, LSR #25            \ Scale R8 from a 3D z-coordinate into a
 CMP     R8, #8                     \ number between 0 and 8, which we can use
 MOVHS   R8, #8                     \ as the drawing command number, as drawing
                                    \ commands 0 to 8 draw particles with higher
                                    \ numbers giving smaller particles [see
                                    \ bufferJump for a full list of drawing
                                    \ commands]

 AND     R7, R7, #&FF               \ Encode the two pixel coordinates in R0 and
 ADD     R1, R1, R7, LSL #12        \ R1 and the colour in R7 into one parameter
 ADD     R1, R1, R0, LSL #20        \ word in R1, by inserting the value of R0
                                    \ into bits 20-31 of R1 and the value of R7
                                    \ into bits 12-19

 STR     R8, [R9], #4               \ Store the drawing command number in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer

 STR     R1, [R9], #4               \ Store the parameter word in R1 in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer

 STR     R9, [R12, R14, LSR #22]    \ Update the end address in the table at R12
                                    \ to point to the new end address of the
                                    \ graphics buffer

 LDMFD   R13!, {R9, PC}             \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawParticleShadowToBuffer
\       Type; Subroutine
\   Category; Particles
\    Summary; Draw a small black particle into the correct graphics buffer,
\             according to its distance
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ This command draws a small black particle into a graphics buffer, with the
\ buffer being chosen according to the distance of the particle in the z-axis.
\
\ This command is used for drawing particle shadows, while the particles
\ themselves are drawn by DrawParticleToBuffer.
\
\ The command numbers for drawing small particles are 9 to 17, with higher
\ numbers giving smaller particles [so particles are smaller when they are
\ further away, as the command number is based on the z-coordinate]. This is
\ followed by the pixel coordinates, composed into one 32-bit parameter word as
\ follows;
\
\          #20-31       #12-19    #8-11            #0-7
\    019876543210     98765432     1098        76543210
\               ^            ^        ^               ^
\               |            |        |               |
\   Pixel x-coord       Colour   Unused   Pixel y-coord
\         [0-319]   [always 0]                  [0-255]
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        The pixel coordinate of the particle
\
\   R8                              The 3D z-coordinate of the particle
\
\   R12                             The address of the table of containing the
\                                   end addresses of the graphics buffers
\                                   [i.e. graphicsBuffersEnd]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R12                             R12 is preserved
\
\ ******************************************************************************

.DrawParticleShadowToBuffer

 STMFD   R13!, {R9, R14}            \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 RSB     R14, R8, #LANDSCAPE_Z      \ Set R14 = landscape offset - R8
                                    \
                                    \ We use the top byte of this number as the
                                    \ number of the graphics buffer to draw into
                                    \
                                    \ This ensures that more distant particles
                                    \ are drawn into lower-numbered graphics
                                    \ buffers, so higher z-coordinates map to
                                    \ lower buffer numbers and vice versa [so
                                    \ buffer 0 is for objects that are far away
                                    \ and buffer 10 is for the closest objects
                                    \ to the viewer]

 BIC     R14, R14, #&00C00000       \ We are going to use the top byte of R14 as
                                    \ the buffer number, and we're going to look
                                    \ up the corresponding buffer address from a
                                    \ lookup table with four bytes per entry, so
                                    \ we clear bits 22 and 23 to ensure that
                                    \ R14 >> 22 contains the value of the top
                                    \ byte * 4, which we can then use as an
                                    \ offset into the lookup table to fetch the
                                    \ address of the end of the buffer, which is
                                    \ where we can store our particle-drawing
                                    \ command

 LDR     R9, [R12, R14, LSR #22]    \ Set R9 to the address of the next free
                                    \ byte in the graphics buffer whose number
                                    \ matches the top byte of R14, fetching the
                                    \ address from the lookup table at R12

 MOV     R8, R8, LSR #25            \ Scale R8 from a 3D z-coordinate into a
 CMP     R8, #8                     \ number between 9 and 17, which we can use
 MOVHS   R8, #8                     \ as the drawing command number, as drawing
 ADD     R8, R8, #9                 \ commands 9 to 17 draw shadow particles
                                    \ with higher numbers giving smaller
                                    \ particles [see bufferJump for a full list
                                    \ of drawing commands]

 ADD     R1, R1, R0, LSL #20        \ Encode the two pixel coordinates in R0 and
                                    \ R1 into one parameter word in R1, by
                                    \ inserting the value of R0 into bits 20-31
                                    \ of R1
                                    \
                                    \ The colour in bits 12-19 is left at zero,
                                    \ so the particle is always black

 STR     R8, [R9], #4               \ Store the drawing command number in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer

 STR     R1, [R9], #4               \ Store the parameter word in R1 in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer

 STR     R9, [R12, R14, LSR #22]    \ Update the end address in the table at R12
                                    \ to point to the new end address of the
                                    \ graphics buffer

 LDMFD   R13!, {R9, PC}             \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawTriangleShadowToBuffer
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a triangle shadow into the correct graphics buffer, according
\             to its distance
\  Deep dive; Drawing 3D objects
\             Depth-sorting with the graphics buffers
\             Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ This command draws a triangle into a graphics buffer, with the buffer being
\ chosen according to the distance of the triangle in the z-axis.
\
\ This command is used for drawing triangle shadows, while the triangles
\ themselves are drawn by DrawTriangleToBuffer. It is always called with a
\ colour value of 0 in R8, so shadows are always black.
\
\ The command number for drawing a triangle is 18, followed by seven parameter
\ words that match the arguments for the DrawTriangle routine;
\
\   * Command number [18]
\
\   * Pixel coordinate of corner 1 [x1, y1]
\
\   * Pixel coordinate of corner 2 [x2, y2]
\
\   * Pixel coordinate of corner 3 [x3, y3]
\
\   * Colour
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        Pixel coordinate of corner 1
\
\   [R2, R2]                        Pixel coordinate of corner 2
\
\   [R4, R5]                        Pixel coordinate of corner 4
\
\   R8                              Colour [always 0 for black]
\
\   R12                             The address of the table of containing the
\                                   end addresses of the graphics buffers
\                                   [i.e. graphicsBuffersEnd]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R12                             R12 is preserved
\
\ ******************************************************************************

.DrawTriangleShadowToBuffer

 STMFD   R13!, {R9, R14}            \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R14, [R11, #zObject]       \ Set R14 to the z-coordinate of the
                                    \ triangle

 RSB     R14, R14, #LANDSCAPE_Z     \ Set R14 = landscape offset - R14
                                    \
                                    \ We use the top byte of this number as the
                                    \ number of the graphics buffer to draw into
                                    \
                                    \ This ensures that more distant triangles
                                    \ are drawn into lower-numbered graphics
                                    \ buffers, so higher z-coordinates map to
                                    \ lower buffer numbers and vice versa [so
                                    \ buffer 0 is for objects that are far away
                                    \ and buffer 10 is for the closest objects
                                    \ to the viewer]

 CMP     R14, #LANDSCAPE_Z_BEYOND   \ If R14 is a z-distance beyond the back of
 MOVHS   R14, #LANDSCAPE_Z_DEPTH    \ the visible landscape, cap it to the depth
                                    \ of the landscape so it fits into the range
                                    \ of graphics buffers [as there is one
                                    \ buffer for each tile row, going into the
                                    \ screen]

 BIC     R14, R14, #&00C00000       \ We are going to use the top byte of R14 as
                                    \ the buffer number, and we're going to look
                                    \ up the corresponding buffer address from a
                                    \ lookup table with four bytes per entry, so
                                    \ we clear bits 22 and 23 to ensure that
                                    \ R14 >> 22 contains the value of the top
                                    \ byte * 4, which we can then use as an
                                    \ offset into the lookup table to fetch the
                                    \ address of the end of the buffer, which is
                                    \ where we can store our triangle-drawing
                                    \ command

 LDR     R9, [R12, R14, LSR #22]    \ Set R9 to the address of the next free
                                    \ byte in the graphics buffer whose number
                                    \ matches the top byte of R14, fetching the
                                    \ address from the lookup table at R12

 MOV     R7, #18                    \ The command number for drawing a triangle
 STR     R7, [R9], #4               \ is 18, so we store a value of 18 in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer [see bufferJump for a full list of
                                    \ drawing commands]

 STMIA   R9!, {R0-R5, R8}           \ Store the three pixel coordinates from R0
                                    \ to R5, and then the colour in R8, in the
                                    \ buffer

 STR     R9, [R12, R14, LSR #22]    \ Update the end address in the table at R12
                                    \ to point to the new end address of the
                                    \ graphics buffer

 LDMFD   R13!, {R9, PC}             \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawTriangleToBuffer
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a coloured triangle into a slightly nearer graphics buffer,
\             according to its distance
\  Deep dive; Drawing 3D objects
\             Depth-sorting with the graphics buffers
\             Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ This command draws a triangle into a graphics buffer, with the buffer being
\ chosen according to the distance of the triangle in the z-axis, plus one [so
\ this routine draws the triangle into a slightly nearer graphics buffer than
\ DrawTriangleShadowToBuffer, so shadows never overlap their corresponding
\ triangles].
\
\ This command is used for drawing triangles, while DrawTriangleShadowToBuffer
\ is used to draw their shadows.
\
\ The command number for drawing a triangle is 18, followed by seven parameter
\ words that match the arguments for the DrawTriangle routine;
\
\   * Command number [18]
\
\   * Pixel coordinate of corner 1 [x1, y1]
\
\   * Pixel coordinate of corner 2 [x2, y2]
\
\   * Pixel coordinate of corner 3 [x3, y3]
\
\   * Colour
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        Pixel coordinate of corner 1
\
\   [R2, R2]                        Pixel coordinate of corner 2
\
\   [R4, R5]                        Pixel coordinate of corner 4
\
\   R8                              Colour
\
\   R12                             The address of the table of containing the
\                                   end addresses of the graphics buffers
\                                   [i.e. graphicsBuffersEnd]
\
\ ------------------------------------------------------------------------------
\
\ Returns;
\
\   R12                             R12 is preserved
\
\ ******************************************************************************

.DrawTriangleToBuffer

 STMFD   R13!, {R9, R14}            \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R14, [R11, #zObject]       \ Set R14 to the z-coordinate of the
                                    \ triangle

 RSB     R14, R14, #LANDSCAPE_Z     \ Set R14 = landscape offset - R14
                                    \
                                    \ We use the top byte of this number as the
                                    \ number of the graphics buffer to draw into
                                    \
                                    \ This ensures that more distant triangles
                                    \ are drawn into lower-numbered graphics
                                    \ buffers, so higher z-coordinates map to
                                    \ lower buffer numbers and vice versa [so
                                    \ buffer 0 is for objects that are far away
                                    \ and buffer 10 is for the closest objects
                                    \ to the viewer]

 ADD     R14, R14, #TILE_SIZE       \ Add the size of one tile to R14 so we draw
                                    \ the triangle one tile nearer than we would
                                    \ otherwise [i.e. one buffer nearer]

 CMP     R14, #LANDSCAPE_Z_BEYOND   \ If R14 is a z-distance beyond the back of
 MOVHS   R14, #LANDSCAPE_Z_DEPTH    \ the visible landscape, cap it to the depth
                                    \ of the landscape so it fits into the range
                                    \ of graphics buffers [as there is one
                                    \ buffer for each tile row, going into the
                                    \ screen]

 BIC     R14, R14, #&00C00000       \ We are going to use the top byte of R14 as
                                    \ the buffer number, and we're going to look
                                    \ up the corresponding buffer address from a
                                    \ lookup table with four bytes per entry, so
                                    \ we clear bits 22 and 23 to ensure that
                                    \ R14 >> 22 contains the value of the top
                                    \ byte * 4, which we can then use as an
                                    \ offset into the lookup table to fetch the
                                    \ address of the end of the buffer, which is
                                    \ where we can store our triangle-drawing
                                    \ command

 LDR     R9, [R12, R14, LSR #22]    \ Set R9 to the address of the next free
                                    \ byte in the graphics buffer whose number
                                    \ matches the top byte of R14, fetching the
                                    \ address from the lookup table at R12

 MOV     R7, #18                    \ The command number for drawing a triangle
 STR     R7, [R9], #4               \ is 18, so we store a value of 18 in the
                                    \ graphics buffer and increment the address
                                    \ in R9 to point to the next word in the
                                    \ buffer [see bufferJump for a full list of
                                    \ drawing commands]

 STMIA   R9!, {R0-R5, R8}           \ Store the three pixel coordinates from R0
                                    \ to R5, and then the colour in R8, in the
                                    \ buffer

 STR     R9, [R12, R14, LSR #22]    \ Update the end address in the table at R12
                                    \ to point to the new end address of the
                                    \ graphics buffer

 LDMFD   R13!, {R9, PC}             \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; bufferJump
\       Type; Variable
\   Category; Graphics buffers
\    Summary; The jump table for drawing commands that we store in the graphics
\             buffers
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.bufferJump

 EQUD    Draw3x2ParticleFromBuffer  \ Drawing commands 0 to 8 draw large
 EQUD    Draw3x2ParticleFromBuffer  \ particles [for particles]
 EQUD    Draw3x2ParticleFromBuffer
 EQUD    Draw3x2ParticleFromBuffer
 EQUD    Draw3x2ParticleFromBuffer
 EQUD    Draw3x2ParticleFromBuffer
 EQUD    Draw2x2ParticleFromBuffer
 EQUD    Draw2x1ParticleFromBuffer
 EQUD    Draw1x1ParticleFromBuffer

 EQUD    Draw3x1ParticleFromBuffer  \ Drawing commands 9 to 17 draw small
 EQUD    Draw3x1ParticleFromBuffer  \ particles [for shadows]
 EQUD    Draw3x1ParticleFromBuffer
 EQUD    Draw3x1ParticleFromBuffer
 EQUD    Draw3x1ParticleFromBuffer
 EQUD    Draw3x1ParticleFromBuffer
 EQUD    Draw2x1ParticleFromBuffer
 EQUD    Draw2x1ParticleFromBuffer
 EQUD    Draw1x1ParticleFromBuffer

 EQUD    DrawTriangleFromBuffer     \ Drawing command 18 draws a triangle

 EQUD    TerminateGraphicsBuffer    \ Drawing command 19 terminates the buffer

\ ******************************************************************************
\
\       Name; DrawGraphicsBuffer
\       Type; Subroutine
\   Category; Graphics buffers
\    Summary; Draw the contents of the specified graphics buffer
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ------------------------------------------------------------------------------
\
\ This routine draws the contents of a specified graphics buffer. There are 11
\ buffers that are used to layer the game's graphics correctly according to an
\ object's distance. This routine draws the contents of a specific buffer onto
\ the screen.
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The number of the graphics buffer to draw
\                                   [0 to 10]
\
\ ------------------------------------------------------------------------------
\
\ Other entry points;
\
\   DrawNextFromGraphicsBuffer      Process the next command from the graphics
\                                   buffer
\
\   TerminateGraphicsBuffer         Terminate drawing from the graphics buffer
\                                   by returning from the subroutine
\
\ ******************************************************************************

.DrawGraphicsBuffer

 STMFD   R13!, {R6-R12, R14}        \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R1, graphicsBufferEndAddr  \ Set R1 to the address of the table that
                                    \ contains the end addresses of the graphics
                                    \ buffers

 LDR     R9, [R1, R0, LSL #2]       \ Set R9 to the R0-th entry from the
                                    \ graphicsBuffersEnd table, which contains
                                    \ the end address for graphics buffer R0

 LDR     R12, screenAddr            \ Set R12 to the address of the screen bank
                                    \ we are drawing in, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

.DrawNextFromGraphicsBuffer

 LDR     R0, [R9], #4               \ Fetch the address of the next free byte in
                                    \ the graphics buffer [which is the same as
                                    \ the end address of the buffer] from the 
                                    \ graphicsBuffersEnd table, put it into R0
                                    \ and increment R9 to point to the next
                                    \ buffer's end address

]

 labOffset = P% + 8 - bufferJump    : REM Set labOffset to the offset back to the
                                    : REM bufferJump table from the next instruction

[

 OPT    pass%

 ADD     R0, PC, R0, LSL #2         \ Jump to the address in entry R0 in the
 LDR     PC, [R0, #-labOffset]      \ bufferJump table

.TerminateGraphicsBuffer

 LDMFD   R13!, {R6-R12, PC}         \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; AddTerminatorsToBuffers
\       Type; Subroutine
\   Category; Graphics buffers
\    Summary; Add terminators to the ends of the graphics buffers so we know
\             when to stop drawing
\  Deep dive; Depth-sorting with the graphics buffers
\
\ ******************************************************************************

.AddTerminatorsToBuffers

 STMFD   R13!, {R12, R14}           \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 LDR     R12, graphicsBufferEndAddr \ Set R12 to the address of the table that
                                    \ contains the end addresses of the graphics
                                    \ buffers

 LDR     R14, graphicsBufferAddr    \ Set R14 to the address of the table that
                                    \ contains the start addresses of the
                                    \ graphics buffers

 MOV     R0, #TILES_Z               \ We are going to loop through all of the
                                    \ graphics buffers, so set a counter in R0
                                    \ to the number of tile corners in the
                                    \ visible landscape, going back to front [as
                                    \ there is one graphics buffer for each
                                    \ corner row]

 MOV     R1, #19                    \ The command number for terminating a
                                    \ graphics buffer is 19, so put this in R1
                                    \ [see bufferJump for a full list of drawing
                                    \ commands]

.term1

 LDR     R2, [R12, R0, LSL #2]      \ Set R2 to the end address of graphics
                                    \ buffer number R0

 STR     R1, [R2]                   \ Store the termination command number at
                                    \ the end of the buffer

 LDR     R2, [R14, R0, LSL #2]      \ Reset the graphicsBufferEnd entry for this
 STR     R2, [R12, R0, LSL #2]      \ buffer back to the start address from the
                                    \ graphicsBuffer table, so when we draw the
                                    \ next frame, we fill up the graphics
                                    \ buffers from the start again

 SUBS    R0, R0, #1                 \ Decrement the loop counter

 BPL     term1                      \ Loop back until we have terminated all 12
                                    \ graphics buffers

 LDMFD   R13!, {R12, PC}            \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; screenBank2Addr
\       Type; Variable
\   Category; Drawing the screen
\    Summary; The screen address for the start of the 17th pixel line in screen
\             bank 0 [i.e. the line just below the two rows of text]
\  Deep dive; Screen memory in the Archimedes
\
\ ******************************************************************************

.screenBank2Addr

 EQUD    &01FD8000 + 320 * 16

\ ******************************************************************************
\
\       Name; screenBank1Addr
\       Type; Variable
\   Category; Drawing the screen
\    Summary; The screen address for the start of the 17th pixel line in screen
\             bank 1 [i.e. the line just below the two rows of text]
\  Deep dive; Screen memory in the Archimedes
\
\ ******************************************************************************

.screenBank1Addr

 EQUD    &01FEC000 + 320 * 16

\ ******************************************************************************
\
\       Name; screenBankNumber
\       Type; Variable
\   Category; Drawing the screen
\    Summary; The number of the current screen bank [0 or 1]
\  Deep dive; Screen memory in the Archimedes
\
\ ******************************************************************************

.screenBankNumber

 EQUD    0                          \ Stores the details of the current bank, as
                                    \ follows;
                                    \
                                    \   * 0 = draw bank 1, show bank 2
                                    \
                                    \   * 1 = draw bank 2, show bank 1

\ ******************************************************************************
\
\       Name; divisionTableAddr
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; The address of the division lookup table
\
\ ******************************************************************************

.divisionTableAddr

 EQUD    divisionTable

\ ******************************************************************************
\
\       Name; SwitchScreenBank
\       Type; Subroutine
\   Category; Drawing the screen
\    Summary; Switch screen banks and clear the newly hidden screen bank to
\             black
\  Deep dive; Screen memory in the Archimedes
\
\ ******************************************************************************

.SwitchScreenBank

 LDRB    R0, screenBankNumber       \ Flip the current screen bank number in
 EORS    R0, R0, #1                 \ screenBankNumber between bank 0 and bank 1
 STRB    R0, screenBankNumber       \ which means;
                                    \
                                    \   * 0 = draw bank 1, show bank 2
                                    \
                                    \   * 1 = draw bank 2, show bank 1

 LDRNE   R1, screenBank2Addr        \ Set screenAddr as follows;
 LDREQ   R1, screenBank1Addr        \
 STR     R1, screenAddr             \   * If screenBankNumber = 0, set it to the
                                    \     address of bank 2 in screenBank2Addr
                                    \
                                    \   * If screenBankNumber = 1, set it to the
                                    \     address of bank 1 in screenBank1Addr
                                    \
                                    \ So screenAddr points to the screen that is
                                    \ no longer being shown, i.e. the bank that
                                    \ we should now draw into

 TEQ     R0, #0                     \ Set R1 = screenBankNumber + 1
 MOVEQ   R1, #1                     \
 MOVNE   R1, #2                     \ So this sets R1 as follows;
                                    \
                                    \   * If screenBankNumber = 0, R1 = 1
                                    \
                                    \   * If screenBankNumber = 1, R1 = 2
                                    \
                                    \ So R1 is the number of the opposite bank
                                    \ to the one in screenAddr, i.e. the bank
                                    \ that we should now show on-screen

 MOV     R0, #113                   \ Set the display hardware to display the
 SWI     OS_Byte                    \ screen bank in R1, so the correct bank is
                                    \ shown on-screen
                                    \
                                    \ This call returns the old bank number in
                                    \ R1, so R1 now contains the number of the
                                    \ screen bank that is not being displayed

 MOV     R0, #112                   \ Set the VDU driver screen bank to the bank
 SWI     OS_Byte                    \ number in R1, so this directs VDU calls
                                    \ [such as the text routines that update the
                                    \ score bar] so they update the hidden
                                    \ screen bank

 MOV     R0, #19                    \ Wait for the vertical sync
 SWI     OS_Byte

                                    \ We now clear the screen bank that isn't
                                    \ being shown, i.e. the bank at screenAddr

 STMFD   R13!, {R9, R11-R12, R14}   \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOV     R0, #0                     \ Set R0 to R9 to zero so we can use them to
 MOV     R1, #0                     \ clear the screen bank to black [as black
 MOV     R2, #0                     \ is represented by zero in screen memory]
 MOV     R3, #0
 MOV     R4, #0
 MOV     R5, #0
 MOV     R6, #0
 MOV     R7, #0
 MOV     R8, #0
 MOV     R9, #0

 LDR     R11, screenAddr            \ Set R11 = screenAddr
                                    \
                                    \ So R11 contains the address of the screen
                                    \ bank we want to clear, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

 ADD     R12, R11, #&12C00          \ Set R12 = screenAddr + 320 * 240
                                    \
                                    \ So R12 points to the end of screen memory
                                    \ for the bank at screenAddr, as the address
                                    \ in R11 skips the first 16 pixel rows of
                                    \ the 320 * 256 screen

                                    \ We now zero screen memory from R12 down to
                                    \ R11, which clears the screen bank that we
                                    \ are no longer showing on-screen

.bank1

 STMDB   R12!, {R0-R9}              \ Zero 40 x 32-bit words of screen memory,
 STMDB   R12!, {R0-R9}              \ decreasing the address in R12 as we go
 STMDB   R12!, {R0-R9}
 STMDB   R12!, {R0-R9}

 CMP     R12, R11                   \ If R12 hasn't yet reached the start of the
 BNE     bank1                      \ bank's screen memory in R11, loop back to
                                    \ keep zeroing screen memory until it is all
                                    \ cleared to black, leaving just the two
                                    \ lines of text at the top

 LDMFD   R13!, {R9, R11-R12, PC}    \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawQuadrilateral
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a quadrilateral [i.e. two triangles]
\  Deep dive; Drawing the landscape
\             Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        Pixel coordinate of corner 1
\
\   [R2, R3]                        Pixel coordinate of corner 2
\
\   [R4, R5]                        Pixel coordinate of corner 3
\
\   [R6, R7]                        Pixel coordinate of corner 3
\
\   R8                              Colour
\
\ ******************************************************************************

.DrawQuadrilateral

 STMFD   R13!, {R2-R7, R9-R12, R14} \ Store R2 to R7 on the stack, as well as
                                    \ the registers that we want to use on the
                                    \ stack so they can be preserved

 BL      DrawTriangle               \ Draw a triangle in colour R8 with the
                                    \ following corner coordinates;
                                    \
                                    \   [R0, R1]
                                    \   [R2, R3]
                                    \   [R4, R5]

 LDMFD   R13!, {R0-R5}              \ Set R0 to R5 to the original values of
                                    \ R2 to R7, to [R0, R2] is the pixel
                                    \ coordinate of corner 2, and so on

 BL      DrawTriangle               \ Draw a triangle in colour R8 with the
                                    \ following corner coordinates, in terms
                                    \ of the original arguments to the routine;
                                    \
                                    \   [R2, R3]
                                    \   [R4, R5]
                                    \   [R6, R7]
                                    \
                                    \ So overall this draws a quadrilateral in
                                    \ colour R8 with corners at;
                                    \
                                    \   [R0, R1]
                                    \   [R2, R3]
                                    \   [R4, R5]
                                    \   [R6, R7]

 LDMFD   R13!, {R9-R12, PC}         \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 1 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a triangle, starting by ordering the coordinates and jumping
\             to the relevant part of the routine
\  Deep dive; Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   [R0, R1]                        Pixel coordinate of corner 1 in [x1, y1]
\
\   [R2, R3]                        Pixel coordinate of corner 2 in [x2, y2]
\
\   [R4, R5]                        Pixel coordinate of corner 3 in [x3, y3]
\
\   R8                              Colour
\
\ ******************************************************************************

.DrawTriangle

 CMP     R0, #320                   \ Check to see whether any of the triangle
 CMPLO   R1, #239                   \ corners are off-screen, in which case at
 CMPLO   R2, #320                   \ least one of x1 to x3 will be >= 320, or
 CMPLO   R3, #239                   \ at least one of y1 to y3 will be >= 239
 CMPLO   R4, #320                   \ [the y-coordinate is checked against 239
 CMPLO   R5, #239                   \ rather than 255 as the top 16 pixel lines
                                    \ of the 320x256 screen are taken up by the
                                    \ score bar]
                                    \
                                    \ This also catches any negative pixel
                                    \ coordinates, as they will be treated as
                                    \ extremely large positive values in the
                                    \ comparisons [as the LO condition is used
                                    \ for unsigned comparisons]

 BHS     trin23                     \ If any of the corners are off-screen, jump
                                    \ to part 6 to draw a clipped triangle

 STMFD   R13!, {R14}                \ Store the return address on the stack

 LDR     R12, screenAddr            \ Set R12 to the address of the screen bank
                                    \ we are drawing in, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

                                    \ We start by ordering the triangle corners
                                    \ by y-coordinate, so [x1, y1] is the
                                    \ furthest down the screen with the highest
                                    \ y-coordinate, followed by [x2, y2] then
                                    \ [x3, y3], at the same or higher level, and
                                    \ in that order

 CMP     R1, R3                     \ If R1 >= R3, jump to trin1 to skip the
 BHS     trin1                      \ following, as y1 >= y2 already

 MOV     R6, R0                     \ Swap [x1, y1] and [x2, y2], so we now have
 MOV     R0, R2                     \ y1 >= y2
 MOV     R2, R6
 MOV     R6, R1
 MOV     R1, R3
 MOV     R3, R6

.trin1

 CMP     R1, R5                     \ If R1 >= R5, jump to trin2 to skip the
 BHS     trin2                      \ following, as y1 >= y3 already

 MOV     R6, R0                     \ Swap [x1, y1] and [x3, y3], so we now have
 MOV     R0, R4                     \ y1 >= y3
 MOV     R4, R6
 MOV     R6, R1
 MOV     R1, R5
 MOV     R5, R6

.trin2

 CMP     R3, R5                     \ If R3 >= R5, jump to trin3 to skip the
 BHS     trin3                      \ following, as y2 >= y3 already

 MOV     R6, R2                     \ Swap [x2, y2] and [x3, y3], so we now have
 MOV     R2, R4                     \ y2 >= y3
 MOV     R4, R6
 MOV     R6, R3
 MOV     R3, R5
 MOV     R5, R6

                                    \ So the triangle coordinates are ordered
                                    \ like this on-screen;
                                    \
                                    \   [x3, y3]
                                    \   [x2, y2]
                                    \   [x1, y1]

.trin3

 ADD     R11, R1, R1, LSL #2        \ Set R11 = R1 + R1 * 4
                                    \         = 5 * y1

 ADD     R12, R12, R11, LSL #6      \ Set R12 = R12 + R11 * 64
                                    \         = screenAddr + 5 * y1 * 64
                                    \         = screenAddr + 320 * y1
                                    \
                                    \ So R12 is the screen address of the start
                                    \ of the pixel row containing [x1, y1], at
                                    \ the bottom of the triangle on-screen

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 2 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Calculate the slope of [x1, y1] to [x2, y2]
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ We now calculate the slope of the first
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x2, y2], as follows;
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]

 SUBS    R9, R1, R3                 \ Set R9 = R1 - R3
                                    \        = y1 - y2
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y1 >= y2

 BEQ     trin16                     \ If [x1, y1] and [x2, y2] share the same
                                    \ y-coordinate then the triangle has a
                                    \ horizontal edge between [x1, y1] and
                                    \ [x2, y2], so jump to part 5 to process
                                    \ this special case

 STMFD   R13!, {R4-R5}              \ Store the third corner's coordinates in
                                    \ [x3, y3] on the stack to we can retrieve
                                    \ them later

 SUBS    R14, R2, R0                \ Set R14 = R2 - R0
                                    \         = x2 - x1
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ x-coordinate between the two points
                                    \
                                    \ This will be positive if the line from
                                    \ [x1, y1] to [x2, y2] slopes up and to the
                                    \ right, or negative if the line slopes up
                                    \ and to the left

 RSBMI   R14, R2, R0                \ If R14 is negative, set R14 = x1 - x2, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x2 - x1|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin4                      \
                                    \   R14 = |x2 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y2] >= 64
                                    \
                                    \ so jump to trin4 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R6, [R10, R9, LSL #2]      \ Set R6 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y1 - y2] / |x2 - x1|

 B       trin6                      \ Jump to trin6 to skip the following and
                                    \ keep going

.trin4

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x2 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y2] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R6, #0                     \ Set R6 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin5

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R6, R6, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R6]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin5                      \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y1 - y2] / |x2 - x1|

.trin6

 CMP     R2, R0                     \ If R2 - R0 < 0 then;
 RSBMI   R6, R6, #0                 \
                                    \   x2 - x1 < 0
                                    \
                                    \ so negate R6 to give R6 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]
                                    \
                                    \ So R6 contains the slope of the first side
                                    \ of the triangle, from [x1, y1] to [x2, y2]

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 3 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Calculate the slope of [x1, y1] to [x3, y3]
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ We now calculate the slope for the second
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]

 SUBS    R9, R1, R5                 \ Set R9 = R1 - R5
                                    \        = y1 - y3

 SUBS    R14, R4, R0                \ Set R14 = R4 - R0
                                    \         = x3 - x1

 RSBMI   R14, R4, R0                \ If R14 is negative, set R4 = x1 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x1|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin7                      \
                                    \   R14 = |x3 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ so jump to trin7 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R7, [R10, R9, LSL #2]      \ Set R7 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y1 - y3] / |x3 - x1|

 B       trin9                      \ Jump to trin9 to skip the following and
                                    \ keep going

.trin7

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R7, #0                     \ Set R7 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin8

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R7, R7, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R7]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin8                      \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y1 - y3] / |x3 - x1|

.trin9

 CMP     R4, R0                     \ If R4 - R0 < 0 then;
 RSBMI   R7, R7, #0                 \
                                    \   x3 - x1 < 0
                                    \
                                    \ so negate R7 to give R7 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \
                                    \ So R7 contains the slope of the second
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x3, y3]

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 4 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a triangle that isn't clipped and has a sloping first side
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ By this point we have the following;
                                    \
                                    \   [R0, R1] = [x1, y1]
                                    \
                                    \   [R2, R3] = [x2, y2]
                                    \
                                    \   [x3, y3] is on the stack
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]
                                    \      = slope of [x1, y1] to [x2, y2]
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \      = slope of [x1, y1] to [x3, y3]
                                    \
                                    \   R12 = screen address of the start of the
                                    \         pixel row containing [x1, y1]
                                    \
                                    \ [x1, y1] is the point lowest down the
                                    \ screen and [x3, y3] is the highest up the
                                    \ screen, with [x2, y2] the point in the
                                    \ middle [in terms of y-coordinate]
                                    \
                                    \ We now draw the triangle in two parts,
                                    \ effectively slicing the triangle in half
                                    \ with a horizontal line at y-coordinate y2,
                                    \ leaving two triangles to draw;
                                    \
                                    \   1. The triangle from [x1, y1] at the
                                    \      bottom up to the horizontal line with
                                    \      [x2, y2] at one end
                                    \
                                    \   2. The triangle from the horizontal line
                                    \      with [x2, y2] at one end, up to
                                    \      [x3, y3] at the top
                                    \
                                    \ We start at the bottom of the triangle, at
                                    \ [x1, y1], and step upwards by one pixel
                                    \ row at a time, drawing a horizontal line
                                    \ between the two sides, until we reach the
                                    \ level of [x2, y2]
                                    \
                                    \ As we step up each pixel row, we calculate
                                    \ the x-coordinates of each row we draw by
                                    \ adding the slopes in R6 and R7
                                    \
                                    \ We store the x-coordinates of the current
                                    \ horizontal line in R4 and R5, so these are
                                    \ the registers we update with the slope
                                    \ values

 SUBS    R9, R1, R3                 \ Set R9 = R1 - R3
                                    \        = y1 - y2
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y1 >= y2,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y1 and y2

 MOV     R4, R0, LSL #16            \ Set R4 = R0 << 16
                                    \        = x1 << 16
                                    \
                                    \ We scale up R4 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R4 >> 16 gives us the integral part
                                    \
                                    \ We use R4 as the x-coordinate of the left
                                    \ end of the horizontal line to draw

 ORR     R4, R4, #&8000             \ Set R5 to x1, with the top bit of its
 MOV     R5, R4                     \ fractional part set [so that's 0.5]
                                    \
                                    \ We use R5 as the x-coordinate of the right
                                    \ end of the horizontal line to draw

 CMP     R6, R7                     \ If R6 > R7, swap R6 and R7, so we know
 MOVGT   R14, R6                    \ that R6 <= R7, i.e. R6 contains the side
 MOVGT   R6, R7                     \ with the lesser slope, which will be the
 MOVGT   R7, R14                    \ slope along the left edge of the triangle

.trin10

 ADD     R4, R4, R6                 \ Set R4 = R4 + R6
                                    \        = R4 + slope of left edge
                                    \
                                    \ So this moves the x-coordinate of the left
                                    \ edge by the correct slope as we move up by
                                    \ one pixel row

 ADD     R5, R5, R7                 \ Set R5 = R5 + R7
                                    \        = R5 + slope of right edge
                                    \
                                    \ So this moves the x-coordinate of the
                                    \ right edge by the correct slope as we move
                                    \ up by one pixel row

 ADD     R11, R12, R4, LSR #16      \ Set R11 = R12 + R4 >> 16
                                    \         = screen address of row + x1
                                    \
                                    \ So R11 contains the screen address of the
                                    \ left end of the line to draw

 MOV     R10, R5, LSR #16           \ Set R10 = R5 >> 16 - R4 >> 16
 SUBS    R10, R10, R4, LSR #16      \         =   x-coordinate of right edge
                                    \           - x-coordinate of left edge
                                    \
                                    \ So R10 contains the length of the line
                                    \ from the left edge to the right edge

 BLPL    DrawHorizontalLine         \ If R10 is positive then we draw a line
                                    \ from the left edge to the right edge,
                                    \ using the four-pixel colour word that was
                                    \ passed to the DrawTriangle routine in R8

 SUB     R12, R12, #320             \ Subtract 320 from R12 so that R12 now
                                    \ points to the start of the pixel row above
                                    \ the one we just drew

 SUBS    R9, R9, #1                 \ Decrement the pixel row counter in R9,
                                    \ which contains the number of pixel rows
                                    \ between y1 and y2

 BNE     trin10                     \ Loop back until we have drawn all the
                                    \ horizontal lines in the first part of the
                                    \ triangle, from [x1, y1] at the bottom, up
                                    \ to the level of [x2, y2]

                                    \ So now we need to draw the rest of the
                                    \ triangle, from [x2, y2] up to [x3, y3]

 LDMFD   R13!, {R0-R1}              \ Fetch the coordinates for the third point
                                    \ [x3, y3] from the stack and into [R0, R1]

 SUBS    R9, R3, R1                 \ Set R9 = R3 - R1
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y2 and y3

 LDMEQIA R13!, {PC}                 \ If R9 is zero then [x2, y2] and [x3, y3]
                                    \ are at the same y-coordinate, so there is
                                    \ nothing to draw in the top part of the
                                    \ triangle, so return from the subroutine as
                                    \ we are done

                                    \ We now calculate the slope for the third
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R14 = [y2 - y3] / [x3 - x2]

 SUBS    R14, R0, R2                \ Set R14 = R0 - R2
                                    \         = x3 - x2

 RSBMI   R14, R0, R2                \ If R14 is negative, set R4 = x2 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x2|


 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin11                     \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ so jump to trin11 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R11, [R10, R9, LSL #2]     \ Set R11 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R11 = R9 / R14
                                    \
                                    \       = [y2 - y3] / |x3 - x2|

 B       trin13                     \ Jump to trin13 to skip the following and
                                    \ keep going

.trin11

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ We now calculate R11 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R11, #0                    \ Set R11 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin12

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R11, R11, R10              \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R11]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin12                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R11 = R9 / R14
                                    \
                                    \       = [y2 - y3] / |x3 - x2|

.trin13

 CMP     R0, R2                     \ If R0 - R2 < 0 then;
 RSBMI   R11, R11, #0               \
                                    \   x3 - x2 < 0
                                    \
                                    \ so negate R11 to give R11 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R11 = [y2 - y3] / [x3 - x2]
                                    \
                                    \ So R11 contains the slope of the third
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3]

                                    \ By this point we have the following;
                                    \
                                    \   [R0, R1] = [x3, y3]
                                    \
                                    \   [R2, R3] = [x2, y2]
                                    \
                                    \   R4 = x-coordinate of the left edge for
                                    \        the last line that we drew
                                    \
                                    \   R5 = x-coordinate of the right edge for
                                    \        the last line that we drew
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \      = slope of [x1, y1] to [x3, y3]
                                    \
                                    \   R11 = [y2 - y3] / [x3 - x2]
                                    \       = slope of [x2, y2] to [x3, y3]
                                    \
                                    \   R12 = screen address of the start of the
                                    \         pixel row containing [x2, y2]

 SUB     R9, R3, R1                 \ Set R9 = R3 - R1
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y2 and y3

 SUBS    R14, R2, R4, LSR #16       \ Set;
                                    \
                                    \   R14 = R2 - R4 >> 16
                                    \       = x2 - left edge of horizontal line
                                    \
                                    \ So this will be zero if [x2, y2] is at the
                                    \ left edge of the horizontal line

 SUBNES  R10, R2, R5, LSR #16       \ If the above is non-zero, set;
                                    \
                                    \   R10 = R2 - R5 >> 16
                                    \       = x2 - right edge of horizontal line
                                    \
                                    \ So this will be zero if [x2, y2] is at the
                                    \ right edge of the horizontal line

 BNE     trin15                     \ If both of the above are non-zero, jump to
                                    \ trin15, as [x2, y2] doesn't match either
                                    \ edge of the horizontal line we just drew

 CMP     R2, R4, LSR #16            \ Set the flags on R2 - R4 >> 16

 MOVEQ   R6, R11                    \ If R2 = R4 >> 16, then x2 = left edge, so;
 BICEQ   R4, R4, #&FF00             \ so x2 must be at the left edge, so we set
 BICEQ   R4, R4, #&00FF             \ the slope of the left edge in R6 to R11,
 ORREQ   R4, R4, #&8000             \ as it's the left edge that is changing
                                    \ slope as we move into the top part of the
                                    \ triangle;
                                    \
                                    \   R6 = R11
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ We also reset the fractional part of R4
                                    \ [the left edge x-coordinate] to just the
                                    \ top bit set [so that's 0.5]
                                    \
                                    \ So this sets the slope of the left edge
                                    \ and leaves the slope of the right edge as
                                    \ before

 MOVNE   R7, R11                    \ If R2 <> R4 >> 16, then x2 <> left edge, 
 BICNE   R5, R5, #&FF00             \ so x2 must be at the right edge, so we set
 BICNE   R5, R5, #&00FF             \ the slope of the right edge in R7 to R11,
 ORRNE   R5, R5, #&8000             \ as it's the right edge that is changing
                                    \ slope as we move into the top part of the
                                    \ triangle;
                                    \ 
                                    \   R7 = R11
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ We also reset the fractional part of R5
                                    \ [the right edge x-coordinate] to just the
                                    \ top bit set [so that's 0.5]
                                    \
                                    \ So this sets the slope of the right edge
                                    \ and leaves the slope of the left edge as
                                    \ before

.trin14

 ADD     R4, R4, R6                 \ Set R4 = R4 + R6
                                    \        = R4 + slope of left edge
                                    \
                                    \ So this moves the x-coordinate of the left
                                    \ edge by the correct slope as we move up by
                                    \ one pixel row

 ADD     R5, R5, R7                 \ Set R5 = R5 + R7
                                    \        = R5 + slope of right edge
                                    \
                                    \ So this moves the x-coordinate of the
                                    \ right edge by the correct slope as we move
                                    \ up by one pixel row

 ADD     R11, R12, R4, LSR #16      \ Set R11 = R12 + R4 >> 16
                                    \         = screen address of row + x1
                                    \
                                    \ So R11 contains the screen address of the
                                    \ left end of the line to draw

 MOV     R10, R5, LSR #16           \ Set R10 = R5 >> 16 - R4 >> 16
 SUBS    R10, R10, R4, LSR #16      \         =   x-coordinate of right edge
                                    \           - x-coordinate of left edge
                                    \
                                    \ So R10 contains the length of the line
                                    \ from the left edge to the right edge

 BLPL    DrawHorizontalLine         \ If R10 is positive then we draw a line
                                    \ from the left edge to the right edge,
                                    \ using the four-pixel colour word that was
                                    \ passed to the DrawTriangle routine in R8

 SUB     R12, R12, #320             \ Subtract 320 from R12 so that R12 now
                                    \ points to the start of the pixel row above
                                    \ the one we just drew

 SUBS    R9, R9, #1                 \ Decrement the pixel row counter in R9,
                                    \ which contains the number of pixel rows
                                    \ between y1 and y2

 BNE     trin14                     \ Loop back until we have drawn all the
                                    \ horizontal lines in the second part of
                                    \ the triangle, from [x2, y2] at the bottom,
                                    \ up to the level of [x3, y3]

 LDMFD   R13!, {PC}                 \ Return from the subroutine

.trin15

                                    \ We jump here following these two
                                    \ calculations, if neither of them are zero;
                                    \
                                    \   R14 = R2 - R4 >> 16
                                    \       = x2 - left edge of horizontal line
                                    \
                                    \   R10 = R2 - R5 >> 16
                                    \       = x2 - right edge of horizontal line
                                    \
                                    \ I am not sure what this signifies!

 RSBMI   R10, R10, #0               \ The last calculation was for R10, so this
                                    \ makes R10 positive if the above result is
                                    \ negative, so it does this;
                                    \
                                    \   R10 = |R10|

 CMP     R14, #0                    \ Set the following;
 RSBMI   R14, R14, #0               \
                                    \   R14 = |R14|

                                    \ So R10 and R14 are set to the magnitudes
                                    \ of the distances between [x2, y2] and each
                                    \ edge of the horizontal line [R14 for left,
                                    \ R10 for right]

 CMP     R14, R10                   \ If |R14| >= |R10| then [x2, y2] is nearer
 MOVHS   R6, R11                    \ the right edge, so set;
                                    \
                                    \   R6 = R11
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ So this sets the slope of the left edge
                                    \ and leaves the slope of the right edge as
                                    \ before [though I'm not sure why?]

 MOVLO   R7, R11                    \ Otherwise |R14| < |R10| and [x2, y2] is
                                    \ nearer the left edge, so set;
                                    \
                                    \   R7 = R11
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ So this sets the slope of the right edge
                                    \ and leaves the slope of the left edge as
                                    \ before [though I'm not sure why?]

 B       trin14                     \ Jump up to trin14 to draw the top part of
                                    \ the triangle

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 5 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a triangle with a horizontal edge between [x1, y1] and
\             [x2, y2]
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

.trin16

                                    \ If we get here then y1 = y2, so [x1, y1]
                                    \ and [x2, y2] share the same y-coordinate
                                    \ and the triangle has a horizontal edge
                                    \ between [x1, y1] and [x2, y2]

 SUBS    R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3
                                    \
                                    \ We also know that y1 = y2, so;
                                    \
                                    \   R9 = y2 - y3
                                    \      = y1 - y3

 LDMEQIA R13!, {PC}                 \ If R9 = 0 then y2 = y3, which means all
                                    \ three corners share the same y-coordinate
                                    \ and are at the same height on-screen
                                    \
                                    \ This means there is no triangle to draw,
                                    \ so return from the subroutine

 SUBS    R14, R0, R4                \ Set R14 = R0 - R4
                                    \         = x1 - x3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x3, y3], i.e. the delta
                                    \ x-coordinate between the two points
                                    \
                                    \ This will be negative if the line from
                                    \ [x1, y1] to [x3, y3] slopes up and to the
                                    \ right, or negative if the line slopes up
                                    \ and to the left

 RSBMI   R14, R0, R4                \ If R14 is negative, set R14 = x1 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x1 - x3|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin17                     \
                                    \   R14 = |x1 - x3| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ so jump to trin17 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R6, [R10, R9, LSL #2]      \ Set R6 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y3 - y1] / |x3 - x1|

 B       trin19                     \ Jump to trin19 to skip the following and
                                    \ keep going

.trin17

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x1 - x3| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R6, #0                     \ Set R6 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin18

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R6, R6, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R6]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin18                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y3 - y1] / |x3 - x1|

.trin19

 CMP     R4, R0                     \ If R4 - R0 < 0 then;
 RSBMI   R6, R6, #0                 \
                                    \   x3 - x1 < 0
                                    \
                                    \ so negate R6 to give R6 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R6 = [y3 - y1] / [x3 - x1]
                                    \
                                    \ So R6 contains the slope of the first side
                                    \ of the triangle, from [x1, y1] to [x3, y3]

                                    \ We now calculate the slope for the second
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R7 = [y2 - y3] / [x2 - x3]

 SUB     R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3

 SUBS    R14, R2, R4                \ Set R14 = R4 - R2
                                    \         = x3 - x2

 RSBMI   R14, R2, R4                \ If R14 is negative, set R4 = x2 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x2|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin20                     \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ so jump to trin20 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R7, [R10, R9, LSL #2]      \ Set R7 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y2 - y3] / |x3 - x2|

 B       trin22                     \ Jump to trin22 to skip the following and
                                    \ keep going


.trin20

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R7, #0                     \ Set R7 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin21

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R7, R7, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R7]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin21                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y2 - y3] / |x3 - x2|

.trin22

 CMP     R4, R2                     \ If R4 - R2 < 0 then;
 RSBMI   R7, R7, #0                 \
                                    \   x3 - x2 < 0
                                    \
                                    \ so negate R7 to give R7 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R7 = [y2 - y3] / [x3 - x2]
                                    \
                                    \ So R7 contains the slope of the second
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3]

 SUB     R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3

 MOV     R4, R0, LSL #16            \ Set R4 = R0 << 16
                                    \        = x1 << 16
                                    \
                                    \ We scale up R4 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R4 >> 16 gives us the integral part
                                    \
                                    \ We use R4 as the x-coordinate of the left
                                    \ end of the horizontal line to draw [after
                                    \ swapping the ends below if necessary]

 MOV     R5, R2, LSL #16            \ Set R5 = R0 << 16
                                    \        = x2 << 16
                                    \
                                    \ We scale up R5 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R5 >> 16 gives us the integral part
                                    \
                                    \ We use R5 as the x-coordinate of the right
                                    \ end of the horizontal line to draw [after
                                    \ swapping the ends below if necessary]

 ORR     R4, R4, #&8000             \ Set the top bit of the fractional parts of
 ORR     R5, R5, #&8000             \ both R4 and R5 [so that's 0.5]

 CMP     R4, R5                     \ If R4 >= R5 then x1 >= x2, so we need to
 MOVHS   R14, R6                    \ swap R6 and R7, and swap R4 and R5, to
 MOVHS   R6, R7                     \ ensure that R4 is the left end of the line
 MOVHS   R7, R14                    \ and R5 is the right end, and that the
 MOVHS   R14, R4                    \ slopes are swapped accordingly
 MOVHS   R4, R5
 MOVHS   R5, R14

 B       trin14                     \ Jump to trin14 in part 4 to draw the
                                    \ triangle

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 6 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a clipped triangle that's partly off-screen
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

.trin23

 CMP     R1, #239                   \ If all of the y-coordinates are off-screen
 CMPHS   R3, #239                   \ then return from the subroutine
 CMPHS   R5, #239
 MOVHS   PC, R14

 CMP     R0, #320                   \ If all of the x-coordinates are off-screen
 CMPHS   R2, #320                   \ then return from the subroutine
 CMPHS   R4, #320
 MOVHS   PC, R14

 STMFD   R13!, {R14}                \ Store the return address on the stack

 LDR     R12, screenAddr            \ Set R12 to the address of the screen bank
                                    \ we are drawing in, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

                                    \ We start by ordering the triangle corners
                                    \ by y-coordinate, so [x1, y1] is the
                                    \ furthest down the screen with the highest
                                    \ y-coordinate, followed by [x2, y2] then
                                    \ [x3, y3], at the same or higher level, and
                                    \ in that order

 CMP     R1, R3                     \ If R1 > R3, jump to trin24 to skip the
 BGT     trin24                     \ following, as y1 > y2 already

 MOV     R6, R0                     \ Swap [x1, y1] and [x2, y2], so we now have
 MOV     R0, R2                     \ y1 >= y2
 MOV     R2, R6
 MOV     R6, R1
 MOV     R1, R3
 MOV     R3, R6

.trin24

 CMP     R1, R5                     \ If R1 > R5, jump to trin25 to skip the
 BGT     trin25                     \ following, as y1 > y3 already

 MOV     R6, R0                     \ Swap [x1, y1] and [x3, y3], so we now have
 MOV     R0, R4                     \ y1 >= y3
 MOV     R4, R6
 MOV     R6, R1
 MOV     R1, R5
 MOV     R5, R6

.trin25

 CMP     R3, R5                     \ If R3 > R5, jump to trin26 to skip the
 BGT     trin26                     \ following, as y2 > y3 already

 MOV     R6, R2                     \ Swap [x2, y2] and [x3, y3], so we now have
 MOV     R2, R4                     \ y2 >= y3
 MOV     R4, R6
 MOV     R6, R3
 MOV     R3, R5
 MOV     R5, R6

                                    \ So the triangle coordinates are ordered
                                    \ like this on-screen;
                                    \
                                    \   [x3, y3]
                                    \   [x2, y2]
                                    \   [x1, y1]

.trin26

 MOV     R11, R1                    \ Set R11 = R1
                                    \         = y1
                                    \
                                    \ We use the value of R11 in part 11, when
                                    \ drawing the clipped triangle

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 7 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Calculate the slopes of [x1, y1] to [x2, y2] and [x1, y1] to
\             [x3, y3] for a clipped triangle
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ We now calculate the slope of the first
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x2, y2], as follows;
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]

 SUBS    R9, R1, R3                 \ Set R9 = R1 - R3
                                    \        = y1 - y2
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y1 >= y2

 BEQ     trin38                     \ If [x1, y1] and [x2, y2] share the same
                                    \ y-coordinate then the triangle has a
                                    \ horizontal edge between [x1, y1] and
                                    \ [x2, y2], so jump to part 10 to process
                                    \ this special case

 STMFD   R13!, {R4-R5}              \ Store the third corner's coordinates in
                                    \ [x3, y3] on the stack to we can retrieve
                                    \ them later

 SUBS    R14, R2, R0                \ Set R14 = R2 - R0
                                    \         = x2 - x1
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ x-coordinate between the two points
                                    \
                                    \ This will be positive if the line from
                                    \ [x1, y1] to [x2, y2] slopes up and to the
                                    \ right, or negative if the line slopes up
                                    \ and to the left

 RSBMI   R14, R2, R0                \ If R14 is negative, set R14 = x1 - x2, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x2 - x1|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin27                     \
                                    \   R14 = |x2 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y2] >= 64
                                    \
                                    \ so jump to trin27 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R6, [R10, R9, LSL #2]      \ Set R6 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y1 - y2] / |x2 - x1|

 B       trin29                     \ Jump to trin29 to skip the following and
                                    \ keep going

.trin27

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x2 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y2] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R6, #0                     \ Set R6 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin28

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R6, R6, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R6]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin28                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y1 - y2] / |x2 - x1|

.trin29

 CMP     R2, R0                     \ If R2 - R0 < 0 then;
 RSBMI   R6, R6, #0                 \
                                    \   x2 - x1 < 0
                                    \
                                    \ so negate R6 to give R6 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]
                                    \
                                    \ So R6 contains the slope of the first side
                                    \ of the triangle, from [x1, y1] to [x2, y2]

                                    \ We now calculate the slope for the second
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]

 SUBS    R9, R1, R5                 \ Set R9 = R1 - R5
                                    \        = y1 - y3

 SUBS    R14, R4, R0                \ Set R14 = R4 - R0
                                    \         = x3 - x1

 RSBMI   R14, R4, R0                \ If R14 is negative, set R4 = x1 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x1|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin30                     \
                                    \   R14 = |x3 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ so jump to trin30 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R7, [R10, R9, LSL #2]      \ Set R7 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y1 - y3] / |x3 - x1|

 B       trin32                     \ Jump to trin32 to skip the following and
                                    \ keep going


.trin30

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x1| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R7, #0                     \ Set R7 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin31

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R7, R7, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R7]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin31                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y1 - y3] / |x3 - x1|

.trin32

 CMP     R4, R0                     \ If R4 - R0 < 0 then;
 RSBMI   R7, R7, #0                 \
                                    \   x3 - x1 < 0
                                    \
                                    \ so negate R7 to give R7 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \
                                    \ So R7 contains the slope of the second
                                    \ side of the triangle, from [x1, y1] to
                                    \ [x3, y3]

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 8 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw the bottom part of a clipped triangle
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ By this point we have the following;
                                    \
                                    \   [R0, R1] = [x1, y1]
                                    \
                                    \   [R2, R3] = [x2, y2]
                                    \
                                    \   [x3, y3] is on the stack
                                    \
                                    \   R6 = [y1 - y2] / [x2 - x1]
                                    \      = slope of [x1, y1] to [x2, y2]
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \      = slope of [x1, y1] to [x3, y3]
                                    \
                                    \   R12 = screen address of the start of the
                                    \         pixel row containing [x1, y1]
                                    \
                                    \ [x1, y1] is the point lowest down the
                                    \ screen and [x3, y3] is the highest up the
                                    \ screen, with [x2, y2] the point in the
                                    \ middle [in terms of y-coordinate]
                                    \
                                    \ We now draw the triangle in two parts,
                                    \ effectively slicing the triangle in half
                                    \ with a horizontal line at y-coordinate y2,
                                    \ leaving two triangles to draw;
                                    \
                                    \   1. The triangle from [x1, y1] at the
                                    \      bottom up to the horizontal line with
                                    \      [x2, y2] at one end
                                    \
                                    \   2. The triangle from the horizontal line
                                    \      with [x2, y2] at one end, up to
                                    \      [x3, y3] at the top
                                    \
                                    \ We start at the bottom of the triangle, at
                                    \ [x1, y1], and step upwards by one pixel
                                    \ row at a time, drawing a horizontal line
                                    \ between the two sides, until we reach the
                                    \ level of [x2, y2]
                                    \
                                    \ As we step up each pixel row, we calculate
                                    \ the x-coordinates of each row we draw by
                                    \ adding the slopes in R6 and R7
                                    \
                                    \ We store the x-coordinates of the current
                                    \ horizontal line in R4 and R5, so these are
                                    \ the registers we update with the slope
                                    \ values

 SUBS    R9, R1, R3                 \ Set R9 = R1 - R3
                                    \        = y1 - y2
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x2, y2], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y1 >= y2,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y1 and y2

 MOV     R4, R0, LSL #16            \ Set R4 = R0 << 16
                                    \        = x1 << 16
                                    \
                                    \ We scale up R4 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R4 >> 16 gives us the integral part
                                    \
                                    \ We use R4 as the x-coordinate of the left
                                    \ end of the horizontal line to draw

 ORR     R4, R4, #&8000             \ Set R5 to x1, with the top bit of its
 MOV     R5, R4                     \ fractional part set [so that's 0.5]
                                    \
                                    \ We use R5 as the x-coordinate of the right
                                    \ end of the horizontal line to draw

 CMP     R6, R7                     \ If R6 > R7, swap R6 and R7, so we know
 MOVGT   R14, R6                    \ that R6 <= R7, i.e. R6 contains the side
 MOVGT   R6, R7                     \ with the lesser slope, which will be the
 MOVGT   R7, R14                    \ slope along the left edge of the triangle

 BL      trin45                     \ Call the subroutine in part 11 to draw the
                                    \ bottom part of the triangle, clipping it
                                    \ to the screen as we go

 LDMFD   R13!, {R0-R1}              \ Fetch the coordinates for the third point
                                    \ [x3, y3] from the stack and into [R0, R1]

 SUBS    R9, R3, R1                 \ Set R9 = R3 - R1
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y2 and y3

 LDMEQIA R13!, {PC}                 \ If R9 is zero then [x2, y2] and [x3, y3]
                                    \ are at the same y-coordinate, so there is
                                    \ nothing to draw in the top part of the
                                    \ triangle, so return from the subroutine as
                                    \ we are done

                                    \ We now calculate the slope for the third
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R14 = [y2 - y3] / [x2 - x1]

 SUBS    R14, R0, R2                \ Set R14 = R0 - R2
                                    \         = x3 - x2

 RSBMI   R14, R0, R2                \ If R14 is negative, set R4 = x2 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x2|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin33                     \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ so jump to trin33 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R12, [R10, R9, LSL #2]     \ Set R12 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R12 = R9 / R14
                                    \
                                    \       = [y2 - y3] / |x3 - x2|

 B       trin35                     \ Jump to trin35 to skip the following and
                                    \ keep going

.trin33

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ We now calculate R12 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R12, #0                    \ Set R12 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin34

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R12, R12, R10              \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R12]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin34                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R12 = R9 / R14
                                    \
                                    \       = [y2 - y3] / |x3 - x2|

.trin35

 CMP     R0, R2                     \ If R0 - R2 < 0 then;
 RSBMI   R12, R12, #0               \
                                    \   x3 - x2 < 0
                                    \
                                    \ so negate R12 to give R12 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R12 = [y2 - y3] / [x3 - x2]
                                    \
                                    \ So R12 contains the slope of the third
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3]

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 9 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw the top part of a clipped triangle
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

                                    \ By this point we have the following;
                                    \
                                    \   [R0, R1] = [x3, y3]
                                    \
                                    \   [R2, R3] = [x2, y2]
                                    \
                                    \   R4 = x-coordinate of the left edge for
                                    \        the last line that we drew
                                    \
                                    \   R5 = x-coordinate of the right edge for
                                    \        the last line that we drew
                                    \
                                    \   R7 = [y1 - y3] / [x3 - x1]
                                    \      = slope of [x1, y1] to [x3, y3]
                                    \
                                    \   R12 = [y2 - y3] / [x3 - x2]
                                    \       = slope of [x2, y2] to [x3, y3]

 SUB     R9, R3, R1                 \ Set R9 = R3 - R1
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3,
                                    \ so we can use this as a loop counter for
                                    \ drawing horizontal lines in the triangle
                                    \ between y-coordinates y2 and y3

                                    \ The following calculations are very
                                    \ similar to those in part 4 for the
                                    \ unclipped triangle, but the shifts keep
                                    \ the sign of the shifted register [they use
                                    \ ASR instead of LSR]

 SUBS    R14, R2, R4, ASR #16       \ Set;
                                    \
                                    \   R14 = R2 - R4 >> 16
                                    \       = x2 - left edge of horizontal line
                                    \
                                    \ So this will be zero if [x2, y2] is at the
                                    \ left edge of the horizontal line

 SUBNES  R10, R2, R5, ASR #16       \ If the above is non-zero, set;
                                    \
                                    \   R10 = R2 - R5 >> 16
                                    \       = x2 - right edge of horizontal line
                                    \
                                    \ So this will be zero if [x2, y2] is at the
                                    \ right edge of the horizontal line

 BNE     trin37                     \ If both of the above are non-zero, jump to
                                    \ trin37, as [x2, y2] doesn't match either
                                    \ edge of the horizontal line we just drew

 CMP     R2, R4, ASR #16            \ Set the flags on R2 - R4 >> 16

 MOVEQ   R6, R12                    \ If R2 = R4 >> 16, then x2 = left edge, so;
 BICEQ   R4, R4, #&FF00             \ so x2 must be at the left edge, so we set
 BICEQ   R4, R4, #&00FF             \ the slope of the left edge in R6 to R12,
 ORREQ   R4, R4, #&8000             \ as it's the left edge that is changing
                                    \ slope as we move into the top part of the
                                    \ triangle;
                                    \
                                    \   R6 = R12
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ We also reset the fractional part of R4
                                    \ [the left edge x-coordinate] to just the
                                    \ top bit set [so that's 0.5]
                                    \
                                    \ So this sets the slope of the left edge
                                    \ and leaves the slope of the right edge as
                                    \ before

 MOVNE   R7, R12                    \ If R2 <> R4 >> 16, then x2 <> left edge, 
 BICNE   R5, R5, #&FF00             \ so x2 must be at the right edge, so we set
 BICNE   R5, R5, #&00FF             \ the slope of the right edge in R7 to R12,
 ORRNE   R5, R5, #&8000             \ as it's the right edge that is changing
                                    \ slope as we move into the top part of the
                                    \ triangle;
                                    \ 
                                    \   R7 = R12
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ We also reset the fractional part of R5
                                    \ [the right edge x-coordinate] to just the
                                    \ top bit set [so that's 0.5]
                                    \
                                    \ So this sets the slope of the right edge
                                    \ and leaves the slope of the left edge as
                                    \ before

.trin36

 LDR     R12, screenAddr            \ Set R12 to the address of the screen bank
                                    \ we are drawing in, pointing just below
                                    \ the two lines of text at the top of the
                                    \ screen

 BL      trin45                     \ Call the subroutine in part 11 to draw the
                                    \ top part of the triangle, clipping it to
                                    \ the screen as we go

 LDMFD   R13!, {PC}                 \ Return from the subroutine

.trin37

                                    \ We jump here following these two
                                    \ calculations, if neither of them are zero;
                                    \
                                    \   R14 = R2 - R4 >> 16
                                    \       = x2 - left edge of horizontal line
                                    \
                                    \   R10 = R2 - R5 >> 16
                                    \       = x2 - right edge of horizontal line
                                    \
                                    \ I am not sure what this signifies!

 RSBMI   R10, R10, #0               \ The last calculation was for R10, so this
                                    \ makes R10 positive if the above result is
                                    \ negative, so it does this;
                                    \
                                    \   R10 = |R10|

 CMP     R14, #0                    \ Set the following;
 RSBMI   R14, R14, #0               \
                                    \   R14 = |R14|

                                    \ So R10 and R14 are set to the magnitudes
                                    \ of the distances between [x2, y2] and each
                                    \ edge of the horizontal line [R14 for left,
                                    \ R10 for right]

 CMP     R14, R10                   \ If |R14| >= |R10| then [x2, y2] is nearer
 MOVHS   R6, R12                    \ the right edge, so set;
                                    \
                                    \   R6 = R12
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ So this sets the slope of the left edge
                                    \ and leaves the slope of the right edge as
                                    \ before [though I'm not sure why?]

 MOVLO   R7, R12                    \ Otherwise |R14| < |R10| and [x2, y2] is
                                    \ nearer the left edge, so set;
                                    \
                                    \   R7 = R12
                                    \      = slope of [x2, y2] to [x3, y3]
                                    \
                                    \ So this sets the slope of the right edge
                                    \ and leaves the slope of the left edge as
                                    \ before [though I'm not sure why?]

 B       trin36                     \ Jump up to trin14 to draw the top part of
                                    \ the triangle

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 10 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a clipped triangle with a horizontal edge between [x1, y1]
\             and [x2, y2]
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

.trin38

                                    \ If we get here then y1 = y2, so [x1, y1]
                                    \ and [x2, y2] share the same y-coordinate
                                    \ and the triangle has a horizontal edge
                                    \ between [x1, y1] and [x2, y2]

 SUBS    R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x2, y2] and [x3, y3], i.e. the delta
                                    \ y-coordinate between the two points
                                    \
                                    \ We know this is positive because y2 >= y3
                                    \
                                    \ We also know that y1 = y2, so;
                                    \
                                    \   R9 = y2 - y3
                                    \      = y1 - y3

 LDMEQIA R13!, {PC}                 \ If R9 = 0 then y2 = y3, which means all
                                    \ three corners share the same y-coordinate
                                    \ and are at the same height on-screen
                                    \
                                    \ This means there is no triangle to draw,
                                    \ so return from the subroutine

 SUBS    R14, R0, R4                \ Set R14 = R0 - R4
                                    \         = x1 - x3
                                    \
                                    \ So this is the vertical distance between
                                    \ [x1, y1] and [x3, y3], i.e. the delta
                                    \ x-coordinate between the two points
                                    \
                                    \ This will be negative if the line from
                                    \ [x1, y1] to [x3, y3] slopes up and to the
                                    \ right, or negative if the line slopes up
                                    \ and to the left

 RSBMI   R14, R0, R4                \ If R14 is negative, set R14 = x1 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x1 - x3|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin39                     \
                                    \   R14 = |x1 - x3| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ so jump to trin39 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R6, [R10, R9, LSL #2]      \ Set R6 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y3 - y1] / |x3 - x1|

 B       trin41                     \ Jump to trin41 to skip the following and
                                    \ keep going

.trin39

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x1 - x3| >= 64
                                    \
                                    \   R9  = [y1 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R6, #0                     \ Set R6 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin40

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R6, R6, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R6]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin40                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R6 = R9 / R14
                                    \
                                    \      = [y3 - y1] / |x3 - x1|

.trin41

 CMP     R4, R0                     \ If R4 - R0 < 0 then;
 RSBMI   R6, R6, #0                 \
                                    \   x3 - x1 < 0
                                    \
                                    \ so negate R6 to give R6 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R6 = [y3 - y1] / [x3 - x1]
                                    \
                                    \ So R6 contains the slope of the first side
                                    \ of the triangle, from [x1, y1] to [x3, y3]

                                    \ We now calculate the slope for the second
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3], as follows;
                                    \
                                    \   R7 = [y2 - y3] / [x2 - x3]

 SUB     R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3

 SUBS    R14, R2, R4                \ Set R14 = R4 - R2
                                    \         = x3 - x2

 RSBMI   R14, R2, R4                \ If R14 is negative, set R4 = x2 - x3, so
                                    \ we have the following;
                                    \
                                    \   R14 = |x3 - x2|

 CMP     R14, #64                   \ If either R14 >= 64 or R9 >= 64, then at
 CMPLO   R9, #64                    \ least one of these is true;
 BHS     trin42                     \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ so jump to trin42 to calculate the slope
                                    \ using the shift-and-subtract algorithm

 LDR     R10, divisionTableAddr     \ Set R10 to the address of the division
                                    \ tables

 ADD     R10, R10, R14, LSL #8      \ Set R10 to the address of the division
                                    \ table containing n / R14, for n = 0 to 63
                                    \
                                    \ This works because each division table
                                    \ contains 64 words, or 256 bytes, so the
                                    \ address of table n / d is;
                                    \
                                    \   divisionTable + d * 256

 LDR     R7, [R10, R9, LSL #2]      \ Set R7 to the R9-th word in the division
                                    \ table containing n / R14, so this
                                    \ calculates the following;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y2 - y3] / |x3 - x2|

 B       trin44                     \ Jump to trin44 to skip the following and
                                    \ keep going


.trin42

                                    \ If we get here then at least one of these
                                    \ is true;
                                    \
                                    \   R14 = |x3 - x2| >= 64
                                    \
                                    \   R9  = [y2 - y3] >= 64
                                    \
                                    \ We now calculate R2 = R9 / R14 using the
                                    \ shift-and-subtract division algorithm

 MOV     R9, R9, LSL #16            \ First we scale R9 up as far as we can, to
                                    \ make the result as accurate as possible

 MOV     R7, #0                     \ Set R7 = 0 to contain the result

 MOV     R10, #&80000000            \ Set bit 31 of R10 so we can shift it to
                                    \ the right in each iteration, using it as a
                                    \ counter

.trin43

 MOVS    R14, R14, LSL #1           \ Shift R14 left, moving the top bit into
                                    \ the C flag

 CMPCC   R14, R9                    \ If we shifted a 0 out of the top of R14,
                                    \ test for a possible subtraction

 SUBCS   R14, R14, R9               \ If we shifted a 1 out of the top of R14 or
 ORRCS   R7, R7, R10                \ R14 >= R9, then do the subtraction;
                                    \
                                    \   R14 = R14 - R9
                                    \
                                    \ and set the relevant bit in the result
                                    \ [i.e. apply the set bit in R10 to the
                                    \ result in R7]

 MOVS    R10, R10, LSR #1           \ Shift R10 to the right, moving bit 0 into
                                    \ the C flag

 BCC     trin43                     \ Loop back until we shift the 1 out of the
                                    \ right end of R10 [after 32 shifts]

                                    \ So we now have the following result;
                                    \
                                    \   R7 = R9 / R14
                                    \
                                    \      = [y2 - y3] / |x3 - x2|

.trin44

 CMP     R4, R2                     \ If R4 - R2 < 0 then;
 RSBMI   R7, R7, #0                 \
                                    \   x3 - x2 < 0
                                    \
                                    \ so negate R7 to give R7 the correct sign
                                    \ for the following calculation;
                                    \
                                    \   R7 = [y2 - y3] / [x3 - x2]
                                    \
                                    \ So R7 contains the slope of the second
                                    \ side of the triangle, from [x2, y2] to
                                    \ [x3, y3]

 SUB     R9, R3, R5                 \ Set R9 = R3 - R5
                                    \        = y2 - y3

 MOV     R4, R0, LSL #16            \ Set R4 = R0 << 16
                                    \        = x1 << 16
                                    \
                                    \ We scale up R4 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R4 >> 16 gives us the integral part
                                    \
                                    \ We use R4 as the x-coordinate of the left
                                    \ end of the horizontal line to draw [after
                                    \ swapping the ends below if necessary]

 MOV     R5, R2, LSL #16            \ Set R5 = R0 << 16
                                    \        = x2 << 16
                                    \
                                    \ We scale up R5 so that it can contain a
                                    \ fractional result - we will treat the
                                    \ bottom 16 bits as the fraction, so
                                    \ R5 >> 16 gives us the integral part
                                    \
                                    \ We use R5 as the x-coordinate of the right
                                    \ end of the horizontal line to draw [after
                                    \ swapping the ends below if necessary]

 ORR     R4, R4, #&8000             \ Set the top bit of the fractional parts of
 ORR     R5, R5, #&8000             \ both R4 and R5 [so that's 0.5]

 CMP     R4, R5                     \ If R4 > R5 then x1 > x2, so we need to
 MOVGT   R14, R6                    \ swap R6 and R7, and swap R4 and R5, to
 MOVGT   R6, R7                     \ ensure that R4 is the left end of the line
 MOVGT   R7, R14                    \ and R5 is the right end, and that the
 MOVGT   R14, R4                    \ slopes are swapped accordingly
 MOVGT   R4, R5
 MOVGT   R5, R14

 B       trin36                     \ Jump to trin36 in part 9 to draw the
                                    \ triangle

\ ******************************************************************************
\
\       Name; DrawTriangle [Part 11 of 11]
\       Type; Subroutine
\   Category; Drawing triangles
\    Summary; Draw a triangle, clipping it to the screen as we go
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

.trin45

                                    \ We call this part of the routine as a
                                    \ subroutine, so returning from the
                                    \ subroutine in this context means returning
                                    \ to the main triangle code above
                                    \
                                    \ We call it with the following values;
                                    \
                                    \   R4 = x-coordinate of the left end of the
                                    \        horizontal line to draw
                                    \
                                    \   R5 = x-coordinate of the right end of
                                    \        the horizontal line to draw
                                    \
                                    \   R6 = left edge slope
                                    \
                                    \   R7 = right edge slope
                                    \
                                    \   R8 = four-pixel colour word
                                    \
                                    \   R9 = the delta y-coordinate between
                                    \        [x1, y1] and [x2, y2], which
                                    \        defines the height of the triangle
                                    \        to draw
                                    \
                                    \   R11 = y1, so that's the y-coordinate of
                                    \         the bottom point of the triangle
                                    \         to draw
                                    \
                                    \   R12 = screen address of the start of the
                                    \         pixel row containing [x1, y1]

 CMP     R9, #256                   \ If R9 >= 256 then the triangle to draw is
 MOVHS   PC, R14                    \ taller than the screen, which is just too
                                    \ big, so return from the subroutine without
                                    \ drawing this triangle

 STMFD   R13!, {R14}                \ Store the return address on the stack

                                    \ We now draw the triangle from bottom to
                                    \ top, keeping track of the y-coordinate of
                                    \ the current pixel row in R11 and counting
                                    \ down the pixel rows in R9

.trin46

 ADD     R4, R4, R6                 \ Set R4 = R4 + R6
                                    \        = R4 + slope of left edge
                                    \
                                    \ So this moves the x-coordinate of the left
                                    \ edge by the correct slope as we move up by
                                    \ one pixel row

 ADD     R5, R5, R7                 \ Set R5 = R5 + R7
                                    \        = R5 + slope of right edge
                                    \
                                    \ So this moves the x-coordinate of the
                                    \ right edge by the correct slope as we move
                                    \ up by one pixel row

 CMP     R11, #0                    \ If R11 < 0 then we are already off the top
 LDMMIIA R13!, {PC}                 \ of the screen, so return from the
                                    \ subroutine

 CMP     R11, #239                  \ If R11 >= 239 then we are off the bottom
 BHS     trin48                     \ of the screen, so jump to trin48 to move
                                    \ on to the next row above in the triangle,
                                    \ so the triangle is clipped to the bottom
                                    \ of the screen

 STMFD   R13!, {R11}                \ Store the y-coordinate of the current row
                                    \ on the stack so we can retrieve it below

 ADD     R11, R11, R11, LSL #2      \ Set R11 = R11 + R11 * 4
                                    \         = y-coord of row * 5

 ADD     R11, R12, R11, LSL #6      \ Set R11 = R12 + R11 * 64
                                    \         = screen address + 320 * y-coord
                                    \
                                    \ So R11 points to the start of the pixel
                                    \ row in screen memory

 CMP     R4, #&01400000             \ If R4 > &01400000, then the left end of
 BPL     trin47                     \ the line is greater than &140 [as the
                                    \ fractional number &01400000 represents the
                                    \ integer &0140], so it is greater than 320
                                    \ and past the right edge of the screen
                                    \
                                    \ This means the whole line is off-screen,
                                    \ so jump to trin47 to move on to the next
                                    \ row above in the triangle

 CMP     R5, #0                     \ If R5 < 0, then the right end of the line
 BMI     trin47                     \ is off the left edge of the screen, so
                                    \ jump to trin47 to move on to the next row
                                    \ above in the triangle

 CMP     R4, #0                     \ If the left end of the line is positive,
 MOVPL   R0, R4, LSR #16            \ then it's on-screen, so set;
                                    \
                                    \   R0 = R4 >> 16
                                    \      = x-coordinate of left end of line
                                    \
                                    \ The shift removes the fractional part from
                                    \ R4

 ADDPL   R11, R11, R4, LSR #16      \ If the left end of the line is positive,
                                    \ set R11 = R11 + R4 >> 16
                                    \         = screen address of row + x1
                                    \
                                    \ So R11 contains the screen address of the
                                    \ left end of the line to draw

 MOVMI   R0, #0                     \ If the left end of the line is negative,
                                    \ i.e. off the left edge of the screen, then
                                    \ set R0 = 0

                                    \ So R0 is set to the x-coordinate of the
                                    \ left end of the line we want to draw

 CMP     R5, #&01400000             \ If R5 < &01400000, then the right end of
 RSBLO   R10, R0, R5, LSR #16       \ the line is less than &140 [as the
                                    \ fractional number &01400000 represents the
                                    \ integer &0140], so it is less than 320
                                    \ and is not past the right edge of the
                                    \ screen, so set;
                                    \
                                    \   R10 = R5 >> 16 - R0
                                    \       = x-coordinate of right end of line
                                    \         - x-coordinate of left end of line

 RSBHS   R10, R0, #320              \ If the right end of the line is off the
                                    \ right side of the screen, set;
                                    \
                                    \   R10 = 320 - x-coordinate of left end of
                                    \         line

                                    \ So R10 contains the length of the line
                                    \ from the left edge to the right edge

 CMP     R10, #0                    \ If R10 is positive then we draw a line
 BLPL    DrawHorizontalLine         \ from the left edge to the right edge,
                                    \ using the four-pixel colour word that was
                                    \ passed to the DrawTriangle routine in R8

.trin47

 LDMFD   R13!, {R11}                \ Retrieve the y-coordinate of the current
                                    \ row from the stack, which we stored above,
                                    \ and put it into R11 once more

.trin48

 SUB     R11, R11, #1               \ Decrement R11 so we move one pixel line up
                                    \ the screen

 SUBS    R9, R9, #1                 \ Decrement the triangle height counter in
                                    \ R9

 BNE     trin46                     \ Loop back to draw the next line in the
                                    \ triangle until we have drawn all R9
                                    \ horizontal lines

 LDMFD   R13!, {PC}                 \ Return from the subroutine [and rejoin the
                                    \ main triangle routine]

\ ******************************************************************************
\
\       Name; lineJump
\       Type; Variable
\   Category; Drawing lines
\    Summary; Jump table for drawing a horizontal line of between 0 and 17
\             pixels using the relevant entry point in DrawLineSegment
\  Deep dive; Drawing triangles
\
\ ******************************************************************************

.lineJump

 EQUD    DrawLineSegment + 17 * 4   \ Draw a horizontal line of 0 pixels
 EQUD    DrawLineSegment + 16 * 4   \ Draw a horizontal line of 1 pixels
 EQUD    DrawLineSegment + 15 * 4   \ Draw a horizontal line of 2 pixels
 EQUD    DrawLineSegment + 14 * 4   \ Draw a horizontal line of 3 pixels
 EQUD    DrawLineSegment + 13 * 4   \ Draw a horizontal line of 4 pixels
 EQUD    DrawLineSegment + 12 * 4   \ Draw a horizontal line of 5 pixels
 EQUD    DrawLineSegment + 11 * 4   \ Draw a horizontal line of 6 pixels
 EQUD    DrawLineSegment + 10 * 4   \ Draw a horizontal line of 7 pixels
 EQUD    DrawLineSegment + 9 * 4    \ Draw a horizontal line of 8 pixels
 EQUD    DrawLineSegment + 8 * 4    \ Draw a horizontal line of 9 pixels
 EQUD    DrawLineSegment + 7 * 4    \ Draw a horizontal line of 10 pixels
 EQUD    DrawLineSegment + 6 * 4    \ Draw a horizontal line of 11 pixels
 EQUD    DrawLineSegment + 5 * 4    \ Draw a horizontal line of 12 pixels
 EQUD    DrawLineSegment + 4 * 4    \ Draw a horizontal line of 13 pixels
 EQUD    DrawLineSegment + 3 * 4    \ Draw a horizontal line of 14 pixels
 EQUD    DrawLineSegment + 2 * 4    \ Draw a horizontal line of 15 pixels
 EQUD    DrawLineSegment + 1 * 4    \ Draw a horizontal line of 16 pixels
 EQUD    DrawLineSegment            \ Draw a horizontal line of 17 pixels

\ ******************************************************************************
\
\       Name; DrawHorizontalLine
\       Type; Subroutine
\   Category; Drawing lines
\    Summary; Draw a horizontal line
\  Deep dive; Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R8                              Line colour [in the form of a four-pixel
\                                   colour word]
\
\   R10                             Line length
\
\   R11                             Screen address of the left end of the line
\
\ ******************************************************************************

.DrawHorizontalLine

 CMP     R10, #18                   \ If R10 < 18 then set the flags so the next
                                    \ set of instructions are run [so for lines
                                    \ of length 17 pixels and fewer, we jump to
                                    \ the relevant routine in the lineJump to
                                    \ draw the line

]

 varOffset = P% + 8 - lineJump      : REM Set labOffset to the offset back to the
                                    : REM lineJump table from the next instruction

[

 OPT    pass%

 ADDLO   R0, PC, R10, LSL #2        \ If R10 < 18, jump to the address in entry
 LDRLO   PC, [R0, #-varOffset]      \ R10 in the lineJump table, which will draw
                                    \ a horizontal line R10 pixels long and
                                    \ return from the subroutine using a tail
                                    \ call

                                    \ If we get here then we need to draw a line
                                    \ of 18 pixels or more

 ADD     R10, R11, R10              \ Set R10 to the screen address of the right
                                    \ end of the line [i.e. the address of the
                                    \ left end in R11 plus the width in R10]

 BIC     R0, R10, #%00000011        \ Set R0 to the screen address with bits 0
                                    \ and 1 cleared, so R0 points to the word in
                                    \ screen memory that contains the right end
                                    \ of the line [the 'right cap']

                                    \ So by this point R11 points to the word in
                                    \ screen memory that contains the left cap
                                    \ of the line [which is the word containing
                                    \ the left end of the line], and R0 points
                                    \ to the right cap
                                    \
                                    \ The caps can contain 1, 2, 3 or 4 pixels,
                                    \ as each 32-bit word contains four one-byte
                                    \ pixels
                                    \
                                    \ If a cap contains four pixels, then bits 0
                                    \ and 1 of the cap address will be zero, so
                                    \ in the following we draw the line as
                                    \ follows;
                                    \
                                    \   * If the left cap contains 1, 2 or 3
                                    \     pixels, draw those pixels
                                    \
                                    \   * Draw the portion of the line between
                                    \     the left and right caps, drawing it
                                    \     one word [four pixels] at a time
                                    \
                                    \   * If the right cap contains 1, 2 or 3
                                    \     pixels, draw those pixels
                                    \
                                    \ We start with the left cap at screen
                                    \ address R11

 TST     R11, #%00000011            \ If R11 is not a multiple of 4 then one or
 STRNEB  R8, [R11], #1              \ both of bits 0 and 1 will be non-zero, so
                                    \ set the pixel at R11 to the colour in R8
                                    \ and increment R11
                                    \
                                    \ So this draws the pixel at the end of the
                                    \ line, and it increments R11 so R11 now
                                    \ points to the next pixel along

 TSTNE   R11, #%00000011            \ If R11 is still not a multiple of 4 then
 STRNEB  R8, [R11], #1              \ bits 0 and 1 will still be non-zero, so
                                    \ set the pixel at R11 to the colour in R8
                                    \ and increment R11
                                    \
                                    \ So this draws the second pixel in the line
                                    \ if drawing the first one didn't take us to
                                    \ a word boundary, and it increments R11 so
                                    \ R11 now points to the next pixel along

 TSTNE   R11, #%00000011            \ If R11 is still not a multiple of 4 then
 STRNEB  R8, [R11], #1              \ bits 0 and 1 will still be non-zero, so
                                    \ set the pixel at R11 to the colour in R8
                                    \ and increment R11
                                    \
                                    \ So this draws the third pixel in the line
                                    \ if drawing the first two didn't take us to
                                    \ a word boundary, and it increments R11 so
                                    \ R11 now points to the next pixel along

                                    \ By this point R11 will definitely be on a
                                    \ word boundary and we have successfully
                                    \ drawn the left cap of the line, so now we
                                    \ draw the centre portion of the line, all
                                    \ the way to the right cap
                                    \
                                    \ We do this in a loop that is unrolled once
                                    \ to speed things up a little but

.hlin1

 STR     R8, [R11], #4              \ Draw a full word [four pixels] in the
                                    \ colour in R8 at screen address R11 and
                                    \ increment R11 by 4 to move on to the next
                                    \ word

 CMP     R11, R0                    \ If R11 < R0 then we have not yet reached
 STRLO   R8, [R11], #4              \ the right cap, so draw another word in
                                    \ memory and increment R11 again

 CMPLO   R11, R0                    \ If R11 < R0 then we have still not reached
 BLO     hlin1                      \ the right cap, so loop back to hlin1 to
                                    \ keep drawing the centre portion of the
                                    \ line until we do reach the right cap

 CMP     R11, R10                   \ If R11 < R10 then we have not yet reached
 STRLOB  R8, [R11], #1              \ the end of the line, so we draw up to
 CMPLO   R11, R10                   \ three pixels in the final word of the line
 STRLOB  R8, [R11], #1              \ by simply drawing each pixel, incrementing
 CMPLO   R11, R10                   \ R11 and then re-checking whether R11 has
 STRLOB  R8, [R11], #1              \ reached R10 [and drawing the next pixel if
                                    \ it hasn't]

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; DrawLineSegment
\       Type; Subroutine
\   Category; Drawing lines
\    Summary; Draw a horizontal line of between 0 and 17 pixels by jumping to
\             the relevant entry point
\  Deep dive; Drawing triangles
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R8                              Line colour [in the form of a four-pixel
\                                   colour word]
\
\   R11                             Screen address of left end of line
\
\ ******************************************************************************

.DrawLineSegment

 STRB    R8, [R11, #16]             \ Draw a horizontal line of 17 pixels

 STRB    R8, [R11, #15]             \ Draw a horizontal line of 16 pixels

 STRB    R8, [R11, #14]             \ Draw a horizontal line of 15 pixels

 STRB    R8, [R11, #13]             \ Draw a horizontal line of 14 pixels

 STRB    R8, [R11, #12]             \ Draw a horizontal line of 13 pixels

 STRB    R8, [R11, #11]             \ Draw a horizontal line of 12 pixels

 STRB    R8, [R11, #10]             \ Draw a horizontal line of 11 pixels

 STRB    R8, [R11, #9]              \ Draw a horizontal line of 10 pixels

 STRB    R8, [R11, #8]              \ Draw a horizontal line of 9 pixels

 STRB    R8, [R11, #7]              \ Draw a horizontal line of 8 pixels

 STRB    R8, [R11, #6]              \ Draw a horizontal line of 7 pixels

 STRB    R8, [R11, #5]              \ Draw a horizontal line of 6 pixels

 STRB    R8, [R11, #4]              \ Draw a horizontal line of 5 pixels

 STRB    R8, [R11, #3]              \ Draw a horizontal line of 4 pixels

 STRB    R8, [R11, #2]              \ Draw a horizontal line of 3 pixels

 STRB    R8, [R11, #1]              \ Draw a horizontal line of 2 pixels

 STRB    R8, [R11]                  \ Draw a horizontal line of 1 pixel

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; workspaceAddr
\       Type; Variable
\   Category; Start and end
\    Summary; The address of the game's variable workspace
\
\ ******************************************************************************

.workspaceAddr

 EQUD    workspace

\ ******************************************************************************
\
\       Name; stackAddr
\       Type; Variable
\   Category; Start and end
\    Summary; The address of the game's stack
\
\ ******************************************************************************

.stackAddr

 EQUD    stack

\ ******************************************************************************
\
\       Name; memoryTestAddr
\       Type; Variable
\   Category; Start and end
\    Summary; The memory location to check to ensure we have enough memory for
\             the game
\
\ ******************************************************************************

.memoryTestAddr

 EQUD    0

\ ******************************************************************************
\
\       Name; initialScore
\       Type; Variable
\   Category; Score bar
\    Summary; The score at the start of each game
\
\ ******************************************************************************

.initialScore

 EQUD    500

\ ******************************************************************************
\
\       Name; initialHighScore
\       Type; Variable
\   Category; Score bar
\    Summary; The high score when we first load the game
\
\ ******************************************************************************

.initialHighScore

 EQUD    500

\ ******************************************************************************
\
\       Name; initialFuelLevel
\       Type; Variable
\   Category; Score bar
\    Summary; The fuel level at the start of each new game
\
\ ******************************************************************************

.initialFuelLevel

 EQUD    3413

\ ******************************************************************************
\
\       Name; stackPointerOnEntry
\       Type; Variable
\   Category; Start and end
\    Summary; Stores the stack pointer from when the game was run
\
\ ******************************************************************************

.stackPointerOnEntry

 EQUD    0

\ ******************************************************************************
\
\       Name; mouseParameters
\       Type; Variable
\   Category; Player
\    Summary; The parameters for OS_Word 21,3 for resetting the mouse position
\
\ ******************************************************************************

.mouseParameters

 EQUB 3                             \ This is OS_Word 21,3, so the reason code
                                    \ in the first byte is 3

 EQUW 511                           \ The X position for the mouse

 EQUW 511                           \ The Y position for the mouse

 OPT     FN_AlignWithZeroes

\ ******************************************************************************
\
\       Name; mouseParametersAddr
\       Type; Variable
\   Category; Player
\    Summary; The address of the OS_Word block for resetting the mouse position
\
\ ******************************************************************************

.mouseParametersAddr

 EQUD    mouseParameters

\ ******************************************************************************
\
\       Name; ResetMousePosition
\       Type; Subroutine
\   Category; Player
\    Summary; Reset the mouse position to [511, 511], ready for the game
\
\ ******************************************************************************

.ResetMousePosition

 LDR     R1, mouseParametersAddr    \ Call OS_Word 21,3 to reset the mouse
 MOV     R0, #21                    \ position to [511, 511]
 SWI     OS_Word

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; AbortWithMemoryError
\       Type; Subroutine
\   Category; Start and end
\    Summary; Show a memory error and abort the game
\
\ ******************************************************************************

.AbortWithMemoryError

 STRB    R1, [R0]                   \ Restore the byte that we have been poking
                                    \ in the Entry routine to determine whether
                                    \ there is enough memory

 SWI     OS_WriteS                  \ Print an error explaining that there isn't
 EQUS    "There is not enough "     \ enough free memory to run the game
 EQUS    "memory to run this "
 EQUS    "game."
 EQUW    &0D0A
 EQUS    "Please *CONFIGURE your "
 EQUS    "machine to free more "
 EQUS    "memory."
 EQUB    0
 OPT     FN_AlignWithZeroes

 B       ReturnToDesktop            \ Jump to ReturnToDesktop to quit the game
                                    \ and return to the desktop

\ ******************************************************************************
\
\       Name; Entry
\       Type; Subroutine
\   Category; Start and end
\    Summary; The main entry point for the game
\
\ ******************************************************************************

.Entry

 MOV     R0, #22                    \ Change to screen mode 15, which uses 160K
 SWI     OS_WriteC                  \ of screen memory, the same amount that we
 MOV     R0, #15                    \ need for the game [though in the game we
 SWI     OS_WriteC                  \ actually use two mode 13 screens, each of
                                    \ which is 80K]

 STR     R13, stackPointerOnEntry   \ Store the stack pointer from when the game
                                    \ started in stackPointerOnEntry, so we can
                                    \ retore it when we quit the game

 LDR     R13, stackAddr             \ Set the stack pointer in R13 to stackAddr,
                                    \ so the 512 bytes descending from stackAddr
                                    \ can be used as the game's stack

 LDR     R11, workspaceAddr         \ Set R11 to the address of the workspace,
                                    \ which is stored in workspaceAddr, so we
                                    \ can access workspace variables by applying
                                    \ an offset to R11 using [R11, #offset]

 STMFD   R13!, {R14}                \ R14 contains the address we should return
                                    \ to when quitting the game, so store it on
                                    \ the stack so we can retrieve it later

 LDR     R0, memoryTestAddr         \ Set R0 to the address in memoryTestAddr,
                                    \ which points to a byte that will be
                                    \ writable if there is enough free memory
                                    \ for running the game

 LDRB    R1, [R0]                   \ Set R1 to the current contents of memory
                                    \ at R0, so we can restore it after running
                                    \ our memory tests

 MOV     R2, #&AA                   \ Store the value &AA in our test memory
 STRB    R2, [R0]                   \ location in R0, read it back, and if the
 LDRB    R3, [R0]                   \ returned value is different to &AA, jump
 CMP     R2, R3                     \ to AbortWithMemoryError to abort the game,
 BNE     AbortWithMemoryError       \ as there isn't enough free memory

 MOV     R2, #&55                   \ Store the value &55 in our test memory
 STRB    R2, [R0]                   \ location in R0, read it back, and if the
 LDRB    R3, [R0]                   \ returned value is different to &55, jump
 CMP     R2, R3                     \ to AbortWithMemoryError to abort the game,
 BNE     AbortWithMemoryError       \ as there isn't enough free memory

 STRB    R1, [R0]                   \ Restore the original contents of address
                                    \ R0 so it's unchanged by our memory tests

 MOV     R0, #4                     \ Call OS_Byte 4 to set the cursor keys to
 MOV     R1, #1                     \ return ASCII values, so they don't move
 MOV     R2, #0                     \ the cursor during the game
 SWI     OS_Byte

 BL      InitialiseParticleData     \ Initialise the particle data buffer and
                                    \ associated variables

 MOV     R0, #22                    \ Change to screen mode 13 [320×256 pixels
 SWI     OS_WriteC                  \ with 256 colours], which will display
 MOV     R0, #13                    \ screen bank 1
 SWI     OS_WriteC

 SWI     OS_WriteS                  \ Write the game's title into the top line
 EQUS    "Lander Demo/Practice "    \ of the screen, where it remains for the
 EQUS    "(C) D.J.Braben 1987"      \ whole game
 EQUB    0
 OPT     FN_AlignWithZeroes

 BL      SwitchScreenBank           \ Switch screen bank, so VDU commands go to
                                    \ screen bank 2

 MOV     R0, #22                    \ Change to screen mode 13 with shadow
 SWI     OS_WriteC                  \ memory enabled [i.e. mode 128 + 13], which
 MOV     R0, #128+13                \ creates two mode 13 screen banks
 SWI     OS_WriteC                  \ 

 MOV     R0, #23                    \ Start printing the following VDU command;
 SWI     OS_WriteC                  \ 
 MOV     R0, #1                     \   VDU 23, 1, 0, 0, 0, 0, 0, 0, 0, 0
 SWI     OS_WriteC                  \ 
                                    \ which disables the cursor

 MOV     R8, #8                     \ We now want to print the eight zeroes in
                                    \ the above command, so set a loop counter
                                    \ in R8

.entr1

 MOV     R0, #0                     \ This loop prints the eight zeroes in the
 SWI     OS_WriteC                  \ above VDU command

 SUBS    R8, R8, #1                 \ Decrement the loop counter

 BNE     entr1                      \ Loop back until we have printed all eight
                                    \ zeroes

 SWI     OS_WriteS                  \ Write the game's title into the top line
 EQUS    "Lander Demo/Practice "    \ of screen bank 2, so the same title
 EQUS    "(C) D.J.Braben 1987"      \ appears at the top of both screen banks
 EQUB    0
 OPT     FN_AlignWithZeroes

 LDR     R0, initialScore           \ Initialise currentScore to the score that
 STR     R0, [R11, #currentScore]   \ we start each game with, which is set in
                                    \ initialScore

 LDR     R0, initialHighScore       \ Initialise highScore to the high score
 STR     R0, [R11, #highScore]      \ that we start the game with, which is set
                                    \ in initialHighScore

\ ******************************************************************************
\
\       Name; StartNewGame
\       Type; Subroutine
\   Category; Main loop
\    Summary; Start a brand new game with a full set of lives and a newly
\             generated set of objects
\
\ ******************************************************************************

.StartNewGame

                                    \ We start by initialising the scores and
                                    \ printing them on the score bar

 LDR     R0, [R11, #highScore]      \ Set R0 to the current high score

 LDR     R1, [R11, #currentScore]   \ Set R1 to our current score

 CMP     R1, R0                     \ If R1 - R0 is positive, i.e. R1 >= R0,
 MOVPL   R0, R1                     \ then our latest score is higher than the
                                    \ high score, so set R0 to our latest score
                                    \
                                    \ So R0 is set to the maximum of highScore
                                    \ and currentScore, which is the new high
                                    \ score

 STRHS   R0, [R11, #highScore]      \ If R1 >= R0 then we just updated the high
                                    \ score and the new high score is in R0, so
                                    \ store the new high score in highScore

 MOV     R1, #35                    \ Set [R1, R2] = [35, 1] so the following
 MOV     R2, #1                     \ call to PrintScoreInBothBanks prints the
                                    \ high score at column 35 on row 1

 BL      PrintScoreInBothBanks      \ Print the high score in R0 at column 35 on
                                    \ row 1, at the right end of the score bar

 LDR     R0, initialScore           \ Initialise currentScore to the score that
 STR     R0, [R11, #currentScore]   \ we start each game with, which is set in
                                    \ initialScore

                                    \ We now initialise more game variables

 LDR     R0, initialFuelLevel       \ Initialise fuelLevel to the fuel level
 STR     R0, [R11, #fuelLevel]      \ that we start each game with, which is set
                                    \ in initialFuelLevel

 MOV     R0, #&30000                \ Initialise gravity to &30000
 STR     R0, [R11, #gravity]

 MOV     R0, #3                     \ Initialise the number of lives to 3
 STR     R0, [R11, #remainingLives]

\ ******************************************************************************
\
\       Name; PlaceObjectsOnMap
\       Type; Subroutine
\   Category; 3D objects
\    Summary; Randomly place a number of objects on the map, avoiding the sea
\             and the launchpad
\  Deep dive; Placing objects on the map
\
\ ------------------------------------------------------------------------------
\
\ The object map at objectMap contains one byte for each tile on the landscape.
\ This byte determines which object [if any] appears on that tile, where objects
\ are trees, buildings, rockets and so on.
\
\ ******************************************************************************

.PlaceObjectsOnMap

                                    \ We start by initialising the object map
                                    \ at objectMap with values of &FF, which
                                    \ indicates no objects on the map

 MVN     R0, #0                     \ Set R0 to R3 to &FF so we can poke them
 MVN     R1, #0                     \ into memory at objectMap [this sets each
 MVN     R2, #0                     \ 32-bit register to &FFFFFFFF, which is the
 MVN     R3, #0                     \ same as four bytes, each of which is &FF]

 MOV     R4, #256*256               \ Set R4 to use as a byte counter in the
                                    \ following loop, which works through each
                                    \ of the coordinates in the 256x256 map

 ADD     R6, R11, #objectMap        \ Set R6 to the address of the object map

.snew1

 STMIA   R6!, {R0-R3}               \ Store four words, or 16 bytes, at R6,
                                    \ updating R6 as we go, with each byte
                                    \ containing &FF

 SUBS    R4, R4, #16                \ Subtract 16 from the byte counter in R4
                                    \ as we just initialised 16 bytes

 BNE     snew1                      \ Loop back until we have set all bytes in
                                    \ the object map to &FF

                                    \ We now add 2048 randomly chosen 3D objects
                                    \ to the object map, each one at a random
                                    \ coordinate and of a random type

 ADD     R6, R11, #objectMap        \ Set R6 to the address of the object map

 MOV     R5, #2048                  \ Set R5 to a loop counter as we work
                                    \ through all 2048 objects

.snew2

 BL      GetRandomNumbers           \ Set R0 and R1 to random numbers

 MOV     R8, R0                     \ Set R8 = R0, so R8 is a random number
                                    \
                                    \ We use the top byte of R8 below as the
                                    \ x-coordinate of the 3D object

 MOV     R9, R0, LSL #8             \ Set R9 = R0 << 8
                                    \
                                    \ We use the top byte of R9 below as the
                                    \ z-coordinate of the 3D object, so the
                                    \ shift ensures that the top bytes of R8
                                    \ and R9 are different

 STMFD   R13!, {R0}                 \ Store R0 on the stack so it doesn't get
                                    \ corrupted by the following call to
                                    \ GetLandscapeAltitude

 BL      GetLandscapeAltitude       \ Set R0 to the altitude of the landscape at
                                    \ coordinates [x, z] = [R8, R9]

 MOV     R14, R0                    \ Set R14 to the landscape altitude returned
                                    \ in R0

 LDMFD   R13!, {R0}                 \ Retrieve the value of R0 from the stack
                                    \ that we stored above

 CMP     R14, #SEA_LEVEL            \ If R14 = LAUNCHPAD_ALTITUDE or SEA_LEVEL,
 CMPNE   R14, #LAUNCHPAD_ALTITUDE   \ jump to snew3 to skip the following, so we
 BEQ     snew3                      \ do not place any objects on the sea or the
                                    \ launchpad

 AND     R0, R0, #7                 \ Reduce R0 to the range 1 to 8, so this is
 ADD     R0, R0, #1                 \ a random number that we use to determine
                                    \ the type of object we're adding [so there
                                    \ are lots of trees];
                                    \
                                    \   * 1 = small leafy tree
                                    \   * 2 = tall leafy tree
                                    \   * 3 = small leafy tree
                                    \   * 4 = small leafy tree
                                    \   * 5 = gazebo
                                    \   * 6 = tall leafy tree
                                    \   * 7 = fir tree
                                    \   * 8 = building
                                    \
                                    \ See the objectTypes table for details of
                                    \ object types

 AND     R9, R9, #&FF000000         \ Set the bottom three bytes of R9 to zero,
                                    \ leaving just the top byte, so we can use
                                    \ it in the following

                                    \ The object at coordinate [x, z] is stored
                                    \ at offset &zzxx within objectMap, where
                                    \ &xx and &zz are the top bytes of the full
                                    \ 32-bit coordinates
                                    \
                                    \ In the following we set this address in
                                    \ R14;
                                    \
                                    \   objectMap + [R8 >> 24] + [R9 >> 16]
                                    \
                                    \ R8 is shifted into the bottom byte of R14,
                                    \ so that's the x-coordinate, and R9 is
                                    \ shifted into the second byte of R14, so
                                    \ that's the z-coordinate

 ADD     R14, R6, R8, LSR #24       \ Set R14 = R6 + [R8 >> 24] + [R9 >> 16]
 ADD     R14, R14, R9, LSR #16

 STRB    R0, [R14]                  \ Store R0 in the address in R14 to add the
                                    \ object to the map at the coordinates given
                                    \ by the top bytes of R8 and R9, at [R8, R9]

.snew3

 SUBS    R5, R5, #1                 \ Decrement the loop counter

 BPL     snew2                      \ Loop back until we have done all &800
                                    \ iterations

                                    \ We now place three rockets along the right
                                    \ edge of the launchpad, with a rocket on
                                    \ every other tile, working from front to
                                    \ back, into the screen and parallel to the
                                    \ z-axis, and with each one having an
                                    \ x-coordinate of 7

 MOV     R0, #LAUNCHPAD_OBJECT      \ Set R0 to the type of object along the
                                    \ right edge of the launchpad, which is a
                                    \ rocket of type 9

 STRB    R0, [R6, #&0107]           \ Add the front rocket to coordinate [7, 1]

 STRB    R0, [R6, #&0307]           \ Add the middle rocket to coordinate [7, 3]

 STRB    R0, [R6, #&0507]           \ Add the rear rocket to coordinate [7, 5]

\ ******************************************************************************
\
\       Name; PlacePlayerOnLaunchpad
\       Type; Subroutine
\   Category; Player
\    Summary; The main entry point for the game
\
\ ******************************************************************************

.PlacePlayerOnLaunchpad

 LDR     R0, [R11, #remainingLives] \ Set R0 to the number of remaining lives

 MOV     R1, #30                    \ Set [R1, R2] = [30, 1] so the following
 MOV     R2, #1                     \ call to PrintScoreInBothBanks prints the
                                    \ number of lives at column 30 on row 1

 BL      PrintScoreInBothBanks      \ Print the number of lives in R0 at column
                                    \ 30 on row 1, just before the high score
                                    \ towards the right end of the score bar

 MVN     R0, #0                     \ Set playingGame = -1 to flag that the game
 STR     R0, [R11, #playingGame]    \ is being played and that this is not the
                                    \ crash animation

 MOV     R0, #0                     \ Set xCamera = 0 and zCamera = 0
 STR     R0, [R11, #xCamera]        \
 STR     R0, [R11, #zCamera]        \ This doesn't have any effect as the camera
                                    \ position is set at the start of the main
                                    \ loop by the call to MoveAndDrawPlayer,
                                    \ which overwrites these values

 STR     R0, [R11, #shipDirection]  \ Set shipDirection = 0 so the ship faces
                                    \ right when the game starts [though this is
                                    \ quickly corrected when the game uses the
                                    \ mouse coordinates to calculate the
                                    \ direction]

 MOV     R0, #1                     \ Set shipPitch = 1 so the ship is very
 STR     R0, [R11, #shipPitch]      \ slightly pitched up for take-off

 MOV     R0, #LAUNCHPAD_SIZE/2      \ Set the starting coordinates of the
 MOV     R2, R0                     \ player's ship as follows;
 ADD     R3, R11, #xPlayer          \
 MOV     R1, #LAUNCHPAD_Y           \   xPlayer = LAUNCHPAD_SIZE / 2
 STMIA   R3!, {R0-R2}               \   yPlayer = LAUNCHPAD_Y
                                    \   zPlayer = LAUNCHPAD_SIZE / 2
                                    \
                                    \ which is in the middle of the launchpad

 MOV     R0, #0                     \ Set the player's velocity to zero as
 MOV     R1, #0                     \ follows;
 MOV     R2, #0                     \
 STMIA   R3!, {R0-R2}               \  xVelocity = 0
                                    \  yVelocity = 0
                                    \  zVelocity = 0

 BL      ResetMousePosition         \ Reset the mouse position to [511, 511],
                                    \ ready for the game

\ ******************************************************************************
\
\       Name; MainLoop
\       Type; Subroutine
\   Category; Main loop
\    Summary; The main game loop
\  Deep dive; The main game loop
\
\ ******************************************************************************

.MainLoop

 MOV     R0, #129                   \ Call OS_Byte 129 to read the keyboard with
 MOV     R1, #0                     \ the time limit in R1 and R2 [so that's
 MOV     R2, #0                     \ with no time limit as R1 and R2 are zero],
 SWI     OS_Byte                    \ returning the result in R2

 TEQ     R2, #&1B                   \ If R2 = &1B then an escape condition
 BEQ     EndGame                    \ occurred during the keyboard scan [in
                                    \ other words, Escape was pressed], so jump
                                    \ to EndGame to acknowledge the escape
                                    \ condition and quit the game

 BL      MoveAndDrawPlayer          \ Move the player's ship and draw it into
                                    \ the graphics buffers

                                    \ We now set up the rotation matrix for the
                                    \ rocks, using the main loop counter to
                                    \ generate rotation angles that change along
                                    \ with the main loop [so the rocks spin at a
                                    \ nice steady speed]

 LDR     R0, [R11, #mainLoopCount]  \ Set R0 = mainLoopCount << 24
 MOV     R0, R0, LSL #24

 MOV     R1, R0, LSL #1             \ Set R1 = mainLoopCount << 25

 BL      CalculateRotationMatrix    \ Calculate the rotation matrix from the
                                    \ 'angles' given in R0 and R1, which we can
                                    \ apply to any rocks we draw in the
                                    \ MoveAndDrawParticles routine [as rocks are
                                    \ only rotating 3D objects apart from the
                                    \ player, and the player calculates its own
                                    \ rotation matrix]

 BL      DropRocksFromTheSky        \ If the score is 800 or more, then randomly
                                    \ drop rocks from the sky

 BL      MoveAndDrawParticles       \ Move and draw all the particles, such as
                                    \ smoke clouds and bullets, into the
                                    \ graphics buffers

 BL      DrawObjects                \ Draw all the objects, such as trees and
                                    \ buildings, into the graphics buffers

 BL      AddTerminatorsToBuffers    \ Add terminators to the ends of the
                                    \ graphics buffers so we know when to stop
                                    \ drawing

 BL      DrawLandscapeAndBuffers    \ Draw the landscape and the contents of the
                                    \ graphics buffers

 BL      PrintCurrentScore          \ Print the number of remaining bullets at
                                    \ the left end of the score bar

 BL      DrawFuelLevel              \ Draw the fuel bar

 BL      SwitchScreenBank           \ Switch screen banks and clear the newly
                                    \ hidden screen bank to black

 LDR     R14, [R11, #mainLoopCount] \ Increment the main loop counter
 ADD     R14, R14, #1
 STR     R14, [R11, #mainLoopCount]

 B       MainLoop                   \ Loop back to repeat the main loop

\ ******************************************************************************
\
\       Name; EndGame
\       Type; Subroutine
\   Category; Main loop
\    Summary; Finish the game
\
\ ******************************************************************************

.EndGame

 MOV     R0, #126                   \ Call OS_Byte 126 to acknowledge the escape
 SWI     OS_Byte                    \ condition caused by the player pressing
                                    \ Escape in the main loop

 MOV     R0, #22                    \ Change to screen mode 0
 SWI     OS_WriteC
 MOV     R0, #0
 SWI     OS_WriteC

 MOV     R0, #4                     \ Call OS_Byte 4 to set the cursor keys to
 MOV     R1, #0                     \ move the cursor, so they work normally
 MOV     R2, #0                     \ again
 SWI     OS_Byte

\ ******************************************************************************
\
\       Name; ReturnToDesktop
\       Type; Subroutine
\   Category; Start and end
\    Summary; Return to the desktop
\
\ ******************************************************************************

.ReturnToDesktop

 LDMFD   R13!, {R14}                \ Restore the value from the stack and store
                                    \ it in R14, so R14 contains the same value
                                    \ that it had when the game was first run,
                                    \ and which we stored on the stack in the
                                    \ Entry routine
                                    \
                                    \ So this sets R14 to the address we should
                                    \ return to when quitting the game

 LDR     R13, stackPointerOnEntry   \ Set R13 to the value that we stored in the
                                    \ Entry routine, so that the stack pointer
                                    \ is restored to the value that it had when
                                    \ the game was first run, and which we
                                    \ stored in stackPointerOnEntry in the Entry
                                    \ routine

 MOV     PC, R14                    \ Exit from the game by jumping to the
                                    \ address in R14, which will return us to
                                    \ the Desktop [or wherever the game was run
                                    \ from]

\ ******************************************************************************
\
\       Name; PrintHexNumber
\       Type; Subroutine
\   Category; Score bar
\    Summary; An unused routine that prints an 8-digit hexadecimal number on the
\             second character row of the screen
\  Deep dive; Unused code in Lander
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R1                              The number to print
\
\ ******************************************************************************

.PrintHexNumber

 MOV     R0, #30                    \ Print a VDU 30 command to move the text
 SWI     OS_WriteC                  \ cursor to the top-left corner of the
                                    \ screen

 MOV     R0, #&0A                   \ Print a line feed [ASCII &0A] to move the
 SWI     OS_WriteC                  \ cursor down one line, to the start of the
                                    \ second line, which is where we print the
                                    \ score bar

 STMFD   R13!, {R0-R12, R14}        \ Store the registers that we want to use on
                                    \ the stack so they can be preserved

 MOV     R0, R1, LSR #28            \ Print the top nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #24            \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #20            \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #16            \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #12            \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #8             \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1, LSR #4             \ Print the next nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, R1                     \ Print the bottom nibble of the value in R1
 BL      PrintHexDigit

 MOV     R0, #&A                    \ Print a line feed [ASCII &0A] and carriage
 SWI     OS_WriteC                  \ return [ASCII &0D] to move the cursor down
 MOV     R0, #&D                    \ to the start of the next line, ready to
 SWI     OS_WriteC                  \ print further numbers if required

 LDMFD   R13!, {R0-R12, PC}         \ Retrieve the registers that we stored on
                                    \ the stack and return from the subroutine

\ ******************************************************************************
\
\       Name; PrintHexDigit
\       Type; Subroutine
\   Category; Score bar
\    Summary; An unused routine that prints a single digit hexadecimal number in
\             the score bar
\  Deep dive; Unused code in Lander
\
\ ------------------------------------------------------------------------------
\
\ Arguments;
\
\   R0                              The number to print [only the low nibble is
\                                   printed, the rest of the number is ignored]
\
\ ******************************************************************************

.PrintHexDigit

 AND     R0, R0, #&F                \ Extract the low nibble from the number in
                                    \ R0

 CMP     R0, #&A                    \ If the low nibble in R0 >= &A then the hex
 ADDHS   R0, R0, #&37               \ digit is A to F, so add &37 to get the
                                    \ corresponding hex digit [so this converts
                                    \ &A into ASCII &37 + &A = &41, which gives
                                    \ us 'A', the hex digit we want]

 ADDLO   R0, R0, #&30               \ Otherwise the low nibble in R0 is 0 to 9,
                                    \ so add the ASCII value of '0' [ASCII &30]
                                    \ to get the corresponding hex digit

 SWI     OS_WriteC                  \ Print the character in R0

 MOV     PC, R14                    \ Return from the subroutine

\ ******************************************************************************
\
\       Name; objectRock
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for a rock
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectRock

 EQUD    6                          \ Number of vertices

 EQUD    8                          \ Number of faces

 EQUD    objectRockFaces - objectRock

 EQUD    %00000011                  \ Flags; Bit 0 = 1 = object rotates
                                    \        Bit 1 = 0 = object has a shadow

.objectRockVertices

    \      xObject,   yObject,   zObject
 EQUD &00000000 : EQUD &00000000 : EQUD &00A00000        \ Vertex 0
 EQUD &00A00000 : EQUD &00A00000 : EQUD &00000000        \ Vertex 1
 EQUD &FF600000 : EQUD &00A00000 : EQUD &00000000        \ Vertex 2
 EQUD &00A00000 : EQUD &FF600000 : EQUD &00000000        \ Vertex 3
 EQUD &FF600000 : EQUD &FF600000 : EQUD &00000000        \ Vertex 4
 EQUD &00000000 : EQUD &00000000 : EQUD &FF600000        \ Vertex 5

.objectRockFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &54DA5200 : EQUD &54DA5200 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &444 \ 0
 EQUD &54DA5200 : EQUD &00000000 : EQUD &54DA5200 : EQUD 0 : EQUD 3 : EQUD 1 : EQUD &444 \ 1
 EQUD &00000000 : EQUD &AB25AE00 : EQUD &54DA5200 : EQUD 0 : EQUD 4 : EQUD 3 : EQUD &444 \ 2
 EQUD &AB25AE00 : EQUD &00000000 : EQUD &54DA5200 : EQUD 0 : EQUD 2 : EQUD 4 : EQUD &444 \ 3
 EQUD &00000000 : EQUD &54DA5200 : EQUD &AB25AE00 : EQUD 5 : EQUD 1 : EQUD 2 : EQUD &444 \ 4
 EQUD &54DA5200 : EQUD &00000000 : EQUD &AB25AE00 : EQUD 5 : EQUD 3 : EQUD 1 : EQUD &444 \ 5
 EQUD &00000000 : EQUD &AB25AE00 : EQUD &AB25AE00 : EQUD 5 : EQUD 4 : EQUD 3 : EQUD &444 \ 6
 EQUD &AB25AE00 : EQUD &00000000 : EQUD &AB25AE00 : EQUD 5 : EQUD 2 : EQUD 4 : EQUD &444 \ 7

\ ******************************************************************************
\
\       Name; objectPyramid
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for a pyramid
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectPyramid

 EQUD    5                          \ Number of vertices

 EQUD    6                          \ Number of faces

 EQUD    objectPyramidFaces - objectPyramid

 EQUD    %00000001                  \ Flags; Bit 0 = 1 = object rotates
                                    \        Bit 1 = 0 = object has no shadow

.objectPyramidVertices

    \      xObject,   yObject,   zObject
 EQUD &00000000 : EQUD &01000000 : EQUD &00000000        \ Vertex 0
 EQUD &00C00000 : EQUD &FF800000 : EQUD &00C00000        \ Vertex 1
 EQUD &FF400000 : EQUD &FF800000 : EQUD &00C00000        \ Vertex 2
 EQUD &00C00000 : EQUD &FF800000 : EQUD &FF400000        \ Vertex 3
 EQUD &FF400000 : EQUD &FF800000 : EQUD &FF400000        \ Vertex 4

.objectPyramidFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &35AA66D2 : EQUD &6B54CDA5 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &800 \ 0
 EQUD &6B54CDA5 : EQUD &35AA66D2 : EQUD &00000000 : EQUD 0 : EQUD 3 : EQUD 1 : EQUD &088 \ 1
 EQUD &00000000 : EQUD &35AA66D2 : EQUD &94AB325B : EQUD 0 : EQUD 4 : EQUD 3 : EQUD &880 \ 2
 EQUD &94AB325B : EQUD &35AA66D2 : EQUD &00000000 : EQUD 0 : EQUD 2 : EQUD 4 : EQUD &808 \ 3
 EQUD &00000000 : EQUD &88000000 : EQUD &00000000 : EQUD 1 : EQUD 2 : EQUD 3 : EQUD &444 \ 4
 EQUD &00000000 : EQUD &88000000 : EQUD &00000000 : EQUD 2 : EQUD 3 : EQUD 4 : EQUD &008 \ 5

\ ******************************************************************************
\
\       Name; objectPlayer
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the player's ship
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectPlayer

 EQUD    9                          \ Number of vertices

 EQUD    9                          \ Number of faces

 EQUD    objectPlayerFaces - objectPlayer

 EQUD    %00000011                  \ Flags; Bit 0 = 1 = object rotates
                                    \        Bit 1 = 0 = object has a shadow

.objectPlayerVertices

    \      xObject,   yObject,   zObject

 EQUD &01000000 : EQUD &00500000 : EQUD &00800000        \ Vertex 0
 EQUD &01000000 : EQUD &00500000 : EQUD &FF800000        \ Vertex 1
 EQUD &00000000 : EQUD &000A0000 : EQUD &FECCCCCD        \ Vertex 2
 EQUD &FF19999A : EQUD &00500000 : EQUD &00000000        \ Vertex 3
 EQUD &00000000 : EQUD &000A0000 : EQUD &01333333        \ Vertex 4
 EQUD &FFE66667 : EQUD &FF880000 : EQUD &00000000        \ Vertex 5
 EQUD &00555555 : EQUD &00500000 : EQUD &00400000        \ Vertex 6
 EQUD &00555555 : EQUD &00500000 : EQUD &FFC00000        \ Vertex 7
 EQUD &FFCCCCCD : EQUD &00500000 : EQUD &00000000        \ Vertex 8

.objectPlayerFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &457C441A : EQUD &9E2A1F4C : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 5 : EQUD &080 \ 0
 EQUD &35F5D83B : EQUD &9BC03EC1 : EQUD &DA12D71D : EQUD 1 : EQUD 2 : EQUD 5 : EQUD &040 \ 1
 EQUD &35F5D83B : EQUD &9BC03EC1 : EQUD &25ED28E3 : EQUD 0 : EQUD 5 : EQUD 4 : EQUD &040 \ 2
 EQUD &B123D51C : EQUD &AF3F50EE : EQUD &D7417278 : EQUD 2 : EQUD 3 : EQUD 5 : EQUD &040 \ 3
 EQUD &B123D51D : EQUD &AF3F50EE : EQUD &28BE8D88 : EQUD 3 : EQUD 4 : EQUD 5 : EQUD &040 \ 4
 EQUD &F765D8CD : EQUD &73242236 : EQUD &DF4FD176 : EQUD 1 : EQUD 2 : EQUD 3 : EQUD &088 \ 5
 EQUD &F765D8CD : EQUD &73242236 : EQUD &20B02E8A : EQUD 0 : EQUD 3 : EQUD 4 : EQUD &088 \ 6
 EQUD &00000000 : EQUD &78000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 3 : EQUD &044 \ 7
 EQUD &00000000 : EQUD &78000000 : EQUD &00000000 : EQUD 6 : EQUD 7 : EQUD 8 : EQUD &C80 \ 8

\ ******************************************************************************
\
\       Name; objectSmallLeafyTree
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the small leafy tree
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectSmallLeafyTree

 EQUD    11                         \ Number of vertices

 EQUD    5                          \ Number of faces

 EQUD    objectSmallLeafyTreeFaces - objectSmallLeafyTree

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectSmallLeafyTreeVertices

    \      xObject,   yObject,   zObject
 EQUD &00300000 : EQUD &FE800000 : EQUD &00300000        \ Vertex 0
 EQUD &FFD9999A : EQUD &00000000 : EQUD &00000000        \ Vertex 1
 EQUD &00266666 : EQUD &00000000 : EQUD &00000000        \ Vertex 2
 EQUD &00000000 : EQUD &FEF33334 : EQUD &FF400000        \ Vertex 3
 EQUD &00800000 : EQUD &FF400000 : EQUD &FF800000        \ Vertex 4
 EQUD &FF400000 : EQUD &FECCCCCD : EQUD &FFD55556        \ Vertex 5
 EQUD &FF800000 : EQUD &FEA66667 : EQUD &00400000        \ Vertex 6
 EQUD &00800000 : EQUD &FE59999A : EQUD &002AAAAA        \ Vertex 7
 EQUD &00C00000 : EQUD &FEA66667 : EQUD &FFC00000        \ Vertex 8
 EQUD &FFA00000 : EQUD &FECCCCCD : EQUD &00999999        \ Vertex 9
 EQUD &00C00000 : EQUD &FF400000 : EQUD &00C00000        \ Vertex 10

.objectSmallLeafyTreeFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &14A01873 : EQUD &AF8F9F93 : EQUD &56A0681E : EQUD 0 : EQUD 9 : EQUD 10 : EQUD &040 \ 0
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &400 \ 1
 EQUD &499A254E : EQUD &B123FC2C : EQUD &CB6D5299 : EQUD 0 : EQUD 3 : EQUD 4 : EQUD &080 \ 2
 EQUD &E4D2EEBE : EQUD &8DC82837 : EQUD &E72FE5E9 : EQUD 0 : EQUD 5 : EQUD 6 : EQUD &080 \ 3
 EQUD &D5710585 : EQUD &B29EF364 : EQUD &AEC07EB3 : EQUD 0 : EQUD 7 : EQUD 8 : EQUD &080 \ 4

\ ******************************************************************************
\
\       Name; objectTallLeafyTree
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the tall leafy tree
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectTallLeafyTree

 EQUD    14                         \ Number of vertices

 EQUD    6                          \ Number of faces

 EQUD    objectTallLeafyTreeFaces - objectTallLeafyTree

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectTallLeafyTreeVertices

    \      xObject,   yObject,   zObject
 EQUD &0036DB6D : EQUD &FD733334 : EQUD &00300000        \ Vertex 0
 EQUD &FFD00000 : EQUD &00000000 : EQUD &00000000        \ Vertex 1
 EQUD &00300000 : EQUD &00000000 : EQUD &00000000        \ Vertex 2
 EQUD &00000000 : EQUD &FE0CCCCD : EQUD &FF400000        \ Vertex 3
 EQUD &00800000 : EQUD &FE59999A : EQUD &FF800000        \ Vertex 4
 EQUD &FF533334 : EQUD &FE333334 : EQUD &FFC92493        \ Vertex 5
 EQUD &FF400000 : EQUD &FEA66667 : EQUD &00600000        \ Vertex 6
 EQUD &00000000 : EQUD &FF19999A : EQUD &FF666667        \ Vertex 7
 EQUD &FF800000 : EQUD &FF400000 : EQUD &FFA00000        \ Vertex 8
 EQUD &FFA00000 : EQUD &FE800000 : EQUD &00999999        \ Vertex 9
 EQUD &00C00000 : EQUD &FECCCCCD : EQUD &00C00000        \ Vertex 10
 EQUD &FFB33334 : EQUD &FF19999A : EQUD &00E66666        \ Vertex 11
 EQUD &00800000 : EQUD &FF400000 : EQUD &00C00000        \ Vertex 12
 EQUD &00300000 : EQUD &FE59999A : EQUD &00300000        \ Vertex 13

.objectTallLeafyTreeFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &FD3D01DD : EQUD &D2CB371E : EQUD &6F20024E : EQUD 0 : EQUD 9 : EQUD 10 : EQUD &040 \ 0
 EQUD &1E6F981A : EQUD &BB105ECE : EQUD &5D638B16 : EQUD 13 : EQUD 11 : EQUD 12 : EQUD &080 \ 1
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &400 \ 2
 EQUD &49D96509 : EQUD &B8E72762 : EQUD &C19E3A19 : EQUD 0 : EQUD 3 : EQUD 4 : EQUD &080 \ 3
 EQUD &AD213B74 : EQUD &B641CA5D : EQUD &2DC40650 : EQUD 0 : EQUD 5 : EQUD 6 : EQUD &040 \ 4
 EQUD &C9102051 : EQUD &AC846CAD : EQUD &BD92A8C1 : EQUD 13 : EQUD 7 : EQUD 8 : EQUD &040 \ 5

\ ******************************************************************************
\
\       Name; objectSmokingRemainsLeft
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the smoking remains that bend to the left
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectSmokingRemainsLeft

 EQUD    5                          \ Number of vertices

 EQUD    2                          \ Number of faces

 EQUD    objectSmokingRemainsLeftFaces - objectSmokingRemainsLeft

 EQUD    %00000000                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has no shadow

.objectSmokingRemainsLeftVertices

    \      xObject,   yObject,   zObject
 EQUD &FFD9999A : EQUD &00000000 : EQUD &00000000        \ Vertex 0
 EQUD &00266666 : EQUD &00000000 : EQUD &00000000        \ Vertex 1
 EQUD &002B3333 : EQUD &FFC00000 : EQUD &00000000        \ Vertex 2
 EQUD &00300000 : EQUD &FF800000 : EQUD &00000000        \ Vertex 3
 EQUD &FFD55556 : EQUD &FECCCCCD : EQUD &00000000        \ Vertex 4

.objectSmokingRemainsLeftFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 3 : EQUD &000 \ 0
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 2 : EQUD 3 : EQUD 4 : EQUD &000 \ 1

\ ******************************************************************************
\
\       Name; objectSmokingRemainsRight
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the smoking remains that bend to the right
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectSmokingRemainsRight

 EQUD    5                          \ Number of vertices

 EQUD    2                          \ Number of faces

 EQUD    objectSmokingRemainsRightFaces - objectSmokingRemainsRight

 EQUD    %00000000                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has no shadow

.objectSmokingRemainsRightVertices

    \      xObject,   yObject,   zObject
 EQUD &002AAAAA : EQUD &00000000 : EQUD &00000000        \ Vertex 0
 EQUD &FFD55556 : EQUD &00000000 : EQUD &00000000        \ Vertex 1
 EQUD &FFD4CCCD : EQUD &FFD00000 : EQUD &00000000        \ Vertex 2
 EQUD &FFD00000 : EQUD &FFA00000 : EQUD &00000000        \ Vertex 3
 EQUD &002AAAAA : EQUD &FEA66667 : EQUD &00000000        \ Vertex 4

.objectSmokingRemainsRightFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 3 : EQUD &000 \ 0
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 2 : EQUD 3 : EQUD 4 : EQUD &000 \ 1

\ ******************************************************************************
\
\       Name; objectFirTree
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the fir tree
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectFirTree

 EQUD    5                          \ Number of vertices

 EQUD    2                          \ Number of faces

 EQUD    objectFirTreeFaces - objectFirTree

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectFirTreeVertices

    \      xObject,   yObject,   zObject
 EQUD &FFA00000 : EQUD &FFC92493 : EQUD &FFC92493        \ Vertex 0
 EQUD &00600000 : EQUD &FFC92493 : EQUD &FFC92493        \ Vertex 1
 EQUD &00000000 : EQUD &FE333334 : EQUD &0036DB6D        \ Vertex 2
 EQUD &00266666 : EQUD &00000000 : EQUD &00000000        \ Vertex 3
 EQUD &FFD9999A : EQUD &00000000 : EQUD &00000000        \ Vertex 4

.objectFirTreeFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 2 : EQUD 3 : EQUD 4 : EQUD &400 \ 0
 EQUD &00000000 : EQUD &E0B0E050 : EQUD &8C280943 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &040 \ 1

\ ******************************************************************************
\
\       Name; objectGazebo
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the gazebo
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectGazebo

 EQUD    13                         \ Number of vertices

 EQUD    8                          \ Number of faces

 EQUD    objectGazeboFaces - objectGazebo

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectGazeboVertices

    \      xObject,   yObject,   zObject
 EQUD &00000000 : EQUD &FF000000 : EQUD &00000000        \ Vertex 0
 EQUD &FF800000 : EQUD &FF400000 : EQUD &00800000        \ Vertex 1
 EQUD &FF800000 : EQUD &FF400000 : EQUD &FF800000        \ Vertex 2
 EQUD &00800000 : EQUD &FF400000 : EQUD &FF800000        \ Vertex 3
 EQUD &00800000 : EQUD &FF400000 : EQUD &00800000        \ Vertex 4
 EQUD &FF800000 : EQUD &00000000 : EQUD &00800000        \ Vertex 5
 EQUD &FF800000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 6
 EQUD &00800000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 7
 EQUD &00800000 : EQUD &00000000 : EQUD &00800000        \ Vertex 8
 EQUD &FF99999A : EQUD &FF400000 : EQUD &00800000        \ Vertex 9
 EQUD &FF99999A : EQUD &FF400000 : EQUD &FF800000        \ Vertex 10
 EQUD &00666666 : EQUD &FF400000 : EQUD &FF800000        \ Vertex 11
 EQUD &00666666 : EQUD &FF400000 : EQUD &00800000        \ Vertex 12

.objectGazeboFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &00000000 : EQUD &78000000 : EQUD 1 : EQUD 5 : EQUD 9 : EQUD &444 \ 0
 EQUD &00000000 : EQUD &00000000 : EQUD &88000000 : EQUD 2 : EQUD 6 : EQUD 10 : EQUD &444 \ 1
 EQUD &00000000 : EQUD &94AB325B : EQUD &35AA66D2 : EQUD 0 : EQUD 1 : EQUD 4 : EQUD &400 \ 2
 EQUD &00000000 : EQUD &00000000 : EQUD &88000000 : EQUD 3 : EQUD 7 : EQUD 11 : EQUD &444 \ 3
 EQUD &00000000 : EQUD &00000000 : EQUD &78000000 : EQUD 4 : EQUD 8 : EQUD 12 : EQUD &444 \ 4
 EQUD &CA55992E : EQUD &94AB325B : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &840 \ 5
 EQUD &35AA66D2 : EQUD &94AB325B : EQUD &00000000 : EQUD 0 : EQUD 3 : EQUD 4 : EQUD &840 \ 6
 EQUD &00000000 : EQUD &94AB325B : EQUD &CA55992E : EQUD 0 : EQUD 2 : EQUD 3 : EQUD &400 \ 7

\ ******************************************************************************
\
\       Name; objectBuilding
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the building
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectBuilding

 EQUD    16                         \ Number of vertices

 EQUD    12                         \ Number of faces

 EQUD    objectBuildingFaces - objectBuilding

 EQUD    %00000000                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has no shadow

.objectBuildingVertices

    \      xObject,   yObject,   zObject
 EQUD &FF19999A : EQUD &FF266667 : EQUD &00000000        \ Vertex 0
 EQUD &FF400000 : EQUD &FF266667 : EQUD &00000000        \ Vertex 1
 EQUD &00C00000 : EQUD &FF266667 : EQUD &00000000        \ Vertex 2
 EQUD &00E66666 : EQUD &FF266667 : EQUD &00000000        \ Vertex 3
 EQUD &FF19999A : EQUD &FF8CCCCD : EQUD &00A66666        \ Vertex 4
 EQUD &FF19999A : EQUD &FF8CCCCD : EQUD &FF59999A        \ Vertex 5
 EQUD &00E66666 : EQUD &FF8CCCCD : EQUD &00A66666        \ Vertex 6
 EQUD &00E66666 : EQUD &FF8CCCCD : EQUD &FF59999A        \ Vertex 7
 EQUD &FF400000 : EQUD &FF666667 : EQUD &00800000        \ Vertex 8
 EQUD &FF400000 : EQUD &FF666667 : EQUD &FF800000        \ Vertex 9
 EQUD &00C00000 : EQUD &FF666667 : EQUD &00800000        \ Vertex 10
 EQUD &00C00000 : EQUD &FF666667 : EQUD &FF800000        \ Vertex 11
 EQUD &FF400000 : EQUD &00000000 : EQUD &00800000        \ Vertex 12
 EQUD &FF400000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 13
 EQUD &00C00000 : EQUD &00000000 : EQUD &00800000        \ Vertex 14
 EQUD &00C00000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 15

.objectBuildingFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &99CD0E6D : EQUD &3EE445CC : EQUD 0 : EQUD 4 : EQUD 6 : EQUD &400 \ 0
 EQUD &00000000 : EQUD &99CD0E6D : EQUD &3EE445CC : EQUD 0 : EQUD 3 : EQUD 6 : EQUD &400 \ 1
 EQUD &88000000 : EQUD &00000000 : EQUD &00000000 : EQUD 1 : EQUD 8 : EQUD 9 : EQUD &DDD \ 2
 EQUD &78000000 : EQUD &00000000 : EQUD &00000000 : EQUD 2 : EQUD 10 : EQUD 11 : EQUD &555 \ 3
 EQUD &88000000 : EQUD &00000000 : EQUD &00000000 : EQUD 8 : EQUD 12 : EQUD 13 : EQUD &FFF \ 4
 EQUD &88000000 : EQUD &00000000 : EQUD &00000000 : EQUD 8 : EQUD 9 : EQUD 13 : EQUD &FFF \ 5
 EQUD &78000000 : EQUD &00000000 : EQUD &00000000 : EQUD 10 : EQUD 14 : EQUD 15 : EQUD &777 \ 6
 EQUD &78000000 : EQUD &00000000 : EQUD &00000000 : EQUD 10 : EQUD 11 : EQUD 15 : EQUD &777 \ 7
 EQUD &00000000 : EQUD &00000000 : EQUD &88000000 : EQUD 9 : EQUD 13 : EQUD 15 : EQUD &BBB \ 8
 EQUD &00000000 : EQUD &00000000 : EQUD &88000000 : EQUD 9 : EQUD 11 : EQUD 15 : EQUD &BBB \ 9
 EQUD &00000000 : EQUD &99CD0E6D : EQUD &C11BBA34 : EQUD 0 : EQUD 5 : EQUD 7 : EQUD &800 \ 10
 EQUD &00000000 : EQUD &99CD0E6D : EQUD &C11BBA34 : EQUD 0 : EQUD 3 : EQUD 7 : EQUD &800 \ 11

\ ******************************************************************************
\
\       Name; objectSmokingBuilding
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the smoking remains of a building
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectSmokingBuilding

 EQUD    6                          \ Number of vertices

 EQUD    6                          \ Number of faces

 EQUD    objectSmokingBuildingFaces - objectSmokingBuilding

 EQUD    %00000000                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has no shadow

.objectSmokingBuildingVertices

    \      xObject,   yObject,   zObject
 EQUD &FF400000 : EQUD &00000001 : EQUD &00800000        \ Vertex 0
 EQUD &FF400000 : EQUD &00000001 : EQUD &FF800000        \ Vertex 1
 EQUD &00C00000 : EQUD &00000001 : EQUD &00800000        \ Vertex 2
 EQUD &00C00000 : EQUD &00000001 : EQUD &FF800000        \ Vertex 3
 EQUD &FF400000 : EQUD &FF99999A : EQUD &00800000        \ Vertex 4
 EQUD &00C00000 : EQUD &FFB33334 : EQUD &FF800000        \ Vertex 5

.objectSmokingBuildingFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &78000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &000 \ 0
 EQUD &00000000 : EQUD &78000000 : EQUD &00000000 : EQUD 1 : EQUD 2 : EQUD 3 : EQUD &000 \ 1
 EQUD &00000000 : EQUD &00000000 : EQUD &78000000 : EQUD 0 : EQUD 2 : EQUD 4 : EQUD &333 \ 2
 EQUD &88000000 : EQUD &00000000 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 4 : EQUD &666 \ 3
 EQUD &78000000 : EQUD &00000000 : EQUD &00000000 : EQUD 2 : EQUD 3 : EQUD 5 : EQUD &555 \ 4
 EQUD &00000000 : EQUD &00000000 : EQUD &88000001 : EQUD 1 : EQUD 3 : EQUD 5 : EQUD &777 \ 5

\ ******************************************************************************
\
\       Name; objectSmokingGazebo
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the smoking remains of a gazebo
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectSmokingGazebo

 EQUD    6                          \ Number of vertices

 EQUD    4                          \ Number of faces

 EQUD    objectSmokingGazeboFaces - objectSmokingGazebo

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectSmokingGazeboVertices

    \      xObject,   yObject,   zObject
 EQUD &00000000 : EQUD &FF8CCCCD : EQUD &FFF00000        \ Vertex 0
 EQUD &00199999 : EQUD &FF8CCCCD : EQUD &FFF00000        \ Vertex 1
 EQUD &00800000 : EQUD &00000000 : EQUD &00800000        \ Vertex 2
 EQUD &FF800000 : EQUD &00000000 : EQUD &00800000        \ Vertex 3
 EQUD &00800000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 4
 EQUD &FF800000 : EQUD &00000000 : EQUD &FF800000        \ Vertex 5

.objectSmokingGazeboFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &A24BB5BE : EQUD &4AF6A1AD : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &000 \ 0
 EQUD &00000000 : EQUD &A24BB5BE : EQUD &4AF6A1AD : EQUD 0 : EQUD 1 : EQUD 3 : EQUD &333 \ 1
 EQUD &00000000 : EQUD &AC59C060 : EQUD &A9F5EA98 : EQUD 0 : EQUD 1 : EQUD 4 : EQUD &444 \ 2
 EQUD &00000000 : EQUD &AC59C060 : EQUD &A9F5EA98 : EQUD 0 : EQUD 1 : EQUD 5 : EQUD &000 \ 3

\ ******************************************************************************
\
\       Name; objectRocket
\       Type; Variable
\   Category; 3D objects
\    Summary; Object blueprint for the rocket
\  Deep dive; Object blueprints
\
\ ******************************************************************************

.objectRocket

 EQUD    13                         \ Number of vertices

 EQUD    8                          \ Number of faces

 EQUD    objectRocketFaces - objectRocket

 EQUD    %00000010                  \ Flags; Bit 0 = 0 = object is static
                                    \        Bit 1 = 0 = object has a shadow

.objectRocketVertices

    \      xObject,   yObject,   zObject
 EQUD &00000000 : EQUD &FE400000 : EQUD &00000000        \ Vertex 0
 EQUD &FFC80000 : EQUD &FFD745D2 : EQUD &00380000        \ Vertex 1
 EQUD &FFC80000 : EQUD &FFD745D2 : EQUD &FFC80000        \ Vertex 2
 EQUD &00380000 : EQUD &FFD745D2 : EQUD &00380000        \ Vertex 3
 EQUD &00380000 : EQUD &FFD745D2 : EQUD &FFC80000        \ Vertex 4
 EQUD &FF900000 : EQUD &00000000 : EQUD &00700000        \ Vertex 5
 EQUD &FF900000 : EQUD &00000000 : EQUD &FF900000        \ Vertex 6
 EQUD &00700000 : EQUD &00000000 : EQUD &00700000        \ Vertex 7
 EQUD &00700000 : EQUD &00000000 : EQUD &FF900000        \ Vertex 8
 EQUD &FFE40000 : EQUD &FF071C72 : EQUD &001C0000        \ Vertex 9
 EQUD &FFE40000 : EQUD &FF071C72 : EQUD &FFE40000        \ Vertex 10
 EQUD &001C0000 : EQUD &FF071C72 : EQUD &001C0000        \ Vertex 11
 EQUD &001C0000 : EQUD &FF071C72 : EQUD &FFE40000        \ Vertex 12

.objectRocketFaces

    \      xNormal,   yNormal,   zNormal, vertex1, vertex2, vertex3, colour
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 9 : EQUD 1 : EQUD 5 : EQUD &CC0 \ 0
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 11 : EQUD 3 : EQUD 7 : EQUD &CC0 \ 1
 EQUD &00000000 : EQUD &EFA75F67 : EQUD &76E1A76B : EQUD 0 : EQUD 1 : EQUD 3 : EQUD &C00 \ 2
 EQUD &891E5895 : EQUD &EFA75F67 : EQUD &00000000 : EQUD 0 : EQUD 1 : EQUD 2 : EQUD &800 \ 3
 EQUD &76E1A76B : EQUD &EFA75F67 : EQUD &00000000 : EQUD 3 : EQUD 0 : EQUD 4 : EQUD &800 \ 4
 EQUD &00000000 : EQUD &EFA75F67 : EQUD &891E5895 : EQUD 0 : EQUD 2 : EQUD 4 : EQUD &C00 \ 5
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 10 : EQUD 2 : EQUD 6 : EQUD &CC0 \ 6
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD 12 : EQUD 4 : EQUD 8 : EQUD &CC0 \ 7

\ ******************************************************************************
\
\       Name; sinTable
\       Type; Variable
\   Category; Maths [Geometry]
\    Summary; Sine/cosine lookup table
\
\ ------------------------------------------------------------------------------
\
\ At byte n, the table contains;
\
\   [2^31 - 1] * SIN[2 * PI * [n / 1024]]
\
\ For n = 0 to 1023
\
\ In the original BBC BASIC source, this table would have been populated using
\ something along these lines;
\
\   FOR I% = 0 TO 1023
\    [
\     OPT pass%
\     EQUD [2^31 - 1] * SIN[2 * PI * [I% / 1024]]
\    ]
\   NEXT
\
\ I have used EQUDs here because different computers have different algorithms
\ and accuracies in their maths routines, so the only way to ensure a complete
\ match with the original binaries is to hard-code the values.
\
\ The above loop produces the correct values when run on an Archimedes.
\
\ ******************************************************************************

.sinTable

 EQUD &00000000 : EQUD &00C90F87 : EQUD &01921D1F : EQUD &025B26D7
 EQUD &03242ABE : EQUD &03ED26E6 : EQUD &04B6195D : EQUD &057F0034
 EQUD &0647D97C : EQUD &0710A344 : EQUD &07D95B9E : EQUD &08A2009A
 EQUD &096A9049 : EQUD &0A3308BC : EQUD &0AFB6805 : EQUD &0BC3AC35
 EQUD &0C8BD35D : EQUD &0D53DB92 : EQUD &0E1BC2E3 : EQUD &0EE38765
 EQUD &0FAB272B : EQUD &1072A047 : EQUD &1139F0CE : EQUD &120116D4
 EQUD &12C8106E : EQUD &138EDBB0 : EQUD &145576B1 : EQUD &151BDF85
 EQUD &15E21444 : EQUD &16A81304 : EQUD &176DD9DE : EQUD &183366E8
 EQUD &18F8B83C : EQUD &19BDCBF2 : EQUD &1A82A025 : EQUD &1B4732EE
 EQUD &1C0B826A : EQUD &1CCF8CB2 : EQUD &1D934FE4 : EQUD &1E56CA1D
 EQUD &1F19F97A : EQUD &1FDCDC1A : EQUD &209F701C : EQUD &2161B39F
 EQUD &2223A4C5 : EQUD &22E541AE : EQUD &23A6887E : EQUD &24677757
 EQUD &25280C5D : EQUD &25E845B5 : EQUD &26A82185 : EQUD &27679DF3
 EQUD &2826B928 : EQUD &28E5714A : EQUD &29A3C484 : EQUD &2A61B101
 EQUD &2B1F34EB : EQUD &2BDC4E6E : EQUD &2C98FBBA : EQUD &2D553AFB
 EQUD &2E110A61 : EQUD &2ECC681D : EQUD &2F875262 : EQUD &3041C760
 EQUD &30FBC54C : EQUD &31B54A5D : EQUD &326E54C6 : EQUD &3326E2C2
 EQUD &33DEF286 : EQUD &3496824F : EQUD &354D9056 : EQUD &36041AD8
 EQUD &36BA2013 : EQUD &376F9E45 : EQUD &382493AF : EQUD &38D8FE92
 EQUD &398CDD31 : EQUD &3A402DD1 : EQUD &3AF2EEB6 : EQUD &3BA51E28
 EQUD &3C56BA6F : EQUD &3D07C1D5 : EQUD &3DB832A5 : EQUD &3E680B2C
 EQUD &3F1749B7 : EQUD &3FC5EC97 : EQUD &4073F21C : EQUD &4121589A
 EQUD &41CE1E63 : EQUD &427A41D0 : EQUD &4325C135 : EQUD &43D09AEC
 EQUD &447ACD4F : EQUD &452456BC : EQUD &45CD358E : EQUD &46756827
 EQUD &471CECE6 : EQUD &47C3C22E : EQUD &4869E664 : EQUD &490F57ED
 EQUD &49B41533 : EQUD &4A581C9D : EQUD &4AFB6C97 : EQUD &4B9E038E
 EQUD &4C3FDFF3 : EQUD &4CE10033 : EQUD &4D8162C3 : EQUD &4E210616
 EQUD &4EBFE8A4 : EQUD &4F5E08E2 : EQUD &4FFB654C : EQUD &5097FC5D
 EQUD &5133CC93 : EQUD &51CED46D : EQUD &5269126D : EQUD &53028517
 EQUD &539B2AEF : EQUD &5433027C : EQUD &54CA0A49 : EQUD &556040E2
 EQUD &55F5A4D1 : EQUD &568A34A8 : EQUD &571DEEF8 : EQUD &57B0D255
 EQUD &5842DD53 : EQUD &58D40E8B : EQUD &59646497 : EQUD &59F3DE11
 EQUD &5A827999 : EQUD &5B1035CE : EQUD &5B9D1153 : EQUD &5C290ACB
 EQUD &5CB420DF : EQUD &5D3E5236 : EQUD &5DC79D7B : EQUD &5E50015C
 EQUD &5ED77C89 : EQUD &5F5E0DB2 : EQUD &5FE3B38C : EQUD &60686CCD
 EQUD &60EC382E : EQUD &616F146A : EQUD &61F1003E : EQUD &6271FA68
 EQUD &62F201AB : EQUD &637114CB : EQUD &63EF328E : EQUD &646C59BE
 EQUD &64E88925 : EQUD &6563BF91 : EQUD &65DDFBD2 : EQUD &66573CBA
 EQUD &66CF811E : EQUD &6746C7D6 : EQUD &67BD0FBB : EQUD &683257A9
 EQUD &68A69E80 : EQUD &6919E31F : EQUD &698C246B : EQUD &69FD6149
 EQUD &6A6D98A3 : EQUD &6ADCC963 : EQUD &6B4AF277 : EQUD &6BB812CF
 EQUD &6C24295F : EQUD &6C8F351A : EQUD &6CF934FB : EQUD &6D6227F9
 EQUD &6DCA0D13 : EQUD &6E30E349 : EQUD &6E96A99C : EQUD &6EFB5F11
 EQUD &6F5F02B1 : EQUD &6FC19384 : EQUD &70231098 : EQUD &708378FE
 EQUD &70E2CBC5 : EQUD &71410803 : EQUD &719E2CD1 : EQUD &71FA3947
 EQUD &72552C84 : EQUD &72AF05A6 : EQUD &7307C3CF : EQUD &735F6625
 EQUD &73B5EBD0 : EQUD &740B53F9 : EQUD &745F9DD0 : EQUD &74B2C882
 EQUD &7504D344 : EQUD &7555BD4A : EQUD &75A585CE : EQUD &75F42C09
 EQUD &7641AF3B : EQUD &768E0EA4 : EQUD &76D94988 : EQUD &77235F2C
 EQUD &776C4EDA : EQUD &77B417DE : EQUD &77FAB987 : EQUD &78403327
 EQUD &78848413 : EQUD &78C7ABA0 : EQUD &7909A92B : EQUD &794A7C10
 EQUD &798A23B0 : EQUD &79C89F6C : EQUD &7A05EEAC : EQUD &7A4210D7
 EQUD &7A7D055A : EQUD &7AB6CBA2 : EQUD &7AEF6322 : EQUD &7B26CB4E
 EQUD &7B5D039C : EQUD &7B920B88 : EQUD &7BC5E28E : EQUD &7BF8882F
 EQUD &7C29FBED : EQUD &7C5A3D4E : EQUD &7C894BDC : EQUD &7CB72723
 EQUD &7CE3CEB0 : EQUD &7D0F4217 : EQUD &7D3980EB : EQUD &7D628AC5
 EQUD &7D8A5F3F : EQUD &7DB0FDF6 : EQUD &7DD6668D : EQUD &7DFA98A7
 EQUD &7E1D93E8 : EQUD &7E3F57FD : EQUD &7E5FE492 : EQUD &7E7F3955
 EQUD &7E9D55FB : EQUD &7EBA3A38 : EQUD &7ED5E5C5 : EQUD &7EF0585E
 EQUD &7F0991C2 : EQUD &7F2191B3 : EQUD &7F3857F5 : EQUD &7F4DE44F
 EQUD &7F62368E : EQUD &7F754E7E : EQUD &7F872BF2 : EQUD &7F97CEBC
 EQUD &7FA736B3 : EQUD &7FB563B2 : EQUD &7FC25595 : EQUD &7FCE0C3D
 EQUD &7FD8878D : EQUD &7FE1C76A : EQUD &7FE9CBBF : EQUD &7FF09477
 EQUD &7FF62181 : EQUD &7FFA72D0 : EQUD &7FFD8859 : EQUD &7FFF6216
 EQUD &7FFFFFFE : EQUD &7FFF6216 : EQUD &7FFD8859 : EQUD &7FFA72D0
 EQUD &7FF62181 : EQUD &7FF09477 : EQUD &7FE9CBBF : EQUD &7FE1C76A
 EQUD &7FD8878D : EQUD &7FCE0C3D : EQUD &7FC25595 : EQUD &7FB563B2
 EQUD &7FA736B3 : EQUD &7F97CEBC : EQUD &7F872BF2 : EQUD &7F754E7E
 EQUD &7F62368E : EQUD &7F4DE44F : EQUD &7F3857F5 : EQUD &7F2191B3
 EQUD &7F0991C2 : EQUD &7EF0585E : EQUD &7ED5E5C5 : EQUD &7EBA3A38
 EQUD &7E9D55FB : EQUD &7E7F3955 : EQUD &7E5FE492 : EQUD &7E3F57FD
 EQUD &7E1D93E8 : EQUD &7DFA98A7 : EQUD &7DD6668D : EQUD &7DB0FDF6
 EQUD &7D8A5F3F : EQUD &7D628AC5 : EQUD &7D3980EB : EQUD &7D0F4217
 EQUD &7CE3CEB0 : EQUD &7CB72723 : EQUD &7C894BDC : EQUD &7C5A3D4E
 EQUD &7C29FBED : EQUD &7BF8882F : EQUD &7BC5E28E : EQUD &7B920B88
 EQUD &7B5D039C : EQUD &7B26CB4E : EQUD &7AEF6322 : EQUD &7AB6CBA2
 EQUD &7A7D055A : EQUD &7A4210D7 : EQUD &7A05EEAC : EQUD &79C89F6C
 EQUD &798A23B0 : EQUD &794A7C10 : EQUD &7909A92B : EQUD &78C7ABA0
 EQUD &78848413 : EQUD &78403327 : EQUD &77FAB987 : EQUD &77B417DE
 EQUD &776C4EDA : EQUD &77235F2C : EQUD &76D94988 : EQUD &768E0EA4
 EQUD &7641AF3B : EQUD &75F42C09 : EQUD &75A585CE : EQUD &7555BD4A
 EQUD &7504D344 : EQUD &74B2C882 : EQUD &745F9DD0 : EQUD &740B53FA
 EQUD &73B5EBD0 : EQUD &735F6625 : EQUD &7307C3CF : EQUD &72AF05A5
 EQUD &72552C84 : EQUD &71FA3948 : EQUD &719E2CD1 : EQUD &71410803
 EQUD &70E2CBC5 : EQUD &708378FD : EQUD &70231098 : EQUD &6FC19384
 EQUD &6F5F02B0 : EQUD &6EFB5F11 : EQUD &6E96A99C : EQUD &6E30E348
 EQUD &6DCA0D13 : EQUD &6D6227F9 : EQUD &6CF934FB : EQUD &6C8F351A
 EQUD &6C24295F : EQUD &6BB812D0 : EQUD &6B4AF277 : EQUD &6ADCC963
 EQUD &6A6D98A3 : EQUD &69FD6149 : EQUD &698C246B : EQUD &6919E320
 EQUD &68A69E80 : EQUD &683257AA : EQUD &67BD0FBC : EQUD &6746C7D6
 EQUD &66CF811E : EQUD &66573CBA : EQUD &65DDFBD1 : EQUD &6563BF91
 EQUD &64E88925 : EQUD &646C59BF : EQUD &63EF328E : EQUD &637114CB
 EQUD &62F201AC : EQUD &6271FA68 : EQUD &61F1003E : EQUD &616F146B
 EQUD &60EC382E : EQUD &60686CCE : EQUD &5FE3B38D : EQUD &5F5E0DB2
 EQUD &5ED77C89 : EQUD &5E50015D : EQUD &5DC79D7B : EQUD &5D3E5236
 EQUD &5CB420DF : EQUD &5C290ACB : EQUD &5B9D1153 : EQUD &5B1035CF
 EQUD &5A82799A : EQUD &59F3DE11 : EQUD &59646497 : EQUD &58D40E8C
 EQUD &5842DD53 : EQUD &57B0D256 : EQUD &571DEEFA : EQUD &568A34A8
 EQUD &55F5A4D2 : EQUD &556040E2 : EQUD &54CA0A49 : EQUD &5433027D
 EQUD &539B2AEF : EQUD &53028516 : EQUD &5269126E : EQUD &51CED46E
 EQUD &5133CC93 : EQUD &5097FC5D : EQUD &4FFB654D : EQUD &4F5E08E2
 EQUD &4EBFE8A4 : EQUD &4E210617 : EQUD &4D8162C4 : EQUD &4CE10033
 EQUD &4C3FDFF3 : EQUD &4B9E0390 : EQUD &4AFB6C97 : EQUD &4A581C9D
 EQUD &49B41533 : EQUD &490F57ED : EQUD &4869E664 : EQUD &47C3C22F
 EQUD &471CECE6 : EQUD &46756827 : EQUD &45CD358F : EQUD &452456BC
 EQUD &447ACD50 : EQUD &43D09AEC : EQUD &4325C134 : EQUD &427A41D0
 EQUD &41CE1E64 : EQUD &4121589B : EQUD &4073F21C : EQUD &3FC5EC98
 EQUD &3F1749B8 : EQUD &3E680B2C : EQUD &3DB832A6 : EQUD &3D07C1D6
 EQUD &3C56BA6F : EQUD &3BA51E28 : EQUD &3AF2EEB7 : EQUD &3A402DD0
 EQUD &398CDD32 : EQUD &38D8FE93 : EQUD &382493AF : EQUD &376F9E45
 EQUD &36BA2013 : EQUD &36041AD7 : EQUD &354D9056 : EQUD &3496824F
 EQUD &33DEF287 : EQUD &3326E2C1 : EQUD &326E54C7 : EQUD &31B54A5E
 EQUD &30FBC54C : EQUD &3041C760 : EQUD &2F875262 : EQUD &2ECC681D
 EQUD &2E110A61 : EQUD &2D553AFC : EQUD &2C98FBB9 : EQUD &2BDC4E6F
 EQUD &2B1F34EB : EQUD &2A61B100 : EQUD &29A3C484 : EQUD &28E5714B
 EQUD &2826B927 : EQUD &27679DF3 : EQUD &26A82186 : EQUD &25E845B6
 EQUD &25280C5D : EQUD &24677757 : EQUD &23A6887F : EQUD &22E541AE
 EQUD &2223A4C5 : EQUD &2161B3A0 : EQUD &209F701B : EQUD &1FDCDC1A
 EQUD &1F19F97B : EQUD &1E56CA1D : EQUD &1D934FE5 : EQUD &1CCF8CB3
 EQUD &1C0B8269 : EQUD &1B4732EF : EQUD &1A82A026 : EQUD &19BDCBF1
 EQUD &18F8B83C : EQUD &183366E8 : EQUD &176DD9DD : EQUD &16A81304
 EQUD &15E21444 : EQUD &151BDF86 : EQUD &145576B0 : EQUD &138EDBB1
 EQUD &12C8106F : EQUD &120116D4 : EQUD &1139F0CF : EQUD &1072A048
 EQUD &0FAB272A : EQUD &0EE38765 : EQUD &0E1BC2E4 : EQUD &0D53DB91
 EQUD &0C8BD35E : EQUD &0BC3AC35 : EQUD &0AFB6804 : EQUD &0A3308BC
 EQUD &096A9049 : EQUD &08A20099 : EQUD &07D95B9E : EQUD &0710A345
 EQUD &0647D97D : EQUD &057F0034 : EQUD &04B6195D : EQUD &03ED26E7
 EQUD &03242ABE : EQUD &025B26D7 : EQUD &01921D20 : EQUD &00C90F87
 EQUD &00000000 : EQUD &FF36F079 : EQUD &FE6DE2E0 : EQUD &FDA4D929
 EQUD &FCDBD542 : EQUD &FC12D91A : EQUD &FB49E6A3 : EQUD &FA80FFCD
 EQUD &F9B82684 : EQUD &F8EF5CBC : EQUD &F826A463 : EQUD &F75DFF67
 EQUD &F6956FB7 : EQUD &F5CCF745 : EQUD &F50497FC : EQUD &F43C53CB
 EQUD &F3742CA3 : EQUD &F2AC2470 : EQUD &F1E43D1D : EQUD &F11C789B
 EQUD &F054D8D6 : EQUD &EF8D5FB9 : EQUD &EEC60F32 : EQUD &EDFEE92D
 EQUD &ED37EF92 : EQUD &EC712450 : EQUD &EBAA8950 : EQUD &EAE4207B
 EQUD &EA1DEBBC : EQUD &E957ECFC : EQUD &E8922624 : EQUD &E7CC9918
 EQUD &E70747C5 : EQUD &E642340F : EQUD &E57D5FDB : EQUD &E4B8CD12
 EQUD &E3F47D97 : EQUD &E330734D : EQUD &E26CB01C : EQUD &E1A935E4
 EQUD &E0E60685 : EQUD &E02323E6 : EQUD &DF608FE5 : EQUD &DE9E4C61
 EQUD &DDDC5B3C : EQUD &DD1ABE53 : EQUD &DC597782 : EQUD &DB9888A9
 EQUD &DAD7F3A4 : EQUD &DA17BA4A : EQUD &D957DE7B : EQUD &D898620D
 EQUD &D7D946DA : EQUD &D71A8EB6 : EQUD &D65C3B7C : EQUD &D59E4F01
 EQUD &D4E0CB15 : EQUD &D423B192 : EQUD &D3670447 : EQUD &D2AAC505
 EQUD &D1EEF59F : EQUD &D13397E3 : EQUD &D078AD9E : EQUD &CFBE38A1
 EQUD &CF043AB5 : EQUD &CE4AB5A3 : EQUD &CD91AB3A : EQUD &CCD91D3F
 EQUD &CC210D79 : EQUD &CB697DB1 : EQUD &CAB26FAB : EQUD &C9FBE529
 EQUD &C945DFED : EQUD &C89061BC : EQUD &C7DB6C52 : EQUD &C727016E
 EQUD &C67322CF : EQUD &C5BFD230 : EQUD &C50D114A : EQUD &C45AE1D8
 EQUD &C3A94592 : EQUD &C2F83E2B : EQUD &C247CD5B : EQUD &C197F4D5
 EQUD &C0E8B649 : EQUD &C03A1369 : EQUD &BF8C0DE5 : EQUD &BEDEA766
 EQUD &BE31E19D : EQUD &BD85BE31 : EQUD &BCDA3ECD : EQUD &BC2F6514
 EQUD &BB8532B1 : EQUD &BADBA945 : EQUD &BA32CA72 : EQUD &B98A97DA
 EQUD &B8E3131B : EQUD &B83C3DD2 : EQUD &B796199D : EQUD &B6F0A814
 EQUD &B64BEACD : EQUD &B5A7E364 : EQUD &B504936A : EQUD &B461FC71
 EQUD &B3C0200E : EQUD &B31EFFCE : EQUD &B27E9D3D : EQUD &B1DEF9EA
 EQUD &B140175D : EQUD &B0A1F71F : EQUD &B0049AB4 : EQUD &AF6803A3
 EQUD &AECC336E : EQUD &AE312B93 : EQUD &AD96ED93 : EQUD &ACFD7AEA
 EQUD &AC64D511 : EQUD &ABCCFD84 : EQUD &AB35F5B7 : EQUD &AA9FBF1E
 EQUD &AA0A5B2F : EQUD &A975CB58 : EQUD &A8E21107 : EQUD &A84F2DAB
 EQUD &A7BD22AD : EQUD &A72BF174 : EQUD &A69B9B69 : EQUD &A60C21EF
 EQUD &A57D8667 : EQUD &A4EFCA32 : EQUD &A462EEAE : EQUD &A3D6F536
 EQUD &A34BDF21 : EQUD &A2C1ADCB : EQUD &A2386286 : EQUD &A1AFFEA4
 EQUD &A1288378 : EQUD &A0A1F24F : EQUD &A01C4C74 : EQUD &9F979333
 EQUD &9F13C7D2 : EQUD &9E90EB96 : EQUD &9E0EFFC4 : EQUD &9D8E0599
 EQUD &9D0DFE55 : EQUD &9C8EEB37 : EQUD &9C10CD72 : EQUD &9B93A642
 EQUD &9B1776DD : EQUD &9A9C4070 : EQUD &9A22042E : EQUD &99A8C347
 EQUD &99307EE2 : EQUD &98B93829 : EQUD &9842F046 : EQUD &97CDA857
 EQUD &97596180 : EQUD &96E61CE2 : EQUD &9673DB96 : EQUD &96029EB6
 EQUD &9592675E : EQUD &9523369D : EQUD &94B50D88 : EQUD &9447ED31
 EQUD &93DBD6A1 : EQUD &9370CAE4 : EQUD &9306CB06 : EQUD &929DD807
 EQUD &9235F2EC : EQUD &91CF1CB8 : EQUD &91695664 : EQUD &9104A0EE
 EQUD &90A0FD50 : EQUD &903E6C7C : EQUD &8FDCEF67 : EQUD &8F7C8703
 EQUD &8F1D343B : EQUD &8EBEF7FC : EQUD &8E61D32F : EQUD &8E05C6B8
 EQUD &8DAAD37D : EQUD &8D50FA5B : EQUD &8CF83C31 : EQUD &8CA099DC
 EQUD &8C4A1430 : EQUD &8BF4AC06 : EQUD &8BA06231 : EQUD &8B4D377E
 EQUD &8AFB2CBC : EQUD &8AAA42B6 : EQUD &8A5A7A32 : EQUD &8A0BD3F6
 EQUD &89BE50C5 : EQUD &8971F15C : EQUD &8926B678 : EQUD &88DCA0D5
 EQUD &8893B126 : EQUD &884BE821 : EQUD &88054679 : EQUD &87BFCCD9
 EQUD &877B7BED : EQUD &87385460 : EQUD &86F656D5 : EQUD &86B583EF
 EQUD &8675DC51 : EQUD &86376094 : EQUD &85FA1154 : EQUD &85BDEF29
 EQUD &8582FAA6 : EQUD &8549345D : EQUD &85109CDE : EQUD &84D934B2
 EQUD &84A2FC63 : EQUD &846DF478 : EQUD &843A1D72 : EQUD &840777D1
 EQUD &83D60413 : EQUD &83A5C2B1 : EQUD &8376B424 : EQUD &8348D8DD
 EQUD &831C3150 : EQUD &82F0BDEA : EQUD &82C67F15 : EQUD &829D753B
 EQUD &8275A0C2 : EQUD &824F020A : EQUD &82299973 : EQUD &8205675A
 EQUD &81E26C18 : EQUD &81C0A803 : EQUD &81A01B6F : EQUD &8180C6AB
 EQUD &8162AA05 : EQUD &8145C5C9 : EQUD &812A1A3B : EQUD &810FA7A2
 EQUD &80F66E3E : EQUD &80DE6E4D : EQUD &80C7A80B : EQUD &80B21BB1
 EQUD &809DC972 : EQUD &808AB181 : EQUD &8078D40E : EQUD &80683144
 EQUD &8058C94D : EQUD &804A9C4E : EQUD &803DAA6B : EQUD &8031F3C3
 EQUD &80277873 : EQUD &801E3896 : EQUD &80163441 : EQUD &800F6B89
 EQUD &8009DE7F : EQUD &80058D30 : EQUD &800277A7 : EQUD &80009DEB
 EQUD &80000001 : EQUD &80009DEB : EQUD &800277A7 : EQUD &80058D30
 EQUD &8009DE7F : EQUD &800F6B89 : EQUD &80163441 : EQUD &801E3895
 EQUD &80277873 : EQUD &8031F3C3 : EQUD &803DAA6B : EQUD &804A9C4E
 EQUD &8058C94D : EQUD &80683145 : EQUD &8078D40E : EQUD &808AB182
 EQUD &809DC972 : EQUD &80B21BB1 : EQUD &80C7A80B : EQUD &80DE6E4D
 EQUD &80F66E3E : EQUD &810FA7A1 : EQUD &812A1A3B : EQUD &8145C5C8
 EQUD &8162AA05 : EQUD &8180C6AA : EQUD &81A01B6E : EQUD &81C0A803
 EQUD &81E26C18 : EQUD &8205675A : EQUD &82299972 : EQUD &824F0209
 EQUD &8275A0C2 : EQUD &829D753B : EQUD &82C67F15 : EQUD &82F0BDEA
 EQUD &831C314F : EQUD &8348D8DD : EQUD &8376B424 : EQUD &83A5C2B1
 EQUD &83D60413 : EQUD &840777D0 : EQUD &843A1D71 : EQUD &846DF478
 EQUD &84A2FC63 : EQUD &84D934B2 : EQUD &85109CDE : EQUD &8549345D
 EQUD &8582FAA6 : EQUD &85BDEF29 : EQUD &85FA1153 : EQUD &86376093
 EQUD &8675DC50 : EQUD &86B583EF : EQUD &86F656D4 : EQUD &87385460
 EQUD &877B7BED : EQUD &87BFCCD8 : EQUD &88054679 : EQUD &884BE821
 EQUD &8893B126 : EQUD &88DCA0D4 : EQUD &8926B678 : EQUD &8971F15B
 EQUD &89BE50C5 : EQUD &8A0BD3F6 : EQUD &8A5A7A32 : EQUD &8AAA42B6
 EQUD &8AFB2CBB : EQUD &8B4D377D : EQUD &8BA06231 : EQUD &8BF4AC06
 EQUD &8C4A1430 : EQUD &8CA099DB : EQUD &8CF83C30 : EQUD &8D50FA5A
 EQUD &8DAAD37D : EQUD &8E05C6B8 : EQUD &8E61D32F : EQUD &8EBEF7FB
 EQUD &8F1D343A : EQUD &8F7C8702 : EQUD &8FDCEF66 : EQUD &903E6C7B
 EQUD &90A0FD4F : EQUD &9104A0EE : EQUD &91695664 : EQUD &91CF1CB7
 EQUD &9235F2EC : EQUD &929DD806 : EQUD &9306CB05 : EQUD &9370CAE4
 EQUD &93DBD6A0 : EQUD &9447ED31 : EQUD &94B50D88 : EQUD &9523369D
 EQUD &9592675E : EQUD &96029EB6 : EQUD &9673DB95 : EQUD &96E61CE2
 EQUD &9759617F : EQUD &97CDA856 : EQUD &9842F045 : EQUD &98B93829
 EQUD &99307EE2 : EQUD &99A8C347 : EQUD &9A22042D : EQUD &9A9C406F
 EQUD &9B1776DC : EQUD &9B93A641 : EQUD &9C10CD72 : EQUD &9C8EEB36
 EQUD &9D0DFE54 : EQUD &9D8E0598 : EQUD &9E0EFFC3 : EQUD &9E90EB95
 EQUD &9F13C7D2 : EQUD &9F979334 : EQUD &A01C4C73 : EQUD &A0A1F24E
 EQUD &A1288376 : EQUD &A1AFFEA3 : EQUD &A2386285 : EQUD &A2C1ADC9
 EQUD &A34BDF21 : EQUD &A3D6F535 : EQUD &A462EEAC : EQUD &A4EFCA31
 EQUD &A57D8667 : EQUD &A60C21ED : EQUD &A69B9B69 : EQUD &A72BF175
 EQUD &A7BD22AB : EQUD &A84F2DAA : EQUD &A8E21108 : EQUD &A975CB56
 EQUD &AA0A5B2E : EQUD &AA9FBF1F : EQUD &AB35F5B5 : EQUD &ABCCFD83
 EQUD &AC64D512 : EQUD &ACFD7AE8 : EQUD &AD96ED92 : EQUD &AE312B93
 EQUD &AECC336C : EQUD &AF6803A3 : EQUD &B0049AB5 : EQUD &B0A1F71D
 EQUD &B140175C : EQUD &B1DEF9EA : EQUD &B27E9D3C : EQUD &B31EFFCD
 EQUD &B3C0200F : EQUD &B461FC70 : EQUD &B5049369 : EQUD &B5A7E365
 EQUD &B64BEACD : EQUD &B6F0A813 : EQUD &B796199A : EQUD &B83C3DD1
 EQUD &B8E3131A : EQUD &B98A97D7 : EQUD &BA32CA71 : EQUD &BADBA944
 EQUD &BB8532AF : EQUD &BC2F6514 : EQUD &BCDA3ECC : EQUD &BD85BE2F
 EQUD &BE31E19C : EQUD &BEDEA767 : EQUD &BF8C0DE2 : EQUD &C03A1368
 EQUD &C0E8B649 : EQUD &C197F4D3 : EQUD &C247CD5A : EQUD &C2F83E2C
 EQUD &C3A9458F : EQUD &C45AE1D8 : EQUD &C50D114B : EQUD &C5BFD22E
 EQUD &C67322CE : EQUD &C727016F : EQUD &C7DB6C4F : EQUD &C89061BB
 EQUD &C945DFEE : EQUD &C9FBE527 : EQUD &CAB26FAA : EQUD &CB697DB2
 EQUD &CC210D78 : EQUD &CCD91D3E : EQUD &CD91AB3B : EQUD &CE4AB5A2
 EQUD &CF043AB4 : EQUD &CFBE38A2 : EQUD &D078AD9D : EQUD &D13397E3
 EQUD &D1EEF5A0 : EQUD &D2AAC504 : EQUD &D3670446 : EQUD &D423B18F
 EQUD &D4E0CB14 : EQUD &D59E4F00 : EQUD &D65C3B7A : EQUD &D71A8EB5
 EQUD &D7D946D9 : EQUD &D898620B : EQUD &D957DE7A : EQUD &DA17BA4B
 EQUD &DAD7F3A1 : EQUD &DB9888A8 : EQUD &DC597783 : EQUD &DD1ABE50
 EQUD &DDDC5B3B : EQUD &DE9E4C62 : EQUD &DF608FE2 : EQUD &E02323E5
 EQUD &E0E60686 : EQUD &E1A935E1 : EQUD &E26CB01B : EQUD &E330734F
 EQUD &E3F47D95 : EQUD &E4B8CD11 : EQUD &E57D5FDC : EQUD &E642340C
 EQUD &E70747C4 : EQUD &E7CC9919 : EQUD &E8922621 : EQUD &E957ECFC
 EQUD &EA1DEBBD : EQUD &EAE4207A : EQUD &EBAA894F : EQUD &EC712451
 EQUD &ED37EF91 : EQUD &EDFEE92C : EQUD &EEC60F33 : EQUD &EF8D5FB8
 EQUD &F054D8D5 : EQUD &F11C7898 : EQUD &F1E43D1C : EQUD &F2AC246F
 EQUD &F3742CA0 : EQUD &F43C53CA : EQUD &F50497FC : EQUD &F5CCF742
 EQUD &F6956FB6 : EQUD &F75DFF67 : EQUD &F826A460 : EQUD &F8EF5CBB
 EQUD &F9B82685 : EQUD &FA80FFCA : EQUD &FB49E6A2 : EQUD &FC12D91B
 EQUD &FCDBD540 : EQUD &FDA4D928 : EQUD &FE6DE2E1 : EQUD &FF36F077

\ ******************************************************************************
\
\       Name; arctanTable
\       Type; Variable
\   Category; Maths [Geometry]
\    Summary; Arctan lookup table
\  Deep dive; Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ At byte n, the table contains;
\
\   [[2^31 - 1] / PI] * arctan[n / 128]
\
\ For n = 0 to 127
\
\ In the original BBC BASIC source, this table would have been populated using
\ something along these lines;
\
\   FOR I% = 0 TO 127
\    [
\     OPT pass%
\     EQUD [[2^31 - 1] / PI] * ATN[I% / 128]
\    ]
\   NEXT
\
\ I have used EQUDs here because different computers have different algorithms
\ and accuracies in their maths routines, so the only way to ensure a complete
\ match with the original binaries is to hard-code the values.
\
\ The above loop produces the correct values when run on an Archimedes.
\
\ ******************************************************************************

.arctanTable

 EQUD &00000000 : EQUD &00517C55 : EQUD &00A2F61E : EQUD &00F46AD0
 EQUD &0145D7E1 : EQUD &01973AC7 : EQUD &01E890FC : EQUD &0239D7FB
 EQUD &028B0D43 : EQUD &02DC2E53 : EQUD &032D38B3 : EQUD &037E29EB
 EQUD &03CEFF89 : EQUD &041FB721 : EQUD &04704E4A : EQUD &04C0C2A4
 EQUD &051111D4 : EQUD &05613983 : EQUD &05B13767 : EQUD &06010937
 EQUD &0650ACB6 : EQUD &06A01FAE : EQUD &06EF5FF1 : EQUD &073E6B5A
 EQUD &078D3FCE : EQUD &07DBDB3A : EQUD &082A3B94 : EQUD &08785EDF
 EQUD &08C64325 : EQUD &0913E67C : EQUD &09614703 : EQUD &09AE62E6
 EQUD &09FB385B : EQUD &0A47C5A2 : EQUD &0A940907 : EQUD &0AE000E1
 EQUD &0B2BAB95 : EQUD &0B77078F : EQUD &0BC2134B : EQUD &0C0CCD4E
 EQUD &0C57342A : EQUD &0CA1467C : EQUD &0CEB02EF : EQUD &0D346836
 EQUD &0D7D7514 : EQUD &0DC62856 : EQUD &0E0E80D4 : EQUD &0E567D73
 EQUD &0E9E1D23 : EQUD &0EE55EE2 : EQUD &0F2C41B6 : EQUD &0F72C4B3
 EQUD &0FB8E6F9 : EQUD &0FFEA7B0 : EQUD &1044060F : EQUD &10890156
 EQUD &10CD98D1 : EQUD &1111CBD6 : EQUD &115599C6 : EQUD &1199020E
 EQUD &11DC0423 : EQUD &121E9F86 : EQUD &1260D3C1 : EQUD &12A2A069
 EQUD &12E4051D : EQUD &13250183 : EQUD &1365954E : EQUD &13A5C038
 EQUD &13E58203 : EQUD &1424DA7D : EQUD &1463C97A : EQUD &14A24ED7
 EQUD &14E06A7A : EQUD &151E1C50 : EQUD &155B6450 : EQUD &15984275
 EQUD &15D4B6C4 : EQUD &1610C149 : EQUD &164C6216 : EQUD &16879945
 EQUD &16C266F6 : EQUD &16FCCB50 : EQUD &1736C67E : EQUD &177058B5
 EQUD &17A9822C : EQUD &17E24322 : EQUD &181A9BDA : EQUD &18528C9E
 EQUD &188A15BB : EQUD &18C13785 : EQUD &18F7F252 : EQUD &192E467F
 EQUD &1964346D : EQUD &1999BC80 : EQUD &19CEDF21 : EQUD &1A039CBD
 EQUD &1A37F5C4 : EQUD &1A6BEAA9 : EQUD &1A9F7BE4 : EQUD &1AD2A9EF
 EQUD &1B057548 : EQUD &1B37DE6E : EQUD &1B69E5E5 : EQUD &1B9B8C33
 EQUD &1BCCD1DF : EQUD &1BFDB775 : EQUD &1C2E3D81 : EQUD &1C5E6491
 EQUD &1C8E2D38 : EQUD &1CBD9807 : EQUD &1CECA593 : EQUD &1D1B5671
 EQUD &1D49AB3A : EQUD &1D77A486 : EQUD &1DA542F0 : EQUD &1DD28713
 EQUD &1DFF718C : EQUD &1E2C02F7 : EQUD &1E583BF4 : EQUD &1E841D21
 EQUD &1EAFA71E : EQUD &1EDADA8D : EQUD &1F05B80D : EQUD &1F304042
 EQUD &1F5A73CC : EQUD &1F84534E : EQUD &1FADDF6B : EQUD &1FD718C5

\ ******************************************************************************
\
\       Name; squareRootTable
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; Square root lookup table
\  Deep dive; Flying by mouse
\
\ ------------------------------------------------------------------------------
\
\ At byte n, the table contains;
\
\   [2^31 - 1] * SQRT[n / 1024]
\
\ For n = 0 to 1023
\
\ In the original BBC BASIC source, this table would have been populated using
\ something along these lines;
\
\   FOR I% = 0 TO 1023
\    [
\     OPT pass%
\     EQUD [2^31 - 1] * SQR[I% / 1024]
\    ]
\   NEXT
\
\ I have used EQUDs here because different computers have different algorithms
\ and accuracies in their maths routines, so the only way to ensure a complete
\ match with the original binaries is to hard-code the values.
\
\ The above loop produces the correct values when run on an Archimedes.
\
\ ******************************************************************************

.squareRootTable

 EQUD &00000000 : EQUD &03FFFFFF : EQUD &05A82799 : EQUD &06ED9EBA
 EQUD &07FFFFFF : EQUD &08F1BBCD : EQUD &09CC4709 : EQUD &0A953FD4
 EQUD &0B504F33 : EQUD &0BFFFFFF : EQUD &0CA62C1D : EQUD &0D443949
 EQUD &0DDB3D74 : EQUD &0E6C15A2 : EQUD &0EF77508 : EQUD &0F7DEF58
 EQUD &0FFFFFFF : EQUD &107E0F66 : EQUD &10F876CC : EQUD &116F8334
 EQUD &11E3779B : EQUD &12548EB9 : EQUD &12C2FC59 : EQUD &132EEE75
 EQUD &13988E13 : EQUD &13FFFFFF : EQUD &1465655F : EQUD &14C8DC2E
 EQUD &152A7FA9 : EQUD &158A68A4 : EQUD &15E8ADD2 : EQUD &16456405
 EQUD &16A09E66 : EQUD &16FA6EA1 : EQUD &1752E50D : EQUD &17AA10D1
 EQUD &17FFFFFF : EQUD &1854BFB3 : EQUD &18A85C24 : EQUD &18FAE0C1
 EQUD &194C583A : EQUD &199CCC99 : EQUD &19EC4749 : EQUD &1A3AD129
 EQUD &1A887293 : EQUD &1AD53369 : EQUD &1B211B1C : EQUD &1B6C30B8
 EQUD &1BB67AE8 : EQUD &1BFFFFFF : EQUD &1C48C5FF : EQUD &1C90D29C
 EQUD &1CD82B44 : EQUD &1D1ED520 : EQUD &1D64D51D : EQUD &1DAA2FEF
 EQUD &1DEEEA11 : EQUD &1E3307CC : EQUD &1E768D39 : EQUD &1EB97E45
 EQUD &1EFBDEB1 : EQUD &1F3DB217 : EQUD &1F7EFBEB : EQUD &1FBFBF7E
 EQUD &1FFFFFFF : EQUD &203FC07E : EQUD &207F03EC : EQUD &20BDCD1D
 EQUD &20FC1ECD : EQUD &2139FB9A : EQUD &2177660F : EQUD &21B4609B
 EQUD &21F0ED99 : EQUD &222D0F51 : EQUD &2268C7F4 : EQUD &22A419A2
 EQUD &22DF0668 : EQUD &23199043 : EQUD &2353B91E : EQUD &238D82D6
 EQUD &23C6EF37 : EQUD &23FFFFFF : EQUD &2438B6E1 : EQUD &2471157F
 EQUD &24A91D72 : EQUD &24E0D043 : EQUD &25182F72 : EQUD &254F3C74
 EQUD &2585F8B2 : EQUD &25BC658C : EQUD &25F28458 : EQUD &26285661
 EQUD &265DDCEA : EQUD &2693192F : EQUD &26C80C60 : EQUD &26FCB7A7
 EQUD &27311C27 : EQUD &27653AFA : EQUD &27991533 : EQUD &27CCABDD
 EQUD &27FFFFFF : EQUD &28331298 : EQUD &2865E49F : EQUD &28987707
 EQUD &28CACABE : EQUD &28FCE0A9 : EQUD &292EB9AA : EQUD &2960569E
 EQUD &2991B85C : EQUD &29C2DFB5 : EQUD &29F3CD77 : EQUD &2A24826A
 EQUD &2A54FF53 : EQUD &2A8544F1 : EQUD &2AB55400 : EQUD &2AE52D36
 EQUD &2B14D149 : EQUD &2B4440E6 : EQUD &2B737CBA : EQUD &2BA2856D
 EQUD &2BD15BA4 : EQUD &2BFFFFFF : EQUD &2C2E731E : EQUD &2C5CB599
 EQUD &2C8AC80A : EQUD &2CB8AB04 : EQUD &2CE65F19 : EQUD &2D13E4D8
 EQUD &2D413CCC : EQUD &2D6E677F : EQUD &2D9B6577 : EQUD &2DC83737
 EQUD &2DF4DD42 : EQUD &2E215816 : EQUD &2E4DA830 : EQUD &2E79CE09
 EQUD &2EA5CA1B : EQUD &2ED19CDA : EQUD &2EFD46BA : EQUD &2F28C82D
 EQUD &2F5421A3 : EQUD &2F7F5388 : EQUD &2FAA5E48 : EQUD &2FD5424D
 EQUD &2FFFFFFF : EQUD &302A97C4 : EQUD &30550A00 : EQUD &307F5716
 EQUD &30A97F66 : EQUD &30D38350 : EQUD &30FD6331 : EQUD &31271F66
 EQUD &3150B849 : EQUD &317A2E33 : EQUD &31A3817C : EQUD &31CCB27A
 EQUD &31F5C182 : EQUD &321EAEE7 : EQUD &32477AFB : EQUD &32702610
 EQUD &3298B075 : EQUD &32C11A78 : EQUD &32E96466 : EQUD &33118E8B
 EQUD &33399932 : EQUD &336184A5 : EQUD &3389512C : EQUD &33B0FF0F
 EQUD &33D88E93 : EQUD &33FFFFFF : EQUD &34275396 : EQUD &344E899C
 EQUD &3475A253 : EQUD &349C9DFD : EQUD &34C37CD9 : EQUD &34EA3F28
 EQUD &3510E527 : EQUD &35376F15 : EQUD &355DDD2E : EQUD &35842FAF
 EQUD &35AA66D2 : EQUD &35D082D2 : EQUD &35F683E8 : EQUD &361C6A4C
 EQUD &36423638 : EQUD &3667E7E2 : EQUD &368D7F80 : EQUD &36B2FD48
 EQUD &36D86170 : EQUD &36FDAC2A : EQUD &3722DDAC : EQUD &3747F628
 EQUD &376CF5D0 : EQUD &3791DCD5 : EQUD &37B6AB6A : EQUD &37DB61BD
 EQUD &37FFFFFF : EQUD &3824865F : EQUD &3848F50B : EQUD &386D4C31
 EQUD &38918BFF : EQUD &38B5B4A2 : EQUD &38D9C644 : EQUD &38FDC113
 EQUD &3921A539 : EQUD &394572E2 : EQUD &39692A36 : EQUD &398CCB60
 EQUD &39B05688 : EQUD &39D3CBD7 : EQUD &39F72B76 : EQUD &3A1A758C
 EQUD &3A3DAA40 : EQUD &3A60C9B9 : EQUD &3A83D41C : EQUD &3AA6C991
 EQUD &3AC9AA3B : EQUD &3AEC7641 : EQUD &3B0F2DC6 : EQUD &3B31D0EF
 EQUD &3B545FDE : EQUD &3B76DAB9 : EQUD &3B9941A1 : EQUD &3BBB94B8
 EQUD &3BDDD422 : EQUD &3BFFFFFF : EQUD &3C221871 : EQUD &3C441D99
 EQUD &3C660F98 : EQUD &3C87EE8D : EQUD &3CA9BA99 : EQUD &3CCB73DB
 EQUD &3CED1A72 : EQUD &3D0EAE7E : EQUD &3D30301C : EQUD &3D519F6C
 EQUD &3D72FC8A : EQUD &3D944794 : EQUD &3DB580A9 : EQUD &3DD6A7E4
 EQUD &3DF7BD62 : EQUD &3E18C140 : EQUD &3E39B399 : EQUD &3E5A948A
 EQUD &3E7B642E : EQUD &3E9C22A0 : EQUD &3EBCCFFB : EQUD &3EDD6C59
 EQUD &3EFDF7D6 : EQUD &3F1E728B : EQUD &3F3EDC92 : EQUD &3F5F3605
 EQUD &3F7F7EFD : EQUD &3F9FB792 : EQUD &3FBFDFDF : EQUD &3FDFF7FB
 EQUD &3FFFFFFF : EQUD &401FF803 : EQUD &403FE01F : EQUD &405FB86A
 EQUD &407F80FD : EQUD &409F39ED : EQUD &40BEE353 : EQUD &40DE7D44
 EQUD &40FE07D8 : EQUD &411D8325 : EQUD &413CEF40 : EQUD &415C4C40
 EQUD &417B9A3B : EQUD &419AD946 : EQUD &41BA0976 : EQUD &41D92AE0
 EQUD &41F83D9A : EQUD &421741B8 : EQUD &4236374E : EQUD &42551E71
 EQUD &4273F735 : EQUD &4292C1AF : EQUD &42B17DF1 : EQUD &42D02C0F
 EQUD &42EECC1E : EQUD &430D5E30 : EQUD &432BE257 : EQUD &434A58A9
 EQUD &4368C136 : EQUD &43871C11 : EQUD &43A5694E : EQUD &43C3A8FE
 EQUD &43E1DB33 : EQUD &43FFFFFF : EQUD &441E1775 : EQUD &443C21A5
 EQUD &445A1EA2 : EQUD &44780E7C : EQUD &4495F145 : EQUD &44B3C70E
 EQUD &44D18FE8 : EQUD &44EF4BE3 : EQUD &450CFB11 : EQUD &452A9D81
 EQUD &45483344 : EQUD &4565BC6A : EQUD &45833904 : EQUD &45A0A921
 EQUD &45BE0CD1 : EQUD &45DB6423 : EQUD &45F8AF28 : EQUD &4615EDEF
 EQUD &46332087 : EQUD &465046FE : EQUD &466D6165 : EQUD &468A6FCB
 EQUD &46A7723D : EQUD &46C468CB : EQUD &46E15383 : EQUD &46FE3274
 EQUD &471B05AC : EQUD &4737CD39 : EQUD &4754892A : EQUD &4771398C
 EQUD &478DDE6E : EQUD &47AA77DC : EQUD &47C705E6 : EQUD &47E38898
 EQUD &47FFFFFF : EQUD &481C6C2A : EQUD &4838CD26 : EQUD &485522FF
 EQUD &48716DC3 : EQUD &488DAD7E : EQUD &48A9E23E : EQUD &48C60C10
 EQUD &48E22AFF : EQUD &48FE3F19 : EQUD &491A486B : EQUD &493646FF
 EQUD &49523AE4 : EQUD &496E2424 : EQUD &498A02CC : EQUD &49A5D6E9
 EQUD &49C1A086 : EQUD &49DD5FAE : EQUD &49F9146E : EQUD &4A14BED2
 EQUD &4A305EE4 : EQUD &4A4BF4B1 : EQUD &4A678044 : EQUD &4A8301A8
 EQUD &4A9E78E8 : EQUD &4AB9E610 : EQUD &4AD5492B : EQUD &4AF0A243
 EQUD &4B0BF165 : EQUD &4B273699 : EQUD &4B4271ED : EQUD &4B5DA369
 EQUD &4B78CB19 : EQUD &4B93E907 : EQUD &4BAEFD3E : EQUD &4BCA07C8
 EQUD &4BE508B0 : EQUD &4BFFFFFF : EQUD &4C1AEDC1 : EQUD &4C35D1FE
 EQUD &4C50ACC2 : EQUD &4C6B7E16 : EQUD &4C864604 : EQUD &4CA10496
 EQUD &4CBBB9D5 : EQUD &4CD665CC : EQUD &4CF10884 : EQUD &4D0BA207
 EQUD &4D26325E : EQUD &4D40B993 : EQUD &4D5B37AE : EQUD &4D75ACBB
 EQUD &4D9018C0 : EQUD &4DAA7BC9 : EQUD &4DC4D5DE : EQUD &4DDF2708
 EQUD &4DF96F4F : EQUD &4E13AEBF : EQUD &4E2DE55E : EQUD &4E481336
 EQUD &4E62384F : EQUD &4E7C54B3 : EQUD &4E96686B : EQUD &4EB0737E
 EQUD &4ECA75F5 : EQUD &4EE46FD9 : EQUD &4EFE6132 : EQUD &4F184A09
 EQUD &4F322A66 : EQUD &4F4C0251 : EQUD &4F65D1D3 : EQUD &4F7F98F4
 EQUD &4F9957BB : EQUD &4FB30E32 : EQUD &4FCCBC5F : EQUD &4FE6624C
 EQUD &4FFFFFFF : EQUD &50199582 : EQUD &503322DB : EQUD &504CA812
 EQUD &50662530 : EQUD &507F9A3B : EQUD &5099073D : EQUD &50B26C3B
 EQUD &50CBC93E : EQUD &50E51E4D : EQUD &50FE6B70 : EQUD &5117B0AE
 EQUD &5130EE0F : EQUD &514A2399 : EQUD &51635155 : EQUD &517C7748
 EQUD &5195957C : EQUD &51AEABF5 : EQUD &51C7BABD : EQUD &51E0C1DA
 EQUD &51F9C152 : EQUD &5212B92D : EQUD &522BA972 : EQUD &52449228
 EQUD &525D7355 : EQUD &52764D01 : EQUD &528F1F31 : EQUD &52A7E9EE
 EQUD &52C0AD3D : EQUD &52D96926 : EQUD &52F21DAE : EQUD &530ACADD
 EQUD &532370B8 : EQUD &533C0F47 : EQUD &5354A691 : EQUD &536D369A
 EQUD &5385BF6A : EQUD &539E4108 : EQUD &53B6BB79 : EQUD &53CF2EC4
 EQUD &53E79AEE : EQUD &53FFFFFF : EQUD &54185DFD : EQUD &5430B4EC
 EQUD &544904D5 : EQUD &54614DBC : EQUD &54798FA8 : EQUD &5491CA9F
 EQUD &54A9FEA7 : EQUD &54C22BC5 : EQUD &54DA5200 : EQUD &54F2715D
 EQUD &550A89E3 : EQUD &55229B96 : EQUD &553AA67E : EQUD &5552AA9F
 EQUD &556AA800 : EQUD &55829EA6 : EQUD &559A8E96 : EQUD &55B277D7
 EQUD &55CA5A6D : EQUD &55E23660 : EQUD &55FA0BB3 : EQUD &5611DA6C
 EQUD &5629A292 : EQUD &56416429 : EQUD &56591F37 : EQUD &5670D3C1
 EQUD &568881CC : EQUD &56A0295F : EQUD &56B7CA7E : EQUD &56CF652E
 EQUD &56E6F975 : EQUD &56FE8757 : EQUD &57160EDB : EQUD &572D9005
 EQUD &57450ADB : EQUD &575C7F61 : EQUD &5773ED9C : EQUD &578B5592
 EQUD &57A2B748 : EQUD &57BA12C3 : EQUD &57D16807 : EQUD &57E8B719
 EQUD &57FFFFFF : EQUD &581742BE : EQUD &582E7F59 : EQUD &5845B5D7
 EQUD &585CE63C : EQUD &5874108C : EQUD &588B34CD : EQUD &58A25303
 EQUD &58B96B33 : EQUD &58D07D62 : EQUD &58E78994 : EQUD &58FE8FCE
 EQUD &59159015 : EQUD &592C8A6D : EQUD &59437EDA : EQUD &595A6D62
 EQUD &59715609 : EQUD &598838D3 : EQUD &599F15C6 : EQUD &59B5ECE4
 EQUD &59CCBE33 : EQUD &59E389B8 : EQUD &59FA4F76 : EQUD &5A110F72
 EQUD &5A27C9B1 : EQUD &5A3E7E36 : EQUD &5A552D06 : EQUD &5A6BD626
 EQUD &5A827999 : EQUD &5A991764 : EQUD &5AAFAF8B : EQUD &5AC64213
 EQUD &5ADCCEFF : EQUD &5AF35653 : EQUD &5B09D814 : EQUD &5B205447
 EQUD &5B36CAEE : EQUD &5B4D3C0E : EQUD &5B63A7AC : EQUD &5B7A0DCB
 EQUD &5B906E6F : EQUD &5BA6C99D : EQUD &5BBD1F57 : EQUD &5BD36FA4
 EQUD &5BE9BA85 : EQUD &5BFFFFFF : EQUD &5C164017 : EQUD &5C2C7ACF
 EQUD &5C42B02D : EQUD &5C58E033 : EQUD &5C6F0AE6 : EQUD &5C853049
 EQUD &5C9B5060 : EQUD &5CB16B2F : EQUD &5CC780BB : EQUD &5CDD9105
 EQUD &5CF39C13 : EQUD &5D09A1E8 : EQUD &5D1FA288 : EQUD &5D359DF6
 EQUD &5D4B9436 : EQUD &5D61854C : EQUD &5D77713B : EQUD &5D8D5807
 EQUD &5DA339B4 : EQUD &5DB91645 : EQUD &5DCEEDBE : EQUD &5DE4C022
 EQUD &5DFA8D75 : EQUD &5E1055BA : EQUD &5E2618F5 : EQUD &5E3BD72A
 EQUD &5E51905B : EQUD &5E67448D : EQUD &5E7CF3C2 : EQUD &5E929DFE
 EQUD &5EA84346 : EQUD &5EBDE39B : EQUD &5ED37F01 : EQUD &5EE9157C
 EQUD &5EFEA710 : EQUD &5F1433BE : EQUD &5F29BB8C : EQUD &5F3F3E7B
 EQUD &5F54BC90 : EQUD &5F6A35CE : EQUD &5F7FAA37 : EQUD &5F9519D0
 EQUD &5FAA849B : EQUD &5FBFEA9C : EQUD &5FD54BD5 : EQUD &5FEAA84B
 EQUD &5FFFFFFF : EQUD &601552F6 : EQUD &602AA132 : EQUD &603FEAB8
 EQUD &60552F89 : EQUD &606A6FA9 : EQUD &607FAB1A : EQUD &6094E1E1
 EQUD &60AA1401 : EQUD &60BF417B : EQUD &60D46A54 : EQUD &60E98E8E
 EQUD &60FEAE2C : EQUD &6113C932 : EQUD &6128DFA2 : EQUD &613DF17F
 EQUD &6152FECC : EQUD &6168078D : EQUD &617D0BC4 : EQUD &61920B74
 EQUD &61A706A0 : EQUD &61BBFD4B : EQUD &61D0EF78 : EQUD &61E5DD2A
 EQUD &61FAC663 : EQUD &620FAB27 : EQUD &62248B78 : EQUD &62396759
 EQUD &624E3ECD : EQUD &626311D7 : EQUD &6277E079 : EQUD &628CAAB7
 EQUD &62A17093 : EQUD &62B6320F : EQUD &62CAEF30 : EQUD &62DFA7F7
 EQUD &62F45C67 : EQUD &63090C83 : EQUD &631DB84D : EQUD &63325FC9
 EQUD &634702F9 : EQUD &635BA1DF : EQUD &63703C7F : EQUD &6384D2DA
 EQUD &639964F5 : EQUD &63ADF2D0 : EQUD &63C27C6F : EQUD &63D701D5
 EQUD &63EB8304 : EQUD &63FFFFFF : EQUD &641478C8 : EQUD &6428ED61
 EQUD &643D5DCE : EQUD &6451CA11 : EQUD &6466322D : EQUD &647A9623
 EQUD &648EF5F7 : EQUD &64A351AB : EQUD &64B7A942 : EQUD &64CBFCBE
 EQUD &64E04C21 : EQUD &64F4976E : EQUD &6508DEA8 : EQUD &651D21D0
 EQUD &653160EA : EQUD &65459BF8 : EQUD &6559D2FC : EQUD &656E05F8
 EQUD &658234F0 : EQUD &65965FE5 : EQUD &65AA86DA : EQUD &65BEA9D0
 EQUD &65D2C8CC : EQUD &65E6E3CE : EQUD &65FAFADA : EQUD &660F0DF1
 EQUD &66231D17 : EQUD &6637284C : EQUD &664B2F94 : EQUD &665F32F1
 EQUD &66733265 : EQUD &66872DF3 : EQUD &669B259C : EQUD &66AF1963
 EQUD &66C3094B : EQUD &66D6F555 : EQUD &66EADD84 : EQUD &66FEC1DA
 EQUD &6712A259 : EQUD &67267F04 : EQUD &673A57DC : EQUD &674E2CE4
 EQUD &6761FE1E : EQUD &6775CB8D : EQUD &67899532 : EQUD &679D5B0F
 EQUD &67B11D27 : EQUD &67C4DB7C : EQUD &67D89611 : EQUD &67EC4CE6
 EQUD &67FFFFFF : EQUD &6813AF5D : EQUD &68275B03 : EQUD &683B02F2
 EQUD &684EA72D : EQUD &686247B6 : EQUD &6875E48F : EQUD &68897DBA
 EQUD &689D1339 : EQUD &68B0A50E : EQUD &68C4333C : EQUD &68D7BDC3
 EQUD &68EB44A7 : EQUD &68FEC7E9 : EQUD &6912478C : EQUD &6925C391
 EQUD &69393BFA : EQUD &694CB0CA : EQUD &69602202 : EQUD &69738FA4
 EQUD &6986F9B3 : EQUD &699A6030 : EQUD &69ADC31D : EQUD &69C1227C
 EQUD &69D47E50 : EQUD &69E7D69A : EQUD &69FB2B5B : EQUD &6A0E7C97
 EQUD &6A21CA4F : EQUD &6A351484 : EQUD &6A485B39 : EQUD &6A5B9E70
 EQUD &6A6EDE2B : EQUD &6A821A6A : EQUD &6A955332 : EQUD &6AA88882
 EQUD &6ABBBA5D : EQUD &6ACEE8C5 : EQUD &6AE213BD : EQUD &6AF53B44
 EQUD &6B085F5E : EQUD &6B1B800D : EQUD &6B2E9D51 : EQUD &6B41B72E
 EQUD &6B54CDA4 : EQUD &6B67E0B6 : EQUD &6B7AF066 : EQUD &6B8DFCB4
 EQUD &6BA105A4 : EQUD &6BB40B36 : EQUD &6BC70D6D : EQUD &6BDA0C4A
 EQUD &6BED07D0 : EQUD &6BFFFFFF : EQUD &6C12F4DA : EQUD &6C25E662
 EQUD &6C38D499 : EQUD &6C4BBF81 : EQUD &6C5EA71C : EQUD &6C718B6B
 EQUD &6C846C71 : EQUD &6C974A2D : EQUD &6CAA24A4 : EQUD &6CBCFBD5
 EQUD &6CCFCFC4 : EQUD &6CE2A071 : EQUD &6CF56DDE : EQUD &6D08380D
 EQUD &6D1AFF00 : EQUD &6D2DC2B8 : EQUD &6D408337 : EQUD &6D53407F
 EQUD &6D65FA91 : EQUD &6D78B16F : EQUD &6D8B651A : EQUD &6D9E1594
 EQUD &6DB0C2E0 : EQUD &6DC36CFD : EQUD &6DD613EF : EQUD &6DE8B7B7
 EQUD &6DFB5855 : EQUD &6E0DF5CD : EQUD &6E20901F : EQUD &6E33274D
 EQUD &6E45BB59 : EQUD &6E584C44 : EQUD &6E6ADA10 : EQUD &6E7D64BE
 EQUD &6E8FEC50 : EQUD &6EA270C8 : EQUD &6EB4F227 : EQUD &6EC7706E
 EQUD &6ED9EBA0 : EQUD &6EEC63BE : EQUD &6EFED8C8 : EQUD &6F114AC2
 EQUD &6F23B9AB : EQUD &6F362587 : EQUD &6F488E56 : EQUD &6F5AF41A
 EQUD &6F6D56D4 : EQUD &6F7FB686 : EQUD &6F921332 : EQUD &6FA46CD8
 EQUD &6FB6C37B : EQUD &6FC9171B : EQUD &6FDB67BB : EQUD &6FEDB55C
 EQUD &6FFFFFFF : EQUD &701247A5 : EQUD &70248C51 : EQUD &7036CE04
 EQUD &70490CBE : EQUD &705B4883 : EQUD &706D8152 : EQUD &707FB72D
 EQUD &7091EA17 : EQUD &70A41A0F : EQUD &70B64719 : EQUD &70C87134
 EQUD &70DA9863 : EQUD &70ECBCA7 : EQUD &70FEDE02 : EQUD &7110FC74
 EQUD &712317FF : EQUD &713530A5 : EQUD &71474667 : EQUD &71595946
 EQUD &716B6944 : EQUD &717D7661 : EQUD &718F80A1 : EQUD &71A18803
 EQUD &71B38C89 : EQUD &71C58E35 : EQUD &71D78D08 : EQUD &71E98902
 EQUD &71FB8227 : EQUD &720D7876 : EQUD &721F6BF2 : EQUD &72315C9B
 EQUD &72434A73 : EQUD &7255357C : EQUD &72671DB6 : EQUD &72790323
 EQUD &728AE5C4 : EQUD &729CC59A : EQUD &72AEA2A8 : EQUD &72C07CED
 EQUD &72D2546C : EQUD &72E42926 : EQUD &72F5FB1B : EQUD &7307CA4E
 EQUD &731996C0 : EQUD &732B6071 : EQUD &733D2763 : EQUD &734EEB98
 EQUD &7360AD10 : EQUD &73726BCE : EQUD &738427D1 : EQUD &7395E11C
 EQUD &73A797AF : EQUD &73B94B8D : EQUD &73CAFCB6 : EQUD &73DCAB2B
 EQUD &73EE56ED : EQUD &73FFFFFF : EQUD &7411A660 : EQUD &74234A13
 EQUD &7434EB19 : EQUD &74468972 : EQUD &74582520 : EQUD &7469BE25
 EQUD &747B5481 : EQUD &748CE835 : EQUD &749E7943 : EQUD &74B007AC
 EQUD &74C19372 : EQUD &74D31C95 : EQUD &74E4A316 : EQUD &74F626F7
 EQUD &7507A839 : EQUD &751926DD : EQUD &752AA2E5 : EQUD &753C1C50
 EQUD &754D9322 : EQUD &755F075A : EQUD &757078FA : EQUD &7581E804
 EQUD &75935477 : EQUD &75A4BE56 : EQUD &75B625A1 : EQUD &75C78A5A
 EQUD &75D8EC82 : EQUD &75EA4C1A : EQUD &75FBA923 : EQUD &760D039E
 EQUD &761E5B8C : EQUD &762FB0EF : EQUD &764103C8 : EQUD &76525417
 EQUD &7663A1DE : EQUD &7674ED1D : EQUD &768635D7 : EQUD &76977C0C
 EQUD &76A8BFBD : EQUD &76BA00EC : EQUD &76CB3F99 : EQUD &76DC7BC5
 EQUD &76EDB572 : EQUD &76FEECA1 : EQUD &77102152 : EQUD &77215388
 EQUD &77328342 : EQUD &7743B082 : EQUD &7754DB49 : EQUD &77660399
 EQUD &77772971 : EQUD &77884CD4 : EQUD &77996DC2 : EQUD &77AA8C3D
 EQUD &77BBA844 : EQUD &77CCC1DB : EQUD &77DDD900 : EQUD &77EEEDB7
 EQUD &77FFFFFF : EQUD &78110FD9 : EQUD &78221D48 : EQUD &7833284A
 EQUD &784430E3 : EQUD &78553712 : EQUD &78663AD9 : EQUD &78773C39
 EQUD &78883B33 : EQUD &789937C8 : EQUD &78AA31F8 : EQUD &78BB29C5
 EQUD &78CC1F30 : EQUD &78DD123A : EQUD &78EE02E4 : EQUD &78FEF12E
 EQUD &790FDD1B : EQUD &7920C6AA : EQUD &7931ADDD : EQUD &794292B5
 EQUD &79537532 : EQUD &79645557 : EQUD &79753323 : EQUD &79860E98
 EQUD &7996E7B6 : EQUD &79A7BE80 : EQUD &79B892F5 : EQUD &79C96516
 EQUD &79DA34E5 : EQUD &79EB0263 : EQUD &79FBCD90 : EQUD &7A0C966D
 EQUD &7A1D5CFC : EQUD &7A2E213E : EQUD &7A3EE332 : EQUD &7A4FA2DB
 EQUD &7A606039 : EQUD &7A711B4D : EQUD &7A81D419 : EQUD &7A928A9C
 EQUD &7AA33ED8 : EQUD &7AB3F0CE : EQUD &7AC4A07F : EQUD &7AD54DEB
 EQUD &7AE5F914 : EQUD &7AF6A1FB : EQUD &7B0748A0 : EQUD &7B17ED05
 EQUD &7B288F29 : EQUD &7B392F0F : EQUD &7B49CCB8 : EQUD &7B5A6823
 EQUD &7B6B0152 : EQUD &7B7B9846 : EQUD &7B8C2D00 : EQUD &7B9CBF80
 EQUD &7BAD4FC8 : EQUD &7BBDDDD8 : EQUD &7BCE69B1 : EQUD &7BDEF355
 EQUD &7BEF7AC4 : EQUD &7BFFFFFF : EQUD &7C108306 : EQUD &7C2103DC
 EQUD &7C318280 : EQUD &7C41FEF3 : EQUD &7C527937 : EQUD &7C62F14C
 EQUD &7C736732 : EQUD &7C83DAEC : EQUD &7C944C7A : EQUD &7CA4BBDC
 EQUD &7CB52914 : EQUD &7CC59423 : EQUD &7CD5FD08 : EQUD &7CE663C6
 EQUD &7CF6C85C : EQUD &7D072ACC : EQUD &7D178B17 : EQUD &7D27E93D
 EQUD &7D384540 : EQUD &7D489F20 : EQUD &7D58F6DD : EQUD &7D694C7A
 EQUD &7D799FF6 : EQUD &7D89F153 : EQUD &7D9A4090 : EQUD &7DAA8DB0
 EQUD &7DBAD8B3 : EQUD &7DCB219A : EQUD &7DDB6865 : EQUD &7DEBAD16
 EQUD &7DFBEFAD : EQUD &7E0C302B : EQUD &7E1C6E91 : EQUD &7E2CAADF
 EQUD &7E3CE517 : EQUD &7E4D1D39 : EQUD &7E5D5346 : EQUD &7E6D873F
 EQUD &7E7DB925 : EQUD &7E8DE8F8 : EQUD &7E9E16BA : EQUD &7EAE426A
 EQUD &7EBE6C0A : EQUD &7ECE939B : EQUD &7EDEB91E : EQUD &7EEEDC92
 EQUD &7EFEFDFA : EQUD &7F0F1D55 : EQUD &7F1F3AA5 : EQUD &7F2F55EA
 EQUD &7F3F6F25 : EQUD &7F4F8657 : EQUD &7F5F9B81 : EQUD &7F6FAEA3
 EQUD &7F7FBFBE : EQUD &7F8FCED4 : EQUD &7F9FDBE4 : EQUD &7FAFE6EF
 EQUD &7FBFEFF7 : EQUD &7FCFF6FB : EQUD &7FDFFBFE : EQUD &7FEFFEFF

\ ******************************************************************************
\
\       Name; divisionTable
\       Type; Variable
\   Category; Maths [Arithmetic]
\    Summary; Division lookup tables
\
\ ------------------------------------------------------------------------------
\
\ There are 64 tables, each one for a different denominator d [d = 0 to 63]
\
\ In table d, byte n in the table contains;
\
\   65536 * n / d
\
\ For n = 0 to 63 [the value is &FFFFFFFF when n = 0]
\
\ The address of the table containing the values of n / d is;
\
\   divisionTable + d * 256
\
\ In the original BBC BASIC source, this table would have been populated using
\ something along these lines;
\
\   FOR I% = 0 TO 63
\    [
\     OPT pass%
\     EQUD &FFFFFFFF
\    ]
\    FOR J% = 1 TO 63
\     [
\      OPT pass%
\      EQUD 65536 * I% / J%
\     ]
\    NEXT
\   NEXT
\
\ I have used EQUDs here because different computers have different algorithms
\ and accuracies in their maths routines, so the only way to ensure a complete
\ match with the original binaries is to hard-code the values.
\
\ The above loop produces the correct values when run on an Archimedes.
\
\ ******************************************************************************

.divisionTable

 EQUD &FFFFFFFF : EQUD &00000000 : EQUD &00000000 : EQUD &00000000    \ n / 0 [n = 0 to 63]
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000
 EQUD &00000000 : EQUD &00000000 : EQUD &00000000 : EQUD &00000000

 EQUD &FFFFFFFF : EQUD &00010000 : EQUD &00008000 : EQUD &00005555    \ n / 1 [n = 0 to 63]
 EQUD &00004000 : EQUD &00003333 : EQUD &00002AAA : EQUD &00002492
 EQUD &00002000 : EQUD &00001C71 : EQUD &00001999 : EQUD &00001745
 EQUD &00001555 : EQUD &000013B1 : EQUD &00001249 : EQUD &00001111
 EQUD &00001000 : EQUD &00000F0F : EQUD &00000E38 : EQUD &00000D79
 EQUD &00000CCC : EQUD &00000C30 : EQUD &00000BA2 : EQUD &00000B21
 EQUD &00000AAA : EQUD &00000A3D : EQUD &000009D8 : EQUD &0000097B
 EQUD &00000924 : EQUD &000008D3 : EQUD &00000888 : EQUD &00000842
 EQUD &00000800 : EQUD &000007C1 : EQUD &00000787 : EQUD &00000750
 EQUD &0000071C : EQUD &000006EB : EQUD &000006BC : EQUD &00000690
 EQUD &00000666 : EQUD &0000063E : EQUD &00000618 : EQUD &000005F4
 EQUD &000005D1 : EQUD &000005B0 : EQUD &00000590 : EQUD &00000572
 EQUD &00000555 : EQUD &00000539 : EQUD &0000051E : EQUD &00000505
 EQUD &000004EC : EQUD &000004D4 : EQUD &000004BD : EQUD &000004A7
 EQUD &00000492 : EQUD &0000047D : EQUD &00000469 : EQUD &00000456
 EQUD &00000444 : EQUD &00000432 : EQUD &00000421 : EQUD &00000410

 EQUD &FFFFFFFF : EQUD &00020000 : EQUD &00010000 : EQUD &0000AAAA    \ n / 2 [n = 0 to 63]
 EQUD &00008000 : EQUD &00006666 : EQUD &00005555 : EQUD &00004924
 EQUD &00004000 : EQUD &000038E3 : EQUD &00003333 : EQUD &00002E8B
 EQUD &00002AAA : EQUD &00002762 : EQUD &00002492 : EQUD &00002222
 EQUD &00002000 : EQUD &00001E1E : EQUD &00001C71 : EQUD &00001AF2
 EQUD &00001999 : EQUD &00001861 : EQUD &00001745 : EQUD &00001642
 EQUD &00001555 : EQUD &0000147A : EQUD &000013B1 : EQUD &000012F6
 EQUD &00001249 : EQUD &000011A7 : EQUD &00001111 : EQUD &00001084
 EQUD &00001000 : EQUD &00000F83 : EQUD &00000F0F : EQUD &00000EA0
 EQUD &00000E38 : EQUD &00000DD6 : EQUD &00000D79 : EQUD &00000D20
 EQUD &00000CCC : EQUD &00000C7C : EQUD &00000C30 : EQUD &00000BE8
 EQUD &00000BA2 : EQUD &00000B60 : EQUD &00000B21 : EQUD &00000AE4
 EQUD &00000AAA : EQUD &00000A72 : EQUD &00000A3D : EQUD &00000A0A
 EQUD &000009D8 : EQUD &000009A9 : EQUD &0000097B : EQUD &0000094F
 EQUD &00000924 : EQUD &000008FB : EQUD &000008D3 : EQUD &000008AD
 EQUD &00000888 : EQUD &00000864 : EQUD &00000842 : EQUD &00000820

 EQUD &FFFFFFFF : EQUD &00030000 : EQUD &00018000 : EQUD &00010000    \ n / 3 [n = 0 to 63]
 EQUD &0000C000 : EQUD &00009999 : EQUD &00008000 : EQUD &00006DB6
 EQUD &00006000 : EQUD &00005555 : EQUD &00004CCC : EQUD &000045D1
 EQUD &00004000 : EQUD &00003B13 : EQUD &000036DB : EQUD &00003333
 EQUD &00003000 : EQUD &00002D2D : EQUD &00002AAA : EQUD &0000286B
 EQUD &00002666 : EQUD &00002492 : EQUD &000022E8 : EQUD &00002164
 EQUD &00002000 : EQUD &00001EB8 : EQUD &00001D89 : EQUD &00001C71
 EQUD &00001B6D : EQUD &00001A7B : EQUD &00001999 : EQUD &000018C6
 EQUD &00001800 : EQUD &00001745 : EQUD &00001696 : EQUD &000015F1
 EQUD &00001555 : EQUD &000014C1 : EQUD &00001435 : EQUD &000013B1
 EQUD &00001333 : EQUD &000012BB : EQUD &00001249 : EQUD &000011DC
 EQUD &00001174 : EQUD &00001111 : EQUD &000010B2 : EQUD &00001057
 EQUD &00001000 : EQUD &00000FAC : EQUD &00000F5C : EQUD &00000F0F
 EQUD &00000EC4 : EQUD &00000E7D : EQUD &00000E38 : EQUD &00000DF6
 EQUD &00000DB6 : EQUD &00000D79 : EQUD &00000D3D : EQUD &00000D04
 EQUD &00000CCC : EQUD &00000C97 : EQUD &00000C63 : EQUD &00000C30

 EQUD &FFFFFFFF : EQUD &00040000 : EQUD &00020000 : EQUD &00015555    \ n / 4 [n = 0 to 63]
 EQUD &00010000 : EQUD &0000CCCC : EQUD &0000AAAA : EQUD &00009249
 EQUD &00008000 : EQUD &000071C7 : EQUD &00006666 : EQUD &00005D17
 EQUD &00005555 : EQUD &00004EC4 : EQUD &00004924 : EQUD &00004444
 EQUD &00004000 : EQUD &00003C3C : EQUD &000038E3 : EQUD &000035E5
 EQUD &00003333 : EQUD &000030C3 : EQUD &00002E8B : EQUD &00002C85
 EQUD &00002AAA : EQUD &000028F5 : EQUD &00002762 : EQUD &000025ED
 EQUD &00002492 : EQUD &0000234F : EQUD &00002222 : EQUD &00002108
 EQUD &00002000 : EQUD &00001F07 : EQUD &00001E1E : EQUD &00001D41
 EQUD &00001C71 : EQUD &00001BAC : EQUD &00001AF2 : EQUD &00001A41
 EQUD &00001999 : EQUD &000018F9 : EQUD &00001861 : EQUD &000017D0
 EQUD &00001745 : EQUD &000016C1 : EQUD &00001642 : EQUD &000015C9
 EQUD &00001555 : EQUD &000014E5 : EQUD &0000147A : EQUD &00001414
 EQUD &000013B1 : EQUD &00001352 : EQUD &000012F6 : EQUD &0000129E
 EQUD &00001249 : EQUD &000011F7 : EQUD &000011A7 : EQUD &0000115B
 EQUD &00001111 : EQUD &000010C9 : EQUD &00001084 : EQUD &00001041

 EQUD &FFFFFFFF : EQUD &00050000 : EQUD &00028000 : EQUD &0001AAAA    \ n / 5 [n = 0 to 63]
 EQUD &00014000 : EQUD &00010000 : EQUD &0000D555 : EQUD &0000B6DB
 EQUD &0000A000 : EQUD &00008E38 : EQUD &00008000 : EQUD &0000745D
 EQUD &00006AAA : EQUD &00006276 : EQUD &00005B6D : EQUD &00005555
 EQUD &00005000 : EQUD &00004B4B : EQUD &0000471C : EQUD &0000435E
 EQUD &00004000 : EQUD &00003CF3 : EQUD &00003A2E : EQUD &000037A6
 EQUD &00003555 : EQUD &00003333 : EQUD &0000313B : EQUD &00002F68
 EQUD &00002DB6 : EQUD &00002C23 : EQUD &00002AAA : EQUD &0000294A
 EQUD &00002800 : EQUD &000026C9 : EQUD &000025A5 : EQUD &00002492
 EQUD &0000238E : EQUD &00002298 : EQUD &000021AF : EQUD &000020D2
 EQUD &00002000 : EQUD &00001F38 : EQUD &00001E79 : EQUD &00001DC4
 EQUD &00001D17 : EQUD &00001C71 : EQUD &00001BD3 : EQUD &00001B3B
 EQUD &00001AAA : EQUD &00001A1F : EQUD &00001999 : EQUD &00001919
 EQUD &0000189D : EQUD &00001826 : EQUD &000017B4 : EQUD &00001745
 EQUD &000016DB : EQUD &00001674 : EQUD &00001611 : EQUD &000015B1
 EQUD &00001555 : EQUD &000014FB : EQUD &000014A5 : EQUD &00001451

 EQUD &FFFFFFFF : EQUD &00060000 : EQUD &00030000 : EQUD &00020000    \ n / 6 [n = 0 to 63]
 EQUD &00018000 : EQUD &00013333 : EQUD &00010000 : EQUD &0000DB6D
 EQUD &0000C000 : EQUD &0000AAAA : EQUD &00009999 : EQUD &00008BA2
 EQUD &00008000 : EQUD &00007627 : EQUD &00006DB6 : EQUD &00006666
 EQUD &00006000 : EQUD &00005A5A : EQUD &00005555 : EQUD &000050D7
 EQUD &00004CCC : EQUD &00004924 : EQUD &000045D1 : EQUD &000042C8
 EQUD &00004000 : EQUD &00003D70 : EQUD &00003B13 : EQUD &000038E3
 EQUD &000036DB : EQUD &000034F7 : EQUD &00003333 : EQUD &0000318C
 EQUD &00003000 : EQUD &00002E8B : EQUD &00002D2D : EQUD &00002BE2
 EQUD &00002AAA : EQUD &00002983 : EQUD &0000286B : EQUD &00002762
 EQUD &00002666 : EQUD &00002576 : EQUD &00002492 : EQUD &000023B8
 EQUD &000022E8 : EQUD &00002222 : EQUD &00002164 : EQUD &000020AE
 EQUD &00002000 : EQUD &00001F58 : EQUD &00001EB8 : EQUD &00001E1E
 EQUD &00001D89 : EQUD &00001CFB : EQUD &00001C71 : EQUD &00001BED
 EQUD &00001B6D : EQUD &00001AF2 : EQUD &00001A7B : EQUD &00001A08
 EQUD &00001999 : EQUD &0000192E : EQUD &000018C6 : EQUD &00001861

 EQUD &FFFFFFFF : EQUD &00070000 : EQUD &00038000 : EQUD &00025555    \ n / 7 [n = 0 to 63]
 EQUD &0001C000 : EQUD &00016666 : EQUD &00012AAA : EQUD &00010000
 EQUD &0000E000 : EQUD &0000C71C : EQUD &0000B333 : EQUD &0000A2E8
 EQUD &00009555 : EQUD &000089D8 : EQUD &00008000 : EQUD &00007777
 EQUD &00007000 : EQUD &00006969 : EQUD &0000638E : EQUD &00005E50
 EQUD &00005999 : EQUD &00005555 : EQUD &00005174 : EQUD &00004DE9
 EQUD &00004AAA : EQUD &000047AE : EQUD &000044EC : EQUD &0000425E
 EQUD &00004000 : EQUD &00003DCB : EQUD &00003BBB : EQUD &000039CE
 EQUD &00003800 : EQUD &0000364D : EQUD &000034B4 : EQUD &00003333
 EQUD &000031C7 : EQUD &0000306E : EQUD &00002F28 : EQUD &00002DF2
 EQUD &00002CCC : EQUD &00002BB5 : EQUD &00002AAA : EQUD &000029AC
 EQUD &000028BA : EQUD &000027D2 : EQUD &000026F4 : EQUD &00002620
 EQUD &00002555 : EQUD &00002492 : EQUD &000023D7 : EQUD &00002323
 EQUD &00002276 : EQUD &000021CF : EQUD &0000212F : EQUD &00002094
 EQUD &00002000 : EQUD &00001F70 : EQUD &00001EE5 : EQUD &00001E5F
 EQUD &00001DDD : EQUD &00001D60 : EQUD &00001CE7 : EQUD &00001C71

 EQUD &FFFFFFFF : EQUD &00080000 : EQUD &00040000 : EQUD &0002AAAA    \ n / 8 [n = 0 to 63]
 EQUD &00020000 : EQUD &00019999 : EQUD &00015555 : EQUD &00012492
 EQUD &00010000 : EQUD &0000E38E : EQUD &0000CCCC : EQUD &0000BA2E
 EQUD &0000AAAA : EQUD &00009D89 : EQUD &00009249 : EQUD &00008888
 EQUD &00008000 : EQUD &00007878 : EQUD &000071C7 : EQUD &00006BCA
 EQUD &00006666 : EQUD &00006186 : EQUD &00005D17 : EQUD &0000590B
 EQUD &00005555 : EQUD &000051EB : EQUD &00004EC4 : EQUD &00004BDA
 EQUD &00004924 : EQUD &0000469E : EQUD &00004444 : EQUD &00004210
 EQUD &00004000 : EQUD &00003E0F : EQUD &00003C3C : EQUD &00003A83
 EQUD &000038E3 : EQUD &00003759 : EQUD &000035E5 : EQUD &00003483
 EQUD &00003333 : EQUD &000031F3 : EQUD &000030C3 : EQUD &00002FA0
 EQUD &00002E8B : EQUD &00002D82 : EQUD &00002C85 : EQUD &00002B93
 EQUD &00002AAA : EQUD &000029CB : EQUD &000028F5 : EQUD &00002828
 EQUD &00002762 : EQUD &000026A4 : EQUD &000025ED : EQUD &0000253C
 EQUD &00002492 : EQUD &000023EE : EQUD &0000234F : EQUD &000022B6
 EQUD &00002222 : EQUD &00002192 : EQUD &00002108 : EQUD &00002082

 EQUD &FFFFFFFF : EQUD &00090000 : EQUD &00048000 : EQUD &00030000    \ n / 9 [n = 0 to 63]
 EQUD &00024000 : EQUD &0001CCCC : EQUD &00018000 : EQUD &00014924
 EQUD &00012000 : EQUD &00010000 : EQUD &0000E666 : EQUD &0000D174
 EQUD &0000C000 : EQUD &0000B13B : EQUD &0000A492 : EQUD &00009999
 EQUD &00009000 : EQUD &00008787 : EQUD &00008000 : EQUD &00007943
 EQUD &00007333 : EQUD &00006DB6 : EQUD &000068BA : EQUD &0000642C
 EQUD &00006000 : EQUD &00005C28 : EQUD &0000589D : EQUD &00005555
 EQUD &00005249 : EQUD &00004F72 : EQUD &00004CCC : EQUD &00004A52
 EQUD &00004800 : EQUD &000045D1 : EQUD &000043C3 : EQUD &000041D4
 EQUD &00004000 : EQUD &00003E45 : EQUD &00003CA1 : EQUD &00003B13
 EQUD &00003999 : EQUD &00003831 : EQUD &000036DB : EQUD &00003594
 EQUD &0000345D : EQUD &00003333 : EQUD &00003216 : EQUD &00003105
 EQUD &00003000 : EQUD &00002F05 : EQUD &00002E14 : EQUD &00002D2D
 EQUD &00002C4E : EQUD &00002B78 : EQUD &00002AAA : EQUD &000029E4
 EQUD &00002924 : EQUD &0000286B : EQUD &000027B9 : EQUD &0000270D
 EQUD &00002666 : EQUD &000025C5 : EQUD &00002529 : EQUD &00002492

 EQUD &FFFFFFFF : EQUD &000A0000 : EQUD &00050000 : EQUD &00035555    \ n / 10 [n = 0 to 63]
 EQUD &00028000 : EQUD &00020000 : EQUD &0001AAAA : EQUD &00016DB6
 EQUD &00014000 : EQUD &00011C71 : EQUD &00010000 : EQUD &0000E8BA
 EQUD &0000D555 : EQUD &0000C4EC : EQUD &0000B6DB : EQUD &0000AAAA
 EQUD &0000A000 : EQUD &00009696 : EQUD &00008E38 : EQUD &000086BC
 EQUD &00008000 : EQUD &000079E7 : EQUD &0000745D : EQUD &00006F4D
 EQUD &00006AAA : EQUD &00006666 : EQUD &00006276 : EQUD &00005ED0
 EQUD &00005B6D : EQUD &00005846 : EQUD &00005555 : EQUD &00005294
 EQUD &00005000 : EQUD &00004D93 : EQUD &00004B4B : EQUD &00004924
 EQUD &0000471C : EQUD &00004530 : EQUD &0000435E : EQUD &000041A4
 EQUD &00004000 : EQUD &00003E70 : EQUD &00003CF3 : EQUD &00003B88
 EQUD &00003A2E : EQUD &000038E3 : EQUD &000037A6 : EQUD &00003677
 EQUD &00003555 : EQUD &0000343E : EQUD &00003333 : EQUD &00003232
 EQUD &0000313B : EQUD &0000304D : EQUD &00002F68 : EQUD &00002E8B
 EQUD &00002DB6 : EQUD &00002CE9 : EQUD &00002C23 : EQUD &00002B63
 EQUD &00002AAA : EQUD &000029F7 : EQUD &0000294A : EQUD &000028A2

 EQUD &FFFFFFFF : EQUD &000B0000 : EQUD &00058000 : EQUD &0003AAAA    \ n / 11 [n = 0 to 63]
 EQUD &0002C000 : EQUD &00023333 : EQUD &0001D555 : EQUD &00019249
 EQUD &00016000 : EQUD &000138E3 : EQUD &00011999 : EQUD &00010000
 EQUD &0000EAAA : EQUD &0000D89D : EQUD &0000C924 : EQUD &0000BBBB
 EQUD &0000B000 : EQUD &0000A5A5 : EQUD &00009C71 : EQUD &00009435
 EQUD &00008CCC : EQUD &00008618 : EQUD &00008000 : EQUD &00007A6F
 EQUD &00007555 : EQUD &000070A3 : EQUD &00006C4E : EQUD &0000684B
 EQUD &00006492 : EQUD &0000611A : EQUD &00005DDD : EQUD &00005AD6
 EQUD &00005800 : EQUD &00005555 : EQUD &000052D2 : EQUD &00005075
 EQUD &00004E38 : EQUD &00004C1B : EQUD &00004A1A : EQUD &00004834
 EQUD &00004666 : EQUD &000044AE : EQUD &0000430C : EQUD &0000417D
 EQUD &00004000 : EQUD &00003E93 : EQUD &00003D37 : EQUD &00003BEA
 EQUD &00003AAA : EQUD &00003978 : EQUD &00003851 : EQUD &00003737
 EQUD &00003627 : EQUD &00003521 : EQUD &00003425 : EQUD &00003333
 EQUD &00003249 : EQUD &00003167 : EQUD &0000308D : EQUD &00002FBA
 EQUD &00002EEE : EQUD &00002E29 : EQUD &00002D6B : EQUD &00002CB2

 EQUD &FFFFFFFF : EQUD &000C0000 : EQUD &00060000 : EQUD &00040000    \ n / 12 [n = 0 to 63]
 EQUD &00030000 : EQUD &00026666 : EQUD &00020000 : EQUD &0001B6DB
 EQUD &00018000 : EQUD &00015555 : EQUD &00013333 : EQUD &00011745
 EQUD &00010000 : EQUD &0000EC4E : EQUD &0000DB6D : EQUD &0000CCCC
 EQUD &0000C000 : EQUD &0000B4B4 : EQUD &0000AAAA : EQUD &0000A1AF
 EQUD &00009999 : EQUD &00009249 : EQUD &00008BA2 : EQUD &00008590
 EQUD &00008000 : EQUD &00007AE1 : EQUD &00007627 : EQUD &000071C7
 EQUD &00006DB6 : EQUD &000069EE : EQUD &00006666 : EQUD &00006318
 EQUD &00006000 : EQUD &00005D17 : EQUD &00005A5A : EQUD &000057C5
 EQUD &00005555 : EQUD &00005306 : EQUD &000050D7 : EQUD &00004EC4
 EQUD &00004CCC : EQUD &00004AED : EQUD &00004924 : EQUD &00004771
 EQUD &000045D1 : EQUD &00004444 : EQUD &000042C8 : EQUD &0000415C
 EQUD &00004000 : EQUD &00003EB1 : EQUD &00003D70 : EQUD &00003C3C
 EQUD &00003B13 : EQUD &000039F6 : EQUD &000038E3 : EQUD &000037DA
 EQUD &000036DB : EQUD &000035E5 : EQUD &000034F7 : EQUD &00003411
 EQUD &00003333 : EQUD &0000325C : EQUD &0000318C : EQUD &000030C3

 EQUD &FFFFFFFF : EQUD &000D0000 : EQUD &00068000 : EQUD &00045555    \ n / 13 [n = 0 to 63]
 EQUD &00034000 : EQUD &00029999 : EQUD &00022AAA : EQUD &0001DB6D
 EQUD &0001A000 : EQUD &000171C7 : EQUD &00014CCC : EQUD &00012E8B
 EQUD &00011555 : EQUD &00010000 : EQUD &0000EDB6 : EQUD &0000DDDD
 EQUD &0000D000 : EQUD &0000C3C3 : EQUD &0000B8E3 : EQUD &0000AF28
 EQUD &0000A666 : EQUD &00009E79 : EQUD &00009745 : EQUD &000090B2
 EQUD &00008AAA : EQUD &0000851E : EQUD &00008000 : EQUD &00007B42
 EQUD &000076DB : EQUD &000072C2 : EQUD &00006EEE : EQUD &00006B5A
 EQUD &00006800 : EQUD &000064D9 : EQUD &000061E1 : EQUD &00005F15
 EQUD &00005C71 : EQUD &000059F2 : EQUD &00005794 : EQUD &00005555
 EQUD &00005333 : EQUD &0000512B : EQUD &00004F3C : EQUD &00004D65
 EQUD &00004BA2 : EQUD &000049F4 : EQUD &00004859 : EQUD &000046CE
 EQUD &00004555 : EQUD &000043EB : EQUD &0000428F : EQUD &00004141
 EQUD &00004000 : EQUD &00003ECA : EQUD &00003DA1 : EQUD &00003C82
 EQUD &00003B6D : EQUD &00003A62 : EQUD &00003961 : EQUD &00003868
 EQUD &00003777 : EQUD &0000368E : EQUD &000035AD : EQUD &000034D3

 EQUD &FFFFFFFF : EQUD &000E0000 : EQUD &00070000 : EQUD &0004AAAA    \ n / 14 [n = 0 to 63]
 EQUD &00038000 : EQUD &0002CCCC : EQUD &00025555 : EQUD &00020000
 EQUD &0001C000 : EQUD &00018E38 : EQUD &00016666 : EQUD &000145D1
 EQUD &00012AAA : EQUD &000113B1 : EQUD &00010000 : EQUD &0000EEEE
 EQUD &0000E000 : EQUD &0000D2D2 : EQUD &0000C71C : EQUD &0000BCA1
 EQUD &0000B333 : EQUD &0000AAAA : EQUD &0000A2E8 : EQUD &00009BD3
 EQUD &00009555 : EQUD &00008F5C : EQUD &000089D8 : EQUD &000084BD
 EQUD &00008000 : EQUD &00007B96 : EQUD &00007777 : EQUD &0000739C
 EQUD &00007000 : EQUD &00006C9B : EQUD &00006969 : EQUD &00006666
 EQUD &0000638E : EQUD &000060DD : EQUD &00005E50 : EQUD &00005BE5
 EQUD &00005999 : EQUD &0000576A : EQUD &00005555 : EQUD &00005359
 EQUD &00005174 : EQUD &00004FA4 : EQUD &00004DE9 : EQUD &00004C41
 EQUD &00004AAA : EQUD &00004924 : EQUD &000047AE : EQUD &00004646
 EQUD &000044EC : EQUD &0000439F : EQUD &0000425E : EQUD &00004129
 EQUD &00004000 : EQUD &00003EE0 : EQUD &00003DCB : EQUD &00003CBE
 EQUD &00003BBB : EQUD &00003AC1 : EQUD &000039CE : EQUD &000038E3

 EQUD &FFFFFFFF : EQUD &000F0000 : EQUD &00078000 : EQUD &00050000    \ n / 15 [n = 0 to 63]
 EQUD &0003C000 : EQUD &00030000 : EQUD &00028000 : EQUD &00022492
 EQUD &0001E000 : EQUD &0001AAAA : EQUD &00018000 : EQUD &00015D17
 EQUD &00014000 : EQUD &00012762 : EQUD &00011249 : EQUD &00010000
 EQUD &0000F000 : EQUD &0000E1E1 : EQUD &0000D555 : EQUD &0000CA1A
 EQUD &0000C000 : EQUD &0000B6DB : EQUD &0000AE8B : EQUD &0000A6F4
 EQUD &0000A000 : EQUD &00009999 : EQUD &000093B1 : EQUD &00008E38
 EQUD &00008924 : EQUD &00008469 : EQUD &00008000 : EQUD &00007BDE
 EQUD &00007800 : EQUD &0000745D : EQUD &000070F0 : EQUD &00006DB6
 EQUD &00006AAA : EQUD &000067C8 : EQUD &0000650D : EQUD &00006276
 EQUD &00006000 : EQUD &00005DA8 : EQUD &00005B6D : EQUD &0000594D
 EQUD &00005745 : EQUD &00005555 : EQUD &0000537A : EQUD &000051B3
 EQUD &00005000 : EQUD &00004E5E : EQUD &00004CCC : EQUD &00004B4B
 EQUD &000049D8 : EQUD &00004873 : EQUD &0000471C : EQUD &000045D1
 EQUD &00004492 : EQUD &0000435E : EQUD &00004234 : EQUD &00004115
 EQUD &00004000 : EQUD &00003EF3 : EQUD &00003DEF : EQUD &00003CF3

 EQUD &FFFFFFFF : EQUD &00100000 : EQUD &00080000 : EQUD &00055555    \ n / 16 [n = 0 to 63]
 EQUD &00040000 : EQUD &00033333 : EQUD &0002AAAA : EQUD &00024924
 EQUD &00020000 : EQUD &0001C71C : EQUD &00019999 : EQUD &0001745D
 EQUD &00015555 : EQUD &00013B13 : EQUD &00012492 : EQUD &00011111
 EQUD &00010000 : EQUD &0000F0F0 : EQUD &0000E38E : EQUD &0000D794
 EQUD &0000CCCC : EQUD &0000C30C : EQUD &0000BA2E : EQUD &0000B216
 EQUD &0000AAAA : EQUD &0000A3D7 : EQUD &00009D89 : EQUD &000097B4
 EQUD &00009249 : EQUD &00008D3D : EQUD &00008888 : EQUD &00008421
 EQUD &00008000 : EQUD &00007C1F : EQUD &00007878 : EQUD &00007507
 EQUD &000071C7 : EQUD &00006EB3 : EQUD &00006BCA : EQUD &00006906
 EQUD &00006666 : EQUD &000063E7 : EQUD &00006186 : EQUD &00005F41
 EQUD &00005D17 : EQUD &00005B05 : EQUD &0000590B : EQUD &00005726
 EQUD &00005555 : EQUD &00005397 : EQUD &000051EB : EQUD &00005050
 EQUD &00004EC4 : EQUD &00004D48 : EQUD &00004BDA : EQUD &00004A79
 EQUD &00004924 : EQUD &000047DC : EQUD &0000469E : EQUD &0000456C
 EQUD &00004444 : EQUD &00004325 : EQUD &00004210 : EQUD &00004104

 EQUD &FFFFFFFF : EQUD &00110000 : EQUD &00088000 : EQUD &0005AAAA    \ n / 17 [n = 0 to 63]
 EQUD &00044000 : EQUD &00036666 : EQUD &0002D555 : EQUD &00026DB6
 EQUD &00022000 : EQUD &0001E38E : EQUD &0001B333 : EQUD &00018BA2
 EQUD &00016AAA : EQUD &00014EC4 : EQUD &000136DB : EQUD &00012222
 EQUD &00011000 : EQUD &00010000 : EQUD &0000F1C7 : EQUD &0000E50D
 EQUD &0000D999 : EQUD &0000CF3C : EQUD &0000C5D1 : EQUD &0000BD37
 EQUD &0000B555 : EQUD &0000AE14 : EQUD &0000A762 : EQUD &0000A12F
 EQUD &00009B6D : EQUD &00009611 : EQUD &00009111 : EQUD &00008C63
 EQUD &00008800 : EQUD &000083E0 : EQUD &00008000 : EQUD &00007C57
 EQUD &000078E3 : EQUD &0000759F : EQUD &00007286 : EQUD &00006F96
 EQUD &00006CCC : EQUD &00006A25 : EQUD &0000679E : EQUD &00006535
 EQUD &000062E8 : EQUD &000060B6 : EQUD &00005E9B : EQUD &00005C98
 EQUD &00005AAA : EQUD &000058D0 : EQUD &0000570A : EQUD &00005555
 EQUD &000053B1 : EQUD &0000521C : EQUD &00005097 : EQUD &00004F20
 EQUD &00004DB6 : EQUD &00004C59 : EQUD &00004B08 : EQUD &000049C3
 EQUD &00004888 : EQUD &00004758 : EQUD &00004631 : EQUD &00004514

 EQUD &FFFFFFFF : EQUD &00120000 : EQUD &00090000 : EQUD &00060000    \ n / 18 [n = 0 to 63]
 EQUD &00048000 : EQUD &00039999 : EQUD &00030000 : EQUD &00029249
 EQUD &00024000 : EQUD &00020000 : EQUD &0001CCCC : EQUD &0001A2E8
 EQUD &00018000 : EQUD &00016276 : EQUD &00014924 : EQUD &00013333
 EQUD &00012000 : EQUD &00010F0F : EQUD &00010000 : EQUD &0000F286
 EQUD &0000E666 : EQUD &0000DB6D : EQUD &0000D174 : EQUD &0000C859
 EQUD &0000C000 : EQUD &0000B851 : EQUD &0000B13B : EQUD &0000AAAA
 EQUD &0000A492 : EQUD &00009EE5 : EQUD &00009999 : EQUD &000094A5
 EQUD &00009000 : EQUD &00008BA2 : EQUD &00008787 : EQUD &000083A8
 EQUD &00008000 : EQUD &00007C8A : EQUD &00007943 : EQUD &00007627
 EQUD &00007333 : EQUD &00007063 : EQUD &00006DB6 : EQUD &00006B29
 EQUD &000068BA : EQUD &00006666 : EQUD &0000642C : EQUD &0000620A
 EQUD &00006000 : EQUD &00005E0A : EQUD &00005C28 : EQUD &00005A5A
 EQUD &0000589D : EQUD &000056F1 : EQUD &00005555 : EQUD &000053C8
 EQUD &00005249 : EQUD &000050D7 : EQUD &00004F72 : EQUD &00004E1A
 EQUD &00004CCC : EQUD &00004B8A : EQUD &00004A52 : EQUD &00004924

 EQUD &FFFFFFFF : EQUD &00130000 : EQUD &00098000 : EQUD &00065555    \ n / 19 [n = 0 to 63]
 EQUD &0004C000 : EQUD &0003CCCC : EQUD &00032AAA : EQUD &0002B6DB
 EQUD &00026000 : EQUD &00021C71 : EQUD &0001E666 : EQUD &0001BA2E
 EQUD &00019555 : EQUD &00017627 : EQUD &00015B6D : EQUD &00014444
 EQUD &00013000 : EQUD &00011E1E : EQUD &00010E38 : EQUD &00010000
 EQUD &0000F333 : EQUD &0000E79E : EQUD &0000DD17 : EQUD &0000D37A
 EQUD &0000CAAA : EQUD &0000C28F : EQUD &0000BB13 : EQUD &0000B425
 EQUD &0000ADB6 : EQUD &0000A7B9 : EQUD &0000A222 : EQUD &00009CE7
 EQUD &00009800 : EQUD &00009364 : EQUD &00008F0F : EQUD &00008AF8
 EQUD &0000871C : EQUD &00008375 : EQUD &00008000 : EQUD &00007CB7
 EQUD &00007999 : EQUD &000076A2 : EQUD &000073CF : EQUD &0000711D
 EQUD &00006E8B : EQUD &00006C16 : EQUD &000069BD : EQUD &0000677D
 EQUD &00006555 : EQUD &00006343 : EQUD &00006147 : EQUD &00005F5F
 EQUD &00005D89 : EQUD &00005BC6 : EQUD &00005A12 : EQUD &0000586F
 EQUD &000056DB : EQUD &00005555 : EQUD &000053DC : EQUD &00005270
 EQUD &00005111 : EQUD &00004FBC : EQUD &00004E73 : EQUD &00004D34

 EQUD &FFFFFFFF : EQUD &00140000 : EQUD &000A0000 : EQUD &0006AAAA    \ n / 20 [n = 0 to 63]
 EQUD &00050000 : EQUD &00040000 : EQUD &00035555 : EQUD &0002DB6D
 EQUD &00028000 : EQUD &000238E3 : EQUD &00020000 : EQUD &0001D174
 EQUD &0001AAAA : EQUD &000189D8 : EQUD &00016DB6 : EQUD &00015555
 EQUD &00014000 : EQUD &00012D2D : EQUD &00011C71 : EQUD &00010D79
 EQUD &00010000 : EQUD &0000F3CF : EQUD &0000E8BA : EQUD &0000DE9B
 EQUD &0000D555 : EQUD &0000CCCC : EQUD &0000C4EC : EQUD &0000BDA1
 EQUD &0000B6DB : EQUD &0000B08D : EQUD &0000AAAA : EQUD &0000A529
 EQUD &0000A000 : EQUD &00009B26 : EQUD &00009696 : EQUD &00009249
 EQUD &00008E38 : EQUD &00008A60 : EQUD &000086BC : EQUD &00008348
 EQUD &00008000 : EQUD &00007CE0 : EQUD &000079E7 : EQUD &00007711
 EQUD &0000745D : EQUD &000071C7 : EQUD &00006F4D : EQUD &00006CEF
 EQUD &00006AAA : EQUD &0000687D : EQUD &00006666 : EQUD &00006464
 EQUD &00006276 : EQUD &0000609A : EQUD &00005ED0 : EQUD &00005D17
 EQUD &00005B6D : EQUD &000059D3 : EQUD &00005846 : EQUD &000056C7
 EQUD &00005555 : EQUD &000053EF : EQUD &00005294 : EQUD &00005145

 EQUD &FFFFFFFF : EQUD &00150000 : EQUD &000A8000 : EQUD &00070000    \ n / 21 [n = 0 to 63]
 EQUD &00054000 : EQUD &00043333 : EQUD &00038000 : EQUD &00030000
 EQUD &0002A000 : EQUD &00025555 : EQUD &00021999 : EQUD &0001E8BA
 EQUD &0001C000 : EQUD &00019D89 : EQUD &00018000 : EQUD &00016666
 EQUD &00015000 : EQUD &00013C3C : EQUD &00012AAA : EQUD &00011AF2
 EQUD &00010CCC : EQUD &00010000 : EQUD &0000F45D : EQUD &0000E9BD
 EQUD &0000E000 : EQUD &0000D70A : EQUD &0000CEC4 : EQUD &0000C71C
 EQUD &0000C000 : EQUD &0000B961 : EQUD &0000B333 : EQUD &0000AD6B
 EQUD &0000A800 : EQUD &0000A2E8 : EQUD &00009E1E : EQUD &00009999
 EQUD &00009555 : EQUD &0000914C : EQUD &00008D79 : EQUD &000089D8
 EQUD &00008666 : EQUD &0000831F : EQUD &00008000 : EQUD &00007D05
 EQUD &00007A2E : EQUD &00007777 : EQUD &000074DE : EQUD &00007262
 EQUD &00007000 : EQUD &00006DB6 : EQUD &00006B85 : EQUD &00006969
 EQUD &00006762 : EQUD &0000656F : EQUD &0000638E : EQUD &000061BE
 EQUD &00006000 : EQUD &00005E50 : EQUD &00005CB0 : EQUD &00005B1E
 EQUD &00005999 : EQUD &00005821 : EQUD &000056B5 : EQUD &00005555

 EQUD &FFFFFFFF : EQUD &00160000 : EQUD &000B0000 : EQUD &00075555    \ n / 22 [n = 0 to 63]
 EQUD &00058000 : EQUD &00046666 : EQUD &0003AAAA : EQUD &00032492
 EQUD &0002C000 : EQUD &000271C7 : EQUD &00023333 : EQUD &00020000
 EQUD &0001D555 : EQUD &0001B13B : EQUD &00019249 : EQUD &00017777
 EQUD &00016000 : EQUD &00014B4B : EQUD &000138E3 : EQUD &0001286B
 EQUD &00011999 : EQUD &00010C30 : EQUD &00010000 : EQUD &0000F4DE
 EQUD &0000EAAA : EQUD &0000E147 : EQUD &0000D89D : EQUD &0000D097
 EQUD &0000C924 : EQUD &0000C234 : EQUD &0000BBBB : EQUD &0000B5AD
 EQUD &0000B000 : EQUD &0000AAAA : EQUD &0000A5A5 : EQUD &0000A0EA
 EQUD &00009C71 : EQUD &00009837 : EQUD &00009435 : EQUD &00009069
 EQUD &00008CCC : EQUD &0000895D : EQUD &00008618 : EQUD &000082FA
 EQUD &00008000 : EQUD &00007D27 : EQUD &00007A6F : EQUD &000077D4
 EQUD &00007555 : EQUD &000072F0 : EQUD &000070A3 : EQUD &00006E6E
 EQUD &00006C4E : EQUD &00006A43 : EQUD &0000684B : EQUD &00006666
 EQUD &00006492 : EQUD &000062CE : EQUD &0000611A : EQUD &00005F75
 EQUD &00005DDD : EQUD &00005C53 : EQUD &00005AD6 : EQUD &00005965

 EQUD &FFFFFFFF : EQUD &00170000 : EQUD &000B8000 : EQUD &0007AAAA    \ n / 23 [n = 0 to 63]
 EQUD &0005C000 : EQUD &00049999 : EQUD &0003D555 : EQUD &00034924
 EQUD &0002E000 : EQUD &00028E38 : EQUD &00024CCC : EQUD &00021745
 EQUD &0001EAAA : EQUD &0001C4EC : EQUD &0001A492 : EQUD &00018888
 EQUD &00017000 : EQUD &00015A5A : EQUD &0001471C : EQUD &000135E5
 EQUD &00012666 : EQUD &00011861 : EQUD &00010BA2 : EQUD &00010000
 EQUD &0000F555 : EQUD &0000EB85 : EQUD &0000E276 : EQUD &0000DA12
 EQUD &0000D249 : EQUD &0000CB08 : EQUD &0000C444 : EQUD &0000BDEF
 EQUD &0000B800 : EQUD &0000B26C : EQUD &0000AD2D : EQUD &0000A83A
 EQUD &0000A38E : EQUD &00009F22 : EQUD &00009AF2 : EQUD &000096F9
 EQUD &00009333 : EQUD &00008F9C : EQUD &00008C30 : EQUD &000088EE
 EQUD &000085D1 : EQUD &000082D8 : EQUD &00008000 : EQUD &00007D46
 EQUD &00007AAA : EQUD &00007829 : EQUD &000075C2 : EQUD &00007373
 EQUD &0000713B : EQUD &00006F18 : EQUD &00006D09 : EQUD &00006B0D
 EQUD &00006924 : EQUD &0000674C : EQUD &00006584 : EQUD &000063CB
 EQUD &00006222 : EQUD &00006086 : EQUD &00005EF7 : EQUD &00005D75

 EQUD &FFFFFFFF : EQUD &00180000 : EQUD &000C0000 : EQUD &00080000    \ n / 24 [n = 0 to 63]
 EQUD &00060000 : EQUD &0004CCCC : EQUD &00040000 : EQUD &00036DB6
 EQUD &00030000 : EQUD &0002AAAA : EQUD &00026666 : EQUD &00022E8B
 EQUD &00020000 : EQUD &0001D89D : EQUD &0001B6DB : EQUD &00019999
 EQUD &00018000 : EQUD &00016969 : EQUD &00015555 : EQUD &0001435E
 EQUD &00013333 : EQUD &00012492 : EQUD &00011745 : EQUD &00010B21
 EQUD &00010000 : EQUD &0000F5C2 : EQUD &0000EC4E : EQUD &0000E38E
 EQUD &0000DB6D : EQUD &0000D3DC : EQUD &0000CCCC : EQUD &0000C631
 EQUD &0000C000 : EQUD &0000BA2E : EQUD &0000B4B4 : EQUD &0000AF8A
 EQUD &0000AAAA : EQUD &0000A60D : EQUD &0000A1AF : EQUD &00009D89
 EQUD &00009999 : EQUD &000095DA : EQUD &00009249 : EQUD &00008EE2
 EQUD &00008BA2 : EQUD &00008888 : EQUD &00008590 : EQUD &000082B9
 EQUD &00008000 : EQUD &00007D63 : EQUD &00007AE1 : EQUD &00007878
 EQUD &00007627 : EQUD &000073EC : EQUD &000071C7 : EQUD &00006FB5
 EQUD &00006DB6 : EQUD &00006BCA : EQUD &000069EE : EQUD &00006822
 EQUD &00006666 : EQUD &000064B8 : EQUD &00006318 : EQUD &00006186

 EQUD &FFFFFFFF : EQUD &00190000 : EQUD &000C8000 : EQUD &00085555    \ n / 25 [n = 0 to 63]
 EQUD &00064000 : EQUD &00050000 : EQUD &00042AAA : EQUD &00039249
 EQUD &00032000 : EQUD &0002C71C : EQUD &00028000 : EQUD &000245D1
 EQUD &00021555 : EQUD &0001EC4E : EQUD &0001C924 : EQUD &0001AAAA
 EQUD &00019000 : EQUD &00017878 : EQUD &0001638E : EQUD &000150D7
 EQUD &00014000 : EQUD &000130C3 : EQUD &000122E8 : EQUD &00011642
 EQUD &00010AAA : EQUD &00010000 : EQUD &0000F627 : EQUD &0000ED09
 EQUD &0000E492 : EQUD &0000DCB0 : EQUD &0000D555 : EQUD &0000CE73
 EQUD &0000C800 : EQUD &0000C1F0 : EQUD &0000BC3C : EQUD &0000B6DB
 EQUD &0000B1C7 : EQUD &0000ACF9 : EQUD &0000A86B : EQUD &0000A41A
 EQUD &0000A000 : EQUD &00009C18 : EQUD &00009861 : EQUD &000094D6
 EQUD &00009174 : EQUD &00008E38 : EQUD &00008B21 : EQUD &0000882B
 EQUD &00008555 : EQUD &0000829C : EQUD &00008000 : EQUD &00007D7D
 EQUD &00007B13 : EQUD &000078C1 : EQUD &00007684 : EQUD &0000745D
 EQUD &00007249 : EQUD &00007047 : EQUD &00006E58 : EQUD &00006C79
 EQUD &00006AAA : EQUD &000068EB : EQUD &00006739 : EQUD &00006596

 EQUD &FFFFFFFF : EQUD &001A0000 : EQUD &000D0000 : EQUD &0008AAAA    \ n / 26 [n = 0 to 63]
 EQUD &00068000 : EQUD &00053333 : EQUD &00045555 : EQUD &0003B6DB
 EQUD &00034000 : EQUD &0002E38E : EQUD &00029999 : EQUD &00025D17
 EQUD &00022AAA : EQUD &00020000 : EQUD &0001DB6D : EQUD &0001BBBB
 EQUD &0001A000 : EQUD &00018787 : EQUD &000171C7 : EQUD &00015E50
 EQUD &00014CCC : EQUD &00013CF3 : EQUD &00012E8B : EQUD &00012164
 EQUD &00011555 : EQUD &00010A3D : EQUD &00010000 : EQUD &0000F684
 EQUD &0000EDB6 : EQUD &0000E584 : EQUD &0000DDDD : EQUD &0000D6B5
 EQUD &0000D000 : EQUD &0000C9B2 : EQUD &0000C3C3 : EQUD &0000BE2B
 EQUD &0000B8E3 : EQUD &0000B3E4 : EQUD &0000AF28 : EQUD &0000AAAA
 EQUD &0000A666 : EQUD &0000A257 : EQUD &00009E79 : EQUD &00009ACA
 EQUD &00009745 : EQUD &000093E9 : EQUD &000090B2 : EQUD &00008D9D
 EQUD &00008AAA : EQUD &000087D6 : EQUD &0000851E : EQUD &00008282
 EQUD &00008000 : EQUD &00007D95 : EQUD &00007B42 : EQUD &00007904
 EQUD &000076DB : EQUD &000074C5 : EQUD &000072C2 : EQUD &000070D0
 EQUD &00006EEE : EQUD &00006D1D : EQUD &00006B5A : EQUD &000069A6

 EQUD &FFFFFFFF : EQUD &001B0000 : EQUD &000D8000 : EQUD &00090000    \ n / 27 [n = 0 to 63]
 EQUD &0006C000 : EQUD &00056666 : EQUD &00048000 : EQUD &0003DB6D
 EQUD &00036000 : EQUD &00030000 : EQUD &0002B333 : EQUD &0002745D
 EQUD &00024000 : EQUD &000213B1 : EQUD &0001EDB6 : EQUD &0001CCCC
 EQUD &0001B000 : EQUD &00019696 : EQUD &00018000 : EQUD &00016BCA
 EQUD &00015999 : EQUD &00014924 : EQUD &00013A2E : EQUD &00012C85
 EQUD &00012000 : EQUD &0001147A : EQUD &000109D8 : EQUD &00010000
 EQUD &0000F6DB : EQUD &0000EE58 : EQUD &0000E666 : EQUD &0000DEF7
 EQUD &0000D800 : EQUD &0000D174 : EQUD &0000CB4B : EQUD &0000C57C
 EQUD &0000C000 : EQUD &0000BACF : EQUD &0000B5E5 : EQUD &0000B13B
 EQUD &0000ACCC : EQUD &0000A895 : EQUD &0000A492 : EQUD &0000A0BE
 EQUD &00009D17 : EQUD &00009999 : EQUD &00009642 : EQUD &00009310
 EQUD &00009000 : EQUD &00008D0F : EQUD &00008A3D : EQUD &00008787
 EQUD &000084EC : EQUD &0000826A : EQUD &00008000 : EQUD &00007DAC
 EQUD &00007B6D : EQUD &00007943 : EQUD &0000772C : EQUD &00007527
 EQUD &00007333 : EQUD &0000714F : EQUD &00006F7B : EQUD &00006DB6

 EQUD &FFFFFFFF : EQUD &001C0000 : EQUD &000E0000 : EQUD &00095555    \ n / 28 [n = 0 to 63]
 EQUD &00070000 : EQUD &00059999 : EQUD &0004AAAA : EQUD &00040000
 EQUD &00038000 : EQUD &00031C71 : EQUD &0002CCCC : EQUD &00028BA2
 EQUD &00025555 : EQUD &00022762 : EQUD &00020000 : EQUD &0001DDDD
 EQUD &0001C000 : EQUD &0001A5A5 : EQUD &00018E38 : EQUD &00017943
 EQUD &00016666 : EQUD &00015555 : EQUD &000145D1 : EQUD &000137A6
 EQUD &00012AAA : EQUD &00011EB8 : EQUD &000113B1 : EQUD &0001097B
 EQUD &00010000 : EQUD &0000F72C : EQUD &0000EEEE : EQUD &0000E739
 EQUD &0000E000 : EQUD &0000D936 : EQUD &0000D2D2 : EQUD &0000CCCC
 EQUD &0000C71C : EQUD &0000C1BA : EQUD &0000BCA1 : EQUD &0000B7CB
 EQUD &0000B333 : EQUD &0000AED4 : EQUD &0000AAAA : EQUD &0000A6B2
 EQUD &0000A2E8 : EQUD &00009F49 : EQUD &00009BD3 : EQUD &00009882
 EQUD &00009555 : EQUD &00009249 : EQUD &00008F5C : EQUD &00008C8C
 EQUD &000089D8 : EQUD &0000873E : EQUD &000084BD : EQUD &00008253
 EQUD &00008000 : EQUD &00007DC1 : EQUD &00007B96 : EQUD &0000797D
 EQUD &00007777 : EQUD &00007582 : EQUD &0000739C : EQUD &000071C7

 EQUD &FFFFFFFF : EQUD &001D0000 : EQUD &000E8000 : EQUD &0009AAAA    \ n / 29 [n = 0 to 63]
 EQUD &00074000 : EQUD &0005CCCC : EQUD &0004D555 : EQUD &00042492
 EQUD &0003A000 : EQUD &000338E3 : EQUD &0002E666 : EQUD &0002A2E8
 EQUD &00026AAA : EQUD &00023B13 : EQUD &00021249 : EQUD &0001EEEE
 EQUD &0001D000 : EQUD &0001B4B4 : EQUD &00019C71 : EQUD &000186BC
 EQUD &00017333 : EQUD &00016186 : EQUD &00015174 : EQUD &000142C8
 EQUD &00013555 : EQUD &000128F5 : EQUD &00011D89 : EQUD &000112F6
 EQUD &00010924 : EQUD &00010000 : EQUD &0000F777 : EQUD &0000EF7B
 EQUD &0000E800 : EQUD &0000E0F8 : EQUD &0000DA5A : EQUD &0000D41D
 EQUD &0000CE38 : EQUD &0000C8A6 : EQUD &0000C35E : EQUD &0000BE5B
 EQUD &0000B999 : EQUD &0000B512 : EQUD &0000B0C3 : EQUD &0000ACA6
 EQUD &0000A8BA : EQUD &0000A4FA : EQUD &0000A164 : EQUD &00009DF5
 EQUD &00009AAA : EQUD &00009782 : EQUD &0000947A : EQUD &00009191
 EQUD &00008EC4 : EQUD &00008C13 : EQUD &0000897B : EQUD &000086FB
 EQUD &00008492 : EQUD &0000823E : EQUD &00008000 : EQUD &00007DD4
 EQUD &00007BBB : EQUD &000079B4 : EQUD &000077BD : EQUD &000075D7

 EQUD &FFFFFFFF : EQUD &001E0000 : EQUD &000F0000 : EQUD &000A0000    \ n / 30 [n = 0 to 63]
 EQUD &00078000 : EQUD &00060000 : EQUD &00050000 : EQUD &00044924
 EQUD &0003C000 : EQUD &00035555 : EQUD &00030000 : EQUD &0002BA2E
 EQUD &00028000 : EQUD &00024EC4 : EQUD &00022492 : EQUD &00020000
 EQUD &0001E000 : EQUD &0001C3C3 : EQUD &0001AAAA : EQUD &00019435
 EQUD &00018000 : EQUD &00016DB6 : EQUD &00015D17 : EQUD &00014DE9
 EQUD &00014000 : EQUD &00013333 : EQUD &00012762 : EQUD &00011C71
 EQUD &00011249 : EQUD &000108D3 : EQUD &00010000 : EQUD &0000F7BD
 EQUD &0000F000 : EQUD &0000E8BA : EQUD &0000E1E1 : EQUD &0000DB6D
 EQUD &0000D555 : EQUD &0000CF91 : EQUD &0000CA1A : EQUD &0000C4EC
 EQUD &0000C000 : EQUD &0000BB51 : EQUD &0000B6DB : EQUD &0000B29A
 EQUD &0000AE8B : EQUD &0000AAAA : EQUD &0000A6F4 : EQUD &0000A367
 EQUD &0000A000 : EQUD &00009CBC : EQUD &00009999 : EQUD &00009696
 EQUD &000093B1 : EQUD &000090E7 : EQUD &00008E38 : EQUD &00008BA2
 EQUD &00008924 : EQUD &000086BC : EQUD &00008469 : EQUD &0000822B
 EQUD &00008000 : EQUD &00007DE6 : EQUD &00007BDE : EQUD &000079E7

 EQUD &FFFFFFFF : EQUD &001F0000 : EQUD &000F8000 : EQUD &000A5555    \ n / 31 [n = 0 to 63]
 EQUD &0007C000 : EQUD &00063333 : EQUD &00052AAA : EQUD &00046DB6
 EQUD &0003E000 : EQUD &000371C7 : EQUD &00031999 : EQUD &0002D174
 EQUD &00029555 : EQUD &00026276 : EQUD &000236DB : EQUD &00021111
 EQUD &0001F000 : EQUD &0001D2D2 : EQUD &0001B8E3 : EQUD &0001A1AF
 EQUD &00018CCC : EQUD &000179E7 : EQUD &000168BA : EQUD &0001590B
 EQUD &00014AAA : EQUD &00013D70 : EQUD &0001313B : EQUD &000125ED
 EQUD &00011B6D : EQUD &000111A7 : EQUD &00010888 : EQUD &00010000
 EQUD &0000F800 : EQUD &0000F07C : EQUD &0000E969 : EQUD &0000E2BE
 EQUD &0000DC71 : EQUD &0000D67C : EQUD &0000D0D7 : EQUD &0000CB7C
 EQUD &0000C666 : EQUD &0000C18F : EQUD &0000BCF3 : EQUD &0000B88E
 EQUD &0000B45D : EQUD &0000B05B : EQUD &0000AC85 : EQUD &0000A8D9
 EQUD &0000A555 : EQUD &0000A1F5 : EQUD &00009EB8 : EQUD &00009B9B
 EQUD &0000989D : EQUD &000095BC : EQUD &000092F6 : EQUD &0000904A
 EQUD &00008DB6 : EQUD &00008B3A : EQUD &000088D3 : EQUD &00008682
 EQUD &00008444 : EQUD &00008219 : EQUD &00008000 : EQUD &00007DF7

 EQUD &FFFFFFFF : EQUD &00200000 : EQUD &00100000 : EQUD &000AAAAA    \ n / 32 [n = 0 to 63]
 EQUD &00080000 : EQUD &00066666 : EQUD &00055555 : EQUD &00049249
 EQUD &00040000 : EQUD &00038E38 : EQUD &00033333 : EQUD &0002E8BA
 EQUD &0002AAAA : EQUD &00027627 : EQUD &00024924 : EQUD &00022222
 EQUD &00020000 : EQUD &0001E1E1 : EQUD &0001C71C : EQUD &0001AF28
 EQUD &00019999 : EQUD &00018618 : EQUD &0001745D : EQUD &0001642C
 EQUD &00015555 : EQUD &000147AE : EQUD &00013B13 : EQUD &00012F68
 EQUD &00012492 : EQUD &00011A7B : EQUD &00011111 : EQUD &00010842
 EQUD &00010000 : EQUD &0000F83E : EQUD &0000F0F0 : EQUD &0000EA0E
 EQUD &0000E38E : EQUD &0000DD67 : EQUD &0000D794 : EQUD &0000D20D
 EQUD &0000CCCC : EQUD &0000C7CE : EQUD &0000C30C : EQUD &0000BE82
 EQUD &0000BA2E : EQUD &0000B60B : EQUD &0000B216 : EQUD &0000AE4C
 EQUD &0000AAAA : EQUD &0000A72F : EQUD &0000A3D7 : EQUD &0000A0A0
 EQUD &00009D89 : EQUD &00009A90 : EQUD &000097B4 : EQUD &000094F2
 EQUD &00009249 : EQUD &00008FB8 : EQUD &00008D3D : EQUD &00008AD8
 EQUD &00008888 : EQUD &0000864B : EQUD &00008421 : EQUD &00008208

 EQUD &FFFFFFFF : EQUD &00210000 : EQUD &00108000 : EQUD &000B0000    \ n / 33 [n = 0 to 63]
 EQUD &00084000 : EQUD &00069999 : EQUD &00058000 : EQUD &0004B6DB
 EQUD &00042000 : EQUD &0003AAAA : EQUD &00034CCC : EQUD &00030000
 EQUD &0002C000 : EQUD &000289D8 : EQUD &00025B6D : EQUD &00023333
 EQUD &00021000 : EQUD &0001F0F0 : EQUD &0001D555 : EQUD &0001BCA1
 EQUD &0001A666 : EQUD &00019249 : EQUD &00018000 : EQUD &00016F4D
 EQUD &00016000 : EQUD &000151EB : EQUD &000144EC : EQUD &000138E3
 EQUD &00012DB6 : EQUD &0001234F : EQUD &00011999 : EQUD &00011084
 EQUD &00010800 : EQUD &00010000 : EQUD &0000F878 : EQUD &0000F15F
 EQUD &0000EAAA : EQUD &0000E453 : EQUD &0000DE50 : EQUD &0000D89D
 EQUD &0000D333 : EQUD &0000CE0C : EQUD &0000C924 : EQUD &0000C477
 EQUD &0000C000 : EQUD &0000BBBB : EQUD &0000B7A6 : EQUD &0000B3BE
 EQUD &0000B000 : EQUD &0000AC68 : EQUD &0000A8F5 : EQUD &0000A5A5
 EQUD &0000A276 : EQUD &00009F65 : EQUD &00009C71 : EQUD &00009999
 EQUD &000096DB : EQUD &00009435 : EQUD &000091A7 : EQUD &00008F2F
 EQUD &00008CCC : EQUD &00008A7D : EQUD &00008842 : EQUD &00008618

 EQUD &FFFFFFFF : EQUD &00220000 : EQUD &00110000 : EQUD &000B5555    \ n / 34 [n = 0 to 63]
 EQUD &00088000 : EQUD &0006CCCC : EQUD &0005AAAA : EQUD &0004DB6D
 EQUD &00044000 : EQUD &0003C71C : EQUD &00036666 : EQUD &00031745
 EQUD &0002D555 : EQUD &00029D89 : EQUD &00026DB6 : EQUD &00024444
 EQUD &00022000 : EQUD &00020000 : EQUD &0001E38E : EQUD &0001CA1A
 EQUD &0001B333 : EQUD &00019E79 : EQUD &00018BA2 : EQUD &00017A6F
 EQUD &00016AAA : EQUD &00015C28 : EQUD &00014EC4 : EQUD &0001425E
 EQUD &000136DB : EQUD &00012C23 : EQUD &00012222 : EQUD &000118C6
 EQUD &00011000 : EQUD &000107C1 : EQUD &00010000 : EQUD &0000F8AF
 EQUD &0000F1C7 : EQUD &0000EB3E : EQUD &0000E50D : EQUD &0000DF2D
 EQUD &0000D999 : EQUD &0000D44A : EQUD &0000CF3C : EQUD &0000CA6B
 EQUD &0000C5D1 : EQUD &0000C16C : EQUD &0000BD37 : EQUD &0000B931
 EQUD &0000B555 : EQUD &0000B1A1 : EQUD &0000AE14 : EQUD &0000AAAA
 EQUD &0000A762 : EQUD &0000A439 : EQUD &0000A12F : EQUD &00009E41
 EQUD &00009B6D : EQUD &000098B3 : EQUD &00009611 : EQUD &00009386
 EQUD &00009111 : EQUD &00008EB0 : EQUD &00008C63 : EQUD &00008A28

 EQUD &FFFFFFFF : EQUD &00230000 : EQUD &00118000 : EQUD &000BAAAA    \ n / 35 [n = 0 to 63]
 EQUD &0008C000 : EQUD &00070000 : EQUD &0005D555 : EQUD &00050000
 EQUD &00046000 : EQUD &0003E38E : EQUD &00038000 : EQUD &00032E8B
 EQUD &0002EAAA : EQUD &0002B13B : EQUD &00028000 : EQUD &00025555
 EQUD &00023000 : EQUD &00020F0F : EQUD &0001F1C7 : EQUD &0001D794
 EQUD &0001C000 : EQUD &0001AAAA : EQUD &00019745 : EQUD &00018590
 EQUD &00017555 : EQUD &00016666 : EQUD &0001589D : EQUD &00014BDA
 EQUD &00014000 : EQUD &000134F7 : EQUD &00012AAA : EQUD &00012108
 EQUD &00011800 : EQUD &00010F83 : EQUD &00010787 : EQUD &00010000
 EQUD &0000F8E3 : EQUD &0000F229 : EQUD &0000EBCA : EQUD &0000E5BE
 EQUD &0000E000 : EQUD &0000DA89 : EQUD &0000D555 : EQUD &0000D05F
 EQUD &0000CBA2 : EQUD &0000C71C : EQUD &0000C2C8 : EQUD &0000BEA3
 EQUD &0000BAAA : EQUD &0000B6DB : EQUD &0000B333 : EQUD &0000AFAF
 EQUD &0000AC4E : EQUD &0000A90E : EQUD &0000A5ED : EQUD &0000A2E8
 EQUD &0000A000 : EQUD &00009D31 : EQUD &00009A7B : EQUD &000097DD
 EQUD &00009555 : EQUD &000092E2 : EQUD &00009084 : EQUD &00008E38

 EQUD &FFFFFFFF : EQUD &00240000 : EQUD &00120000 : EQUD &000C0000    \ n / 36 [n = 0 to 63]
 EQUD &00090000 : EQUD &00073333 : EQUD &00060000 : EQUD &00052492
 EQUD &00048000 : EQUD &00040000 : EQUD &00039999 : EQUD &000345D1
 EQUD &00030000 : EQUD &0002C4EC : EQUD &00029249 : EQUD &00026666
 EQUD &00024000 : EQUD &00021E1E : EQUD &00020000 : EQUD &0001E50D
 EQUD &0001CCCC : EQUD &0001B6DB : EQUD &0001A2E8 : EQUD &000190B2
 EQUD &00018000 : EQUD &000170A3 : EQUD &00016276 : EQUD &00015555
 EQUD &00014924 : EQUD &00013DCB : EQUD &00013333 : EQUD &0001294A
 EQUD &00012000 : EQUD &00011745 : EQUD &00010F0F : EQUD &00010750
 EQUD &00010000 : EQUD &0000F914 : EQUD &0000F286 : EQUD &0000EC4E
 EQUD &0000E666 : EQUD &0000E0C7 : EQUD &0000DB6D : EQUD &0000D653
 EQUD &0000D174 : EQUD &0000CCCC : EQUD &0000C859 : EQUD &0000C415
 EQUD &0000C000 : EQUD &0000BC14 : EQUD &0000B851 : EQUD &0000B4B4
 EQUD &0000B13B : EQUD &0000ADE3 : EQUD &0000AAAA : EQUD &0000A790
 EQUD &0000A492 : EQUD &0000A1AF : EQUD &00009EE5 : EQUD &00009C34
 EQUD &00009999 : EQUD &00009714 : EQUD &000094A5 : EQUD &00009249

 EQUD &FFFFFFFF : EQUD &00250000 : EQUD &00128000 : EQUD &000C5555    \ n / 37 [n = 0 to 63]
 EQUD &00094000 : EQUD &00076666 : EQUD &00062AAA : EQUD &00054924
 EQUD &0004A000 : EQUD &00041C71 : EQUD &0003B333 : EQUD &00035D17
 EQUD &00031555 : EQUD &0002D89D : EQUD &0002A492 : EQUD &00027777
 EQUD &00025000 : EQUD &00022D2D : EQUD &00020E38 : EQUD &0001F286
 EQUD &0001D999 : EQUD &0001C30C : EQUD &0001AE8B : EQUD &00019BD3
 EQUD &00018AAA : EQUD &00017AE1 : EQUD &00016C4E : EQUD &00015ED0
 EQUD &00015249 : EQUD &0001469E : EQUD &00013BBB : EQUD &0001318C
 EQUD &00012800 : EQUD &00011F07 : EQUD &00011696 : EQUD &00010EA0
 EQUD &0001071C : EQUD &00010000 : EQUD &0000F943 : EQUD &0000F2DF
 EQUD &0000ECCC : EQUD &0000E706 : EQUD &0000E186 : EQUD &0000DC47
 EQUD &0000D745 : EQUD &0000D27D : EQUD &0000CDE9 : EQUD &0000C988
 EQUD &0000C555 : EQUD &0000C14E : EQUD &0000BD70 : EQUD &0000B9B9
 EQUD &0000B627 : EQUD &0000B2B7 : EQUD &0000AF68 : EQUD &0000AC37
 EQUD &0000A924 : EQUD &0000A62C : EQUD &0000A34F : EQUD &0000A08A
 EQUD &00009DDD : EQUD &00009B47 : EQUD &000098C6 : EQUD &00009659

 EQUD &FFFFFFFF : EQUD &00260000 : EQUD &00130000 : EQUD &000CAAAA    \ n / 38 [n = 0 to 63]
 EQUD &00098000 : EQUD &00079999 : EQUD &00065555 : EQUD &00056DB6
 EQUD &0004C000 : EQUD &000438E3 : EQUD &0003CCCC : EQUD &0003745D
 EQUD &00032AAA : EQUD &0002EC4E : EQUD &0002B6DB : EQUD &00028888
 EQUD &00026000 : EQUD &00023C3C : EQUD &00021C71 : EQUD &00020000
 EQUD &0001E666 : EQUD &0001CF3C : EQUD &0001BA2E : EQUD &0001A6F4
 EQUD &00019555 : EQUD &0001851E : EQUD &00017627 : EQUD &0001684B
 EQUD &00015B6D : EQUD &00014F72 : EQUD &00014444 : EQUD &000139CE
 EQUD &00013000 : EQUD &000126C9 : EQUD &00011E1E : EQUD &000115F1
 EQUD &00010E38 : EQUD &000106EB : EQUD &00010000 : EQUD &0000F96F
 EQUD &0000F333 : EQUD &0000ED44 : EQUD &0000E79E : EQUD &0000E23B
 EQUD &0000DD17 : EQUD &0000D82D : EQUD &0000D37A : EQUD &0000CEFA
 EQUD &0000CAAA : EQUD &0000C687 : EQUD &0000C28F : EQUD &0000BEBE
 EQUD &0000BB13 : EQUD &0000B78C : EQUD &0000B425 : EQUD &0000B0DF
 EQUD &0000ADB6 : EQUD &0000AAAA : EQUD &0000A7B9 : EQUD &0000A4E1
 EQUD &0000A222 : EQUD &00009F79 : EQUD &00009CE7 : EQUD &00009A69

 EQUD &FFFFFFFF : EQUD &00270000 : EQUD &00138000 : EQUD &000D0000    \ n / 39 [n = 0 to 63]
 EQUD &0009C000 : EQUD &0007CCCC : EQUD &00068000 : EQUD &00059249
 EQUD &0004E000 : EQUD &00045555 : EQUD &0003E666 : EQUD &00038BA2
 EQUD &00034000 : EQUD &00030000 : EQUD &0002C924 : EQUD &00029999
 EQUD &00027000 : EQUD &00024B4B : EQUD &00022AAA : EQUD &00020D79
 EQUD &0001F333 : EQUD &0001DB6D : EQUD &0001C5D1 : EQUD &0001B216
 EQUD &0001A000 : EQUD &00018F5C : EQUD &00018000 : EQUD &000171C7
 EQUD &00016492 : EQUD &00015846 : EQUD &00014CCC : EQUD &00014210
 EQUD &00013800 : EQUD &00012E8B : EQUD &000125A5 : EQUD &00011D41
 EQUD &00011555 : EQUD &00010DD6 : EQUD &000106BC : EQUD &00010000
 EQUD &0000F999 : EQUD &0000F383 : EQUD &0000EDB6 : EQUD &0000E82F
 EQUD &0000E2E8 : EQUD &0000DDDD : EQUD &0000D90B : EQUD &0000D46C
 EQUD &0000D000 : EQUD &0000CBC1 : EQUD &0000C7AE : EQUD &0000C3C3
 EQUD &0000C000 : EQUD &0000BC60 : EQUD &0000B8E3 : EQUD &0000B586
 EQUD &0000B249 : EQUD &0000AF28 : EQUD &0000AC23 : EQUD &0000A938
 EQUD &0000A666 : EQUD &0000A3AC : EQUD &0000A108 : EQUD &00009E79

 EQUD &FFFFFFFF : EQUD &00280000 : EQUD &00140000 : EQUD &000D5555    \ n / 40 [n = 0 to 63]
 EQUD &000A0000 : EQUD &00080000 : EQUD &0006AAAA : EQUD &0005B6DB
 EQUD &00050000 : EQUD &000471C7 : EQUD &00040000 : EQUD &0003A2E8
 EQUD &00035555 : EQUD &000313B1 : EQUD &0002DB6D : EQUD &0002AAAA
 EQUD &00028000 : EQUD &00025A5A : EQUD &000238E3 : EQUD &00021AF2
 EQUD &00020000 : EQUD &0001E79E : EQUD &0001D174 : EQUD &0001BD37
 EQUD &0001AAAA : EQUD &00019999 : EQUD &000189D8 : EQUD &00017B42
 EQUD &00016DB6 : EQUD &0001611A : EQUD &00015555 : EQUD &00014A52
 EQUD &00014000 : EQUD &0001364D : EQUD &00012D2D : EQUD &00012492
 EQUD &00011C71 : EQUD &000114C1 : EQUD &00010D79 : EQUD &00010690
 EQUD &00010000 : EQUD &0000F9C1 : EQUD &0000F3CF : EQUD &0000EE23
 EQUD &0000E8BA : EQUD &0000E38E : EQUD &0000DE9B : EQUD &0000D9DF
 EQUD &0000D555 : EQUD &0000D0FA : EQUD &0000CCCC : EQUD &0000C8C8
 EQUD &0000C4EC : EQUD &0000C135 : EQUD &0000BDA1 : EQUD &0000BA2E
 EQUD &0000B6DB : EQUD &0000B3A6 : EQUD &0000B08D : EQUD &0000AD8F
 EQUD &0000AAAA : EQUD &0000A7DE : EQUD &0000A529 : EQUD &0000A28A

 EQUD &FFFFFFFF : EQUD &00290000 : EQUD &00148000 : EQUD &000DAAAA    \ n / 41 [n = 0 to 63]
 EQUD &000A4000 : EQUD &00083333 : EQUD &0006D555 : EQUD &0005DB6D
 EQUD &00052000 : EQUD &00048E38 : EQUD &00041999 : EQUD &0003BA2E
 EQUD &00036AAA : EQUD &00032762 : EQUD &0002EDB6 : EQUD &0002BBBB
 EQUD &00029000 : EQUD &00026969 : EQUD &0002471C : EQUD &0002286B
 EQUD &00020CCC : EQUD &0001F3CF : EQUD &0001DD17 : EQUD &0001C859
 EQUD &0001B555 : EQUD &0001A3D7 : EQUD &000193B1 : EQUD &000184BD
 EQUD &000176DB : EQUD &000169EE : EQUD &00015DDD : EQUD &00015294
 EQUD &00014800 : EQUD &00013E0F : EQUD &000134B4 : EQUD &00012BE2
 EQUD &0001238E : EQUD &00011BAC : EQUD &00011435 : EQUD &00010D20
 EQUD &00010666 : EQUD &00010000 : EQUD &0000F9E7 : EQUD &0000F417
 EQUD &0000EE8B : EQUD &0000E93E : EQUD &0000E42C : EQUD &0000DF51
 EQUD &0000DAAA : EQUD &0000D634 : EQUD &0000D1EB : EQUD &0000CDCD
 EQUD &0000C9D8 : EQUD &0000C609 : EQUD &0000C25E : EQUD &0000BED6
 EQUD &0000BB6D : EQUD &0000B823 : EQUD &0000B4F7 : EQUD &0000B1E5
 EQUD &0000AEEE : EQUD &0000AC10 : EQUD &0000A94A : EQUD &0000A69A

 EQUD &FFFFFFFF : EQUD &002A0000 : EQUD &00150000 : EQUD &000E0000    \ n / 42 [n = 0 to 63]
 EQUD &000A8000 : EQUD &00086666 : EQUD &00070000 : EQUD &00060000
 EQUD &00054000 : EQUD &0004AAAA : EQUD &00043333 : EQUD &0003D174
 EQUD &00038000 : EQUD &00033B13 : EQUD &00030000 : EQUD &0002CCCC
 EQUD &0002A000 : EQUD &00027878 : EQUD &00025555 : EQUD &000235E5
 EQUD &00021999 : EQUD &00020000 : EQUD &0001E8BA : EQUD &0001D37A
 EQUD &0001C000 : EQUD &0001AE14 : EQUD &00019D89 : EQUD &00018E38
 EQUD &00018000 : EQUD &000172C2 : EQUD &00016666 : EQUD &00015AD6
 EQUD &00015000 : EQUD &000145D1 : EQUD &00013C3C : EQUD &00013333
 EQUD &00012AAA : EQUD &00012298 : EQUD &00011AF2 : EQUD &000113B1
 EQUD &00010CCC : EQUD &0001063E : EQUD &00010000 : EQUD &0000FA0B
 EQUD &0000F45D : EQUD &0000EEEE : EQUD &0000E9BD : EQUD &0000E4C4
 EQUD &0000E000 : EQUD &0000DB6D : EQUD &0000D70A : EQUD &0000D2D2
 EQUD &0000CEC4 : EQUD &0000CADE : EQUD &0000C71C : EQUD &0000C37D
 EQUD &0000C000 : EQUD &0000BCA1 : EQUD &0000B961 : EQUD &0000B63C
 EQUD &0000B333 : EQUD &0000B043 : EQUD &0000AD6B : EQUD &0000AAAA

 EQUD &FFFFFFFF : EQUD &002B0000 : EQUD &00158000 : EQUD &000E5555    \ n / 43 [n = 0 to 63]
 EQUD &000AC000 : EQUD &00089999 : EQUD &00072AAA : EQUD &00062492
 EQUD &00056000 : EQUD &0004C71C : EQUD &00044CCC : EQUD &0003E8BA
 EQUD &00039555 : EQUD &00034EC4 : EQUD &00031249 : EQUD &0002DDDD
 EQUD &0002B000 : EQUD &00028787 : EQUD &0002638E : EQUD &0002435E
 EQUD &00022666 : EQUD &00020C30 : EQUD &0001F45D : EQUD &0001DE9B
 EQUD &0001CAAA : EQUD &0001B851 : EQUD &0001A762 : EQUD &000197B4
 EQUD &00018924 : EQUD &00017B96 : EQUD &00016EEE : EQUD &00016318
 EQUD &00015800 : EQUD &00014D93 : EQUD &000143C3 : EQUD &00013A83
 EQUD &000131C7 : EQUD &00012983 : EQUD &000121AF : EQUD &00011A41
 EQUD &00011333 : EQUD &00010C7C : EQUD &00010618 : EQUD &00010000
 EQUD &0000FA2E : EQUD &0000F49F : EQUD &0000EF4D : EQUD &0000EA36
 EQUD &0000E555 : EQUD &0000E0A7 : EQUD &0000DC28 : EQUD &0000D7D7
 EQUD &0000D3B1 : EQUD &0000CFB2 : EQUD &0000CBDA : EQUD &0000C825
 EQUD &0000C492 : EQUD &0000C11F : EQUD &0000BDCB : EQUD &0000BA93
 EQUD &0000B777 : EQUD &0000B475 : EQUD &0000B18C : EQUD &0000AEBA

 EQUD &FFFFFFFF : EQUD &002C0000 : EQUD &00160000 : EQUD &000EAAAA    \ n / 44 [n = 0 to 63]
 EQUD &000B0000 : EQUD &0008CCCC : EQUD &00075555 : EQUD &00064924
 EQUD &00058000 : EQUD &0004E38E : EQUD &00046666 : EQUD &00040000
 EQUD &0003AAAA : EQUD &00036276 : EQUD &00032492 : EQUD &0002EEEE
 EQUD &0002C000 : EQUD &00029696 : EQUD &000271C7 : EQUD &000250D7
 EQUD &00023333 : EQUD &00021861 : EQUD &00020000 : EQUD &0001E9BD
 EQUD &0001D555 : EQUD &0001C28F : EQUD &0001B13B : EQUD &0001A12F
 EQUD &00019249 : EQUD &00018469 : EQUD &00017777 : EQUD &00016B5A
 EQUD &00016000 : EQUD &00015555 : EQUD &00014B4B : EQUD &000141D4
 EQUD &000138E3 : EQUD &0001306E : EQUD &0001286B : EQUD &000120D2
 EQUD &00011999 : EQUD &000112BB : EQUD &00010C30 : EQUD &000105F4
 EQUD &00010000 : EQUD &0000FA4F : EQUD &0000F4DE : EQUD &0000EFA8
 EQUD &0000EAAA : EQUD &0000E5E0 : EQUD &0000E147 : EQUD &0000DCDC
 EQUD &0000D89D : EQUD &0000D487 : EQUD &0000D097 : EQUD &0000CCCC
 EQUD &0000C924 : EQUD &0000C59D : EQUD &0000C234 : EQUD &0000BEEA
 EQUD &0000BBBB : EQUD &0000B8A7 : EQUD &0000B5AD : EQUD &0000B2CB

 EQUD &FFFFFFFF : EQUD &002D0000 : EQUD &00168000 : EQUD &000F0000    \ n / 45 [n = 0 to 63]
 EQUD &000B4000 : EQUD &00090000 : EQUD &00078000 : EQUD &00066DB6
 EQUD &0005A000 : EQUD &00050000 : EQUD &00048000 : EQUD &00041745
 EQUD &0003C000 : EQUD &00037627 : EQUD &000336DB : EQUD &00030000
 EQUD &0002D000 : EQUD &0002A5A5 : EQUD &00028000 : EQUD &00025E50
 EQUD &00024000 : EQUD &00022492 : EQUD &00020BA2 : EQUD &0001F4DE
 EQUD &0001E000 : EQUD &0001CCCC : EQUD &0001BB13 : EQUD &0001AAAA
 EQUD &00019B6D : EQUD &00018D3D : EQUD &00018000 : EQUD &0001739C
 EQUD &00016800 : EQUD &00015D17 : EQUD &000152D2 : EQUD &00014924
 EQUD &00014000 : EQUD &00013759 : EQUD &00012F28 : EQUD &00012762
 EQUD &00012000 : EQUD &000118F9 : EQUD &00011249 : EQUD &00010BE8
 EQUD &000105D1 : EQUD &00010000 : EQUD &0000FA6F : EQUD &0000F51B
 EQUD &0000F000 : EQUD &0000EB1A : EQUD &0000E666 : EQUD &0000E1E1
 EQUD &0000DD89 : EQUD &0000D95B : EQUD &0000D555 : EQUD &0000D174
 EQUD &0000CDB6 : EQUD &0000CA1A : EQUD &0000C69E : EQUD &0000C341
 EQUD &0000C000 : EQUD &0000BCDA : EQUD &0000B9CE : EQUD &0000B6DB

 EQUD &FFFFFFFF : EQUD &002E0000 : EQUD &00170000 : EQUD &000F5555    \ n / 46 [n = 0 to 63]
 EQUD &000B8000 : EQUD &00093333 : EQUD &0007AAAA : EQUD &00069249
 EQUD &0005C000 : EQUD &00051C71 : EQUD &00049999 : EQUD &00042E8B
 EQUD &0003D555 : EQUD &000389D8 : EQUD &00034924 : EQUD &00031111
 EQUD &0002E000 : EQUD &0002B4B4 : EQUD &00028E38 : EQUD &00026BCA
 EQUD &00024CCC : EQUD &000230C3 : EQUD &00021745 : EQUD &00020000
 EQUD &0001EAAA : EQUD &0001D70A : EQUD &0001C4EC : EQUD &0001B425
 EQUD &0001A492 : EQUD &00019611 : EQUD &00018888 : EQUD &00017BDE
 EQUD &00017000 : EQUD &000164D9 : EQUD &00015A5A : EQUD &00015075
 EQUD &0001471C : EQUD &00013E45 : EQUD &000135E5 : EQUD &00012DF2
 EQUD &00012666 : EQUD &00011F38 : EQUD &00011861 : EQUD &000111DC
 EQUD &00010BA2 : EQUD &000105B0 : EQUD &00010000 : EQUD &0000FA8D
 EQUD &0000F555 : EQUD &0000F053 : EQUD &0000EB85 : EQUD &0000E6E6
 EQUD &0000E276 : EQUD &0000DE30 : EQUD &0000DA12 : EQUD &0000D61B
 EQUD &0000D249 : EQUD &0000CE98 : EQUD &0000CB08 : EQUD &0000C797
 EQUD &0000C444 : EQUD &0000C10C : EQUD &0000BDEF : EQUD &0000BAEB

 EQUD &FFFFFFFF : EQUD &002F0000 : EQUD &00178000 : EQUD &000FAAAA    \ n / 47 [n = 0 to 63]
 EQUD &000BC000 : EQUD &00096666 : EQUD &0007D555 : EQUD &0006B6DB
 EQUD &0005E000 : EQUD &000538E3 : EQUD &0004B333 : EQUD &000445D1
 EQUD &0003EAAA : EQUD &00039D89 : EQUD &00035B6D : EQUD &00032222
 EQUD &0002F000 : EQUD &0002C3C3 : EQUD &00029C71 : EQUD &00027943
 EQUD &00025999 : EQUD &00023CF3 : EQUD &000222E8 : EQUD &00020B21
 EQUD &0001F555 : EQUD &0001E147 : EQUD &0001CEC4 : EQUD &0001BDA1
 EQUD &0001ADB6 : EQUD &00019EE5 : EQUD &00019111 : EQUD &00018421
 EQUD &00017800 : EQUD &00016C9B : EQUD &000161E1 : EQUD &000157C5
 EQUD &00014E38 : EQUD &00014530 : EQUD &00013CA1 : EQUD &00013483
 EQUD &00012CCC : EQUD &00012576 : EQUD &00011E79 : EQUD &000117D0
 EQUD &00011174 : EQUD &00010B60 : EQUD &00010590 : EQUD &00010000
 EQUD &0000FAAA : EQUD &0000F58D : EQUD &0000F0A3 : EQUD &0000EBEB
 EQUD &0000E762 : EQUD &0000E304 : EQUD &0000DED0 : EQUD &0000DAC3
 EQUD &0000D6DB : EQUD &0000D316 : EQUD &0000CF72 : EQUD &0000CBEE
 EQUD &0000C888 : EQUD &0000C53E : EQUD &0000C210 : EQUD &0000BEFB

 EQUD &FFFFFFFF : EQUD &00300000 : EQUD &00180000 : EQUD &00100000    \ n / 48 [n = 0 to 63]
 EQUD &000C0000 : EQUD &00099999 : EQUD &00080000 : EQUD &0006DB6D
 EQUD &00060000 : EQUD &00055555 : EQUD &0004CCCC : EQUD &00045D17
 EQUD &00040000 : EQUD &0003B13B : EQUD &00036DB6 : EQUD &00033333
 EQUD &00030000 : EQUD &0002D2D2 : EQUD &0002AAAA : EQUD &000286BC
 EQUD &00026666 : EQUD &00024924 : EQUD &00022E8B : EQUD &00021642
 EQUD &00020000 : EQUD &0001EB85 : EQUD &0001D89D : EQUD &0001C71C
 EQUD &0001B6DB : EQUD &0001A7B9 : EQUD &00019999 : EQUD &00018C63
 EQUD &00018000 : EQUD &0001745D : EQUD &00016969 : EQUD &00015F15
 EQUD &00015555 : EQUD &00014C1B : EQUD &0001435E : EQUD &00013B13
 EQUD &00013333 : EQUD &00012BB5 : EQUD &00012492 : EQUD &00011DC4
 EQUD &00011745 : EQUD &00011111 : EQUD &00010B21 : EQUD &00010572
 EQUD &00010000 : EQUD &0000FAC6 : EQUD &0000F5C2 : EQUD &0000F0F0
 EQUD &0000EC4E : EQUD &0000E7D9 : EQUD &0000E38E : EQUD &0000DF6B
 EQUD &0000DB6D : EQUD &0000D794 : EQUD &0000D3DC : EQUD &0000D045
 EQUD &0000CCCC : EQUD &0000C971 : EQUD &0000C631 : EQUD &0000C30C

 EQUD &FFFFFFFF : EQUD &00310000 : EQUD &00188000 : EQUD &00105555    \ n / 49 [n = 0 to 63]
 EQUD &000C4000 : EQUD &0009CCCC : EQUD &00082AAA : EQUD &00070000
 EQUD &00062000 : EQUD &000571C7 : EQUD &0004E666 : EQUD &0004745D
 EQUD &00041555 : EQUD &0003C4EC : EQUD &00038000 : EQUD &00034444
 EQUD &00031000 : EQUD &0002E1E1 : EQUD &0002B8E3 : EQUD &00029435
 EQUD &00027333 : EQUD &00025555 : EQUD &00023A2E : EQUD &00022164
 EQUD &00020AAA : EQUD &0001F5C2 : EQUD &0001E276 : EQUD &0001D097
 EQUD &0001C000 : EQUD &0001B08D : EQUD &0001A222 : EQUD &000194A5
 EQUD &00018800 : EQUD &00017C1F : EQUD &000170F0 : EQUD &00016666
 EQUD &00015C71 : EQUD &00015306 : EQUD &00014A1A : EQUD &000141A4
 EQUD &00013999 : EQUD &000131F3 : EQUD &00012AAA : EQUD &000123B8
 EQUD &00011D17 : EQUD &000116C1 : EQUD &000110B2 : EQUD &00010AE4
 EQUD &00010555 : EQUD &00010000 : EQUD &0000FAE1 : EQUD &0000F5F5
 EQUD &0000F13B : EQUD &0000ECAD : EQUD &0000E84B : EQUD &0000E412
 EQUD &0000E000 : EQUD &0000DC11 : EQUD &0000D846 : EQUD &0000D49C
 EQUD &0000D111 : EQUD &0000CDA3 : EQUD &0000CA52 : EQUD &0000C71C

 EQUD &FFFFFFFF : EQUD &00320000 : EQUD &00190000 : EQUD &0010AAAA    \ n / 50 [n = 0 to 63]
 EQUD &000C8000 : EQUD &000A0000 : EQUD &00085555 : EQUD &00072492
 EQUD &00064000 : EQUD &00058E38 : EQUD &00050000 : EQUD &00048BA2
 EQUD &00042AAA : EQUD &0003D89D : EQUD &00039249 : EQUD &00035555
 EQUD &00032000 : EQUD &0002F0F0 : EQUD &0002C71C : EQUD &0002A1AF
 EQUD &00028000 : EQUD &00026186 : EQUD &000245D1 : EQUD &00022C85
 EQUD &00021555 : EQUD &00020000 : EQUD &0001EC4E : EQUD &0001DA12
 EQUD &0001C924 : EQUD &0001B961 : EQUD &0001AAAA : EQUD &00019CE7
 EQUD &00019000 : EQUD &000183E0 : EQUD &00017878 : EQUD &00016DB6
 EQUD &0001638E : EQUD &000159F2 : EQUD &000150D7 : EQUD &00014834
 EQUD &00014000 : EQUD &00013831 : EQUD &000130C3 : EQUD &000129AC
 EQUD &000122E8 : EQUD &00011C71 : EQUD &00011642 : EQUD &00011057
 EQUD &00010AAA : EQUD &00010539 : EQUD &00010000 : EQUD &0000FAFA
 EQUD &0000F627 : EQUD &0000F182 : EQUD &0000ED09 : EQUD &0000E8BA
 EQUD &0000E492 : EQUD &0000E08F : EQUD &0000DCB0 : EQUD &0000D8F2
 EQUD &0000D555 : EQUD &0000D1D6 : EQUD &0000CE73 : EQUD &0000CB2C

 EQUD &FFFFFFFF : EQUD &00330000 : EQUD &00198000 : EQUD &00110000    \ n / 51 [n = 0 to 63]
 EQUD &000CC000 : EQUD &000A3333 : EQUD &00088000 : EQUD &00074924
 EQUD &00066000 : EQUD &0005AAAA : EQUD &00051999 : EQUD &0004A2E8
 EQUD &00044000 : EQUD &0003EC4E : EQUD &0003A492 : EQUD &00036666
 EQUD &00033000 : EQUD &00030000 : EQUD &0002D555 : EQUD &0002AF28
 EQUD &00028CCC : EQUD &00026DB6 : EQUD &00025174 : EQUD &000237A6
 EQUD &00022000 : EQUD &00020A3D : EQUD &0001F627 : EQUD &0001E38E
 EQUD &0001D249 : EQUD &0001C234 : EQUD &0001B333 : EQUD &0001A529
 EQUD &00019800 : EQUD &00018BA2 : EQUD &00018000 : EQUD &00017507
 EQUD &00016AAA : EQUD &000160DD : EQUD &00015794 : EQUD &00014EC4
 EQUD &00014666 : EQUD &00013E70 : EQUD &000136DB : EQUD &00012FA0
 EQUD &000128BA : EQUD &00012222 : EQUD &00011BD3 : EQUD &000115C9
 EQUD &00011000 : EQUD &00010A72 : EQUD &0001051E : EQUD &00010000
 EQUD &0000FB13 : EQUD &0000F656 : EQUD &0000F1C7 : EQUD &0000ED61
 EQUD &0000E924 : EQUD &0000E50D : EQUD &0000E11A : EQUD &0000DD49
 EQUD &0000D999 : EQUD &0000D608 : EQUD &0000D294 : EQUD &0000CF3C

 EQUD &FFFFFFFF : EQUD &00340000 : EQUD &001A0000 : EQUD &00115555    \ n / 52 [n = 0 to 63]
 EQUD &000D0000 : EQUD &000A6666 : EQUD &0008AAAA : EQUD &00076DB6
 EQUD &00068000 : EQUD &0005C71C : EQUD &00053333 : EQUD &0004BA2E
 EQUD &00045555 : EQUD &00040000 : EQUD &0003B6DB : EQUD &00037777
 EQUD &00034000 : EQUD &00030F0F : EQUD &0002E38E : EQUD &0002BCA1
 EQUD &00029999 : EQUD &000279E7 : EQUD &00025D17 : EQUD &000242C8
 EQUD &00022AAA : EQUD &0002147A : EQUD &00020000 : EQUD &0001ED09
 EQUD &0001DB6D : EQUD &0001CB08 : EQUD &0001BBBB : EQUD &0001AD6B
 EQUD &0001A000 : EQUD &00019364 : EQUD &00018787 : EQUD &00017C57
 EQUD &000171C7 : EQUD &000167C8 : EQUD &00015E50 : EQUD &00015555
 EQUD &00014CCC : EQUD &000144AE : EQUD &00013CF3 : EQUD &00013594
 EQUD &00012E8B : EQUD &000127D2 : EQUD &00012164 : EQUD &00011B3B
 EQUD &00011555 : EQUD &00010FAC : EQUD &00010A3D : EQUD &00010505
 EQUD &00010000 : EQUD &0000FB2B : EQUD &0000F684 : EQUD &0000F209
 EQUD &0000EDB6 : EQUD &0000E98B : EQUD &0000E584 : EQUD &0000E1A0
 EQUD &0000DDDD : EQUD &0000DA3A : EQUD &0000D6B5 : EQUD &0000D34D

 EQUD &FFFFFFFF : EQUD &00350000 : EQUD &001A8000 : EQUD &0011AAAA    \ n / 53 [n = 0 to 63]
 EQUD &000D4000 : EQUD &000A9999 : EQUD &0008D555 : EQUD &00079249
 EQUD &0006A000 : EQUD &0005E38E : EQUD &00054CCC : EQUD &0004D174
 EQUD &00046AAA : EQUD &000413B1 : EQUD &0003C924 : EQUD &00038888
 EQUD &00035000 : EQUD &00031E1E : EQUD &0002F1C7 : EQUD &0002CA1A
 EQUD &0002A666 : EQUD &00028618 : EQUD &000268BA : EQUD &00024DE9
 EQUD &00023555 : EQUD &00021EB8 : EQUD &000209D8 : EQUD &0001F684
 EQUD &0001E492 : EQUD &0001D3DC : EQUD &0001C444 : EQUD &0001B5AD
 EQUD &0001A800 : EQUD &00019B26 : EQUD &00018F0F : EQUD &000183A8
 EQUD &000178E3 : EQUD &00016EB3 : EQUD &0001650D : EQUD &00015BE5
 EQUD &00015333 : EQUD &00014AED : EQUD &0001430C : EQUD &00013B88
 EQUD &0001345D : EQUD &00012D82 : EQUD &000126F4 : EQUD &000120AE
 EQUD &00011AAA : EQUD &000114E5 : EQUD &00010F5C : EQUD &00010A0A
 EQUD &000104EC : EQUD &00010000 : EQUD &0000FB42 : EQUD &0000F6B0
 EQUD &0000F249 : EQUD &0000EE08 : EQUD &0000E9EE : EQUD &0000E5F7
 EQUD &0000E222 : EQUD &0000DE6D : EQUD &0000DAD6 : EQUD &0000D75D

 EQUD &FFFFFFFF : EQUD &00360000 : EQUD &001B0000 : EQUD &00120000    \ n / 54 [n = 0 to 63]
 EQUD &000D8000 : EQUD &000ACCCC : EQUD &00090000 : EQUD &0007B6DB
 EQUD &0006C000 : EQUD &00060000 : EQUD &00056666 : EQUD &0004E8BA
 EQUD &00048000 : EQUD &00042762 : EQUD &0003DB6D : EQUD &00039999
 EQUD &00036000 : EQUD &00032D2D : EQUD &00030000 : EQUD &0002D794
 EQUD &0002B333 : EQUD &00029249 : EQUD &0002745D : EQUD &0002590B
 EQUD &00024000 : EQUD &000228F5 : EQUD &000213B1 : EQUD &00020000
 EQUD &0001EDB6 : EQUD &0001DCB0 : EQUD &0001CCCC : EQUD &0001BDEF
 EQUD &0001B000 : EQUD &0001A2E8 : EQUD &00019696 : EQUD &00018AF8
 EQUD &00018000 : EQUD &0001759F : EQUD &00016BCA : EQUD &00016276
 EQUD &00015999 : EQUD &0001512B : EQUD &00014924 : EQUD &0001417D
 EQUD &00013A2E : EQUD &00013333 : EQUD &00012C85 : EQUD &00012620
 EQUD &00012000 : EQUD &00011A1F : EQUD &0001147A : EQUD &00010F0F
 EQUD &000109D8 : EQUD &000104D4 : EQUD &00010000 : EQUD &0000FB58
 EQUD &0000F6DB : EQUD &0000F286 : EQUD &0000EE58 : EQUD &0000EA4E
 EQUD &0000E666 : EQUD &0000E29F : EQUD &0000DEF7 : EQUD &0000DB6D

 EQUD &FFFFFFFF : EQUD &00370000 : EQUD &001B8000 : EQUD &00125555    \ n / 55 [n = 0 to 63]
 EQUD &000DC000 : EQUD &000B0000 : EQUD &00092AAA : EQUD &0007DB6D
 EQUD &0006E000 : EQUD &00061C71 : EQUD &00058000 : EQUD &00050000
 EQUD &00049555 : EQUD &00043B13 : EQUD &0003EDB6 : EQUD &0003AAAA
 EQUD &00037000 : EQUD &00033C3C : EQUD &00030E38 : EQUD &0002E50D
 EQUD &0002C000 : EQUD &00029E79 : EQUD &00028000 : EQUD &0002642C
 EQUD &00024AAA : EQUD &00023333 : EQUD &00021D89 : EQUD &0002097B
 EQUD &0001F6DB : EQUD &0001E584 : EQUD &0001D555 : EQUD &0001C631
 EQUD &0001B800 : EQUD &0001AAAA : EQUD &00019E1E : EQUD &00019249
 EQUD &0001871C : EQUD &00017C8A : EQUD &00017286 : EQUD &00016906
 EQUD &00016000 : EQUD &0001576A : EQUD &00014F3C : EQUD &00014771
 EQUD &00014000 : EQUD &000138E3 : EQUD &00013216 : EQUD &00012B93
 EQUD &00012555 : EQUD &00011F58 : EQUD &00011999 : EQUD &00011414
 EQUD &00010EC4 : EQUD &000109A9 : EQUD &000104BD : EQUD &00010000
 EQUD &0000FB6D : EQUD &0000F704 : EQUD &0000F2C2 : EQUD &0000EEA4
 EQUD &0000EAAA : EQUD &0000E6D1 : EQUD &0000E318 : EQUD &0000DF7D

 EQUD &FFFFFFFF : EQUD &00380000 : EQUD &001C0000 : EQUD &0012AAAA    \ n / 56 [n = 0 to 63]
 EQUD &000E0000 : EQUD &000B3333 : EQUD &00095555 : EQUD &00080000
 EQUD &00070000 : EQUD &000638E3 : EQUD &00059999 : EQUD &00051745
 EQUD &0004AAAA : EQUD &00044EC4 : EQUD &00040000 : EQUD &0003BBBB
 EQUD &00038000 : EQUD &00034B4B : EQUD &00031C71 : EQUD &0002F286
 EQUD &0002CCCC : EQUD &0002AAAA : EQUD &00028BA2 : EQUD &00026F4D
 EQUD &00025555 : EQUD &00023D70 : EQUD &00022762 : EQUD &000212F6
 EQUD &00020000 : EQUD &0001EE58 : EQUD &0001DDDD : EQUD &0001CE73
 EQUD &0001C000 : EQUD &0001B26C : EQUD &0001A5A5 : EQUD &00019999
 EQUD &00018E38 : EQUD &00018375 : EQUD &00017943 : EQUD &00016F96
 EQUD &00016666 : EQUD &00015DA8 : EQUD &00015555 : EQUD &00014D65
 EQUD &000145D1 : EQUD &00013E93 : EQUD &000137A6 : EQUD &00013105
 EQUD &00012AAA : EQUD &00012492 : EQUD &00011EB8 : EQUD &00011919
 EQUD &000113B1 : EQUD &00010E7D : EQUD &0001097B : EQUD &000104A7
 EQUD &00010000 : EQUD &0000FB82 : EQUD &0000F72C : EQUD &0000F2FB
 EQUD &0000EEEE : EQUD &0000EB04 : EQUD &0000E739 : EQUD &0000E38E

 EQUD &FFFFFFFF : EQUD &00390000 : EQUD &001C8000 : EQUD &00130000    \ n / 57 [n = 0 to 63]
 EQUD &000E4000 : EQUD &000B6666 : EQUD &00098000 : EQUD &00082492
 EQUD &00072000 : EQUD &00065555 : EQUD &0005B333 : EQUD &00052E8B
 EQUD &0004C000 : EQUD &00046276 : EQUD &00041249 : EQUD &0003CCCC
 EQUD &00039000 : EQUD &00035A5A : EQUD &00032AAA : EQUD &00030000
 EQUD &0002D999 : EQUD &0002B6DB : EQUD &00029745 : EQUD &00027A6F
 EQUD &00026000 : EQUD &000247AE : EQUD &0002313B : EQUD &00021C71
 EQUD &00020924 : EQUD &0001F72C : EQUD &0001E666 : EQUD &0001D6B5
 EQUD &0001C800 : EQUD &0001BA2E : EQUD &0001AD2D : EQUD &0001A0EA
 EQUD &00019555 : EQUD &00018A60 : EQUD &00018000 : EQUD &00017627
 EQUD &00016CCC : EQUD &000163E7 : EQUD &00015B6D : EQUD &00015359
 EQUD &00014BA2 : EQUD &00014444 : EQUD &00013D37 : EQUD &00013677
 EQUD &00013000 : EQUD &000129CB : EQUD &000123D7 : EQUD &00011E1E
 EQUD &0001189D : EQUD &00011352 : EQUD &00010E38 : EQUD &0001094F
 EQUD &00010492 : EQUD &00010000 : EQUD &0000FB96 : EQUD &0000F752
 EQUD &0000F333 : EQUD &0000EF36 : EQUD &0000EB5A : EQUD &0000E79E

 EQUD &FFFFFFFF : EQUD &003A0000 : EQUD &001D0000 : EQUD &00135555    \ n / 58 [n = 0 to 63]
 EQUD &000E8000 : EQUD &000B9999 : EQUD &0009AAAA : EQUD &00084924
 EQUD &00074000 : EQUD &000671C7 : EQUD &0005CCCC : EQUD &000545D1
 EQUD &0004D555 : EQUD &00047627 : EQUD &00042492 : EQUD &0003DDDD
 EQUD &0003A000 : EQUD &00036969 : EQUD &000338E3 : EQUD &00030D79
 EQUD &0002E666 : EQUD &0002C30C : EQUD &0002A2E8 : EQUD &00028590
 EQUD &00026AAA : EQUD &000251EB : EQUD &00023B13 : EQUD &000225ED
 EQUD &00021249 : EQUD &00020000 : EQUD &0001EEEE : EQUD &0001DEF7
 EQUD &0001D000 : EQUD &0001C1F0 : EQUD &0001B4B4 : EQUD &0001A83A
 EQUD &00019C71 : EQUD &0001914C : EQUD &000186BC : EQUD &00017CB7
 EQUD &00017333 : EQUD &00016A25 : EQUD &00016186 : EQUD &0001594D
 EQUD &00015174 : EQUD &000149F4 : EQUD &000142C8 : EQUD &00013BEA
 EQUD &00013555 : EQUD &00012F05 : EQUD &000128F5 : EQUD &00012323
 EQUD &00011D89 : EQUD &00011826 : EQUD &000112F6 : EQUD &00010DF6
 EQUD &00010924 : EQUD &0001047D : EQUD &00010000 : EQUD &0000FBA9
 EQUD &0000F777 : EQUD &0000F368 : EQUD &0000EF7B : EQUD &0000EBAE

 EQUD &FFFFFFFF : EQUD &003B0000 : EQUD &001D8000 : EQUD &0013AAAA    \ n / 59 [n = 0 to 63]
 EQUD &000EC000 : EQUD &000BCCCC : EQUD &0009D555 : EQUD &00086DB6
 EQUD &00076000 : EQUD &00068E38 : EQUD &0005E666 : EQUD &00055D17
 EQUD &0004EAAA : EQUD &000489D8 : EQUD &000436DB : EQUD &0003EEEE
 EQUD &0003B000 : EQUD &00037878 : EQUD &0003471C : EQUD &00031AF2
 EQUD &0002F333 : EQUD &0002CF3C : EQUD &0002AE8B : EQUD &000290B2
 EQUD &00027555 : EQUD &00025C28 : EQUD &000244EC : EQUD &00022F68
 EQUD &00021B6D : EQUD &000208D3 : EQUD &0001F777 : EQUD &0001E739
 EQUD &0001D800 : EQUD &0001C9B2 : EQUD &0001BC3C : EQUD &0001AF8A
 EQUD &0001A38E : EQUD &00019837 : EQUD &00018D79 : EQUD &00018348
 EQUD &00017999 : EQUD &00017063 : EQUD &0001679E : EQUD &00015F41
 EQUD &00015745 : EQUD &00014FA4 : EQUD &00014859 : EQUD &0001415C
 EQUD &00013AAA : EQUD &0001343E : EQUD &00012E14 : EQUD &00012828
 EQUD &00012276 : EQUD &00011CFB : EQUD &000117B4 : EQUD &0001129E
 EQUD &00010DB6 : EQUD &000108FB : EQUD &00010469 : EQUD &00010000
 EQUD &0000FBBB : EQUD &0000F79B : EQUD &0000F39C : EQUD &0000EFBE

 EQUD &FFFFFFFF : EQUD &003C0000 : EQUD &001E0000 : EQUD &00140000    \ n / 60 [n = 0 to 63]
 EQUD &000F0000 : EQUD &000C0000 : EQUD &000A0000 : EQUD &00089249
 EQUD &00078000 : EQUD &0006AAAA : EQUD &00060000 : EQUD &0005745D
 EQUD &00050000 : EQUD &00049D89 : EQUD &00044924 : EQUD &00040000
 EQUD &0003C000 : EQUD &00038787 : EQUD &00035555 : EQUD &0003286B
 EQUD &00030000 : EQUD &0002DB6D : EQUD &0002BA2E : EQUD &00029BD3
 EQUD &00028000 : EQUD &00026666 : EQUD &00024EC4 : EQUD &000238E3
 EQUD &00022492 : EQUD &000211A7 : EQUD &00020000 : EQUD &0001EF7B
 EQUD &0001E000 : EQUD &0001D174 : EQUD &0001C3C3 : EQUD &0001B6DB
 EQUD &0001AAAA : EQUD &00019F22 : EQUD &00019435 : EQUD &000189D8
 EQUD &00018000 : EQUD &000176A2 : EQUD &00016DB6 : EQUD &00016535
 EQUD &00015D17 : EQUD &00015555 : EQUD &00014DE9 : EQUD &000146CE
 EQUD &00014000 : EQUD &00013978 : EQUD &00013333 : EQUD &00012D2D
 EQUD &00012762 : EQUD &000121CF : EQUD &00011C71 : EQUD &00011745
 EQUD &00011249 : EQUD &00010D79 : EQUD &000108D3 : EQUD &00010456
 EQUD &00010000 : EQUD &0000FBCD : EQUD &0000F7BD : EQUD &0000F3CF

 EQUD &FFFFFFFF : EQUD &003D0000 : EQUD &001E8000 : EQUD &00145555    \ n / 61 [n = 0 to 63]
 EQUD &000F4000 : EQUD &000C3333 : EQUD &000A2AAA : EQUD &0008B6DB
 EQUD &0007A000 : EQUD &0006C71C : EQUD &00061999 : EQUD &00058BA2
 EQUD &00051555 : EQUD &0004B13B : EQUD &00045B6D : EQUD &00041111
 EQUD &0003D000 : EQUD &00039696 : EQUD &0003638E : EQUD &000335E5
 EQUD &00030CCC : EQUD &0002E79E : EQUD &0002C5D1 : EQUD &0002A6F4
 EQUD &00028AAA : EQUD &000270A3 : EQUD &0002589D : EQUD &0002425E
 EQUD &00022DB6 : EQUD &00021A7B : EQUD &00020888 : EQUD &0001F7BD
 EQUD &0001E800 : EQUD &0001D936 : EQUD &0001CB4B : EQUD &0001BE2B
 EQUD &0001B1C7 : EQUD &0001A60D : EQUD &00019AF2 : EQUD &00019069
 EQUD &00018666 : EQUD &00017CE0 : EQUD &000173CF : EQUD &00016B29
 EQUD &000162E8 : EQUD &00015B05 : EQUD &0001537A : EQUD &00014C41
 EQUD &00014555 : EQUD &00013EB1 : EQUD &00013851 : EQUD &00013232
 EQUD &00012C4E : EQUD &000126A4 : EQUD &0001212F : EQUD &00011BED
 EQUD &000116DB : EQUD &000111F7 : EQUD &00010D3D : EQUD &000108AD
 EQUD &00010444 : EQUD &00010000 : EQUD &0000FBDE : EQUD &0000F7DF

 EQUD &FFFFFFFF : EQUD &003E0000 : EQUD &001F0000 : EQUD &0014AAAA    \ n / 62 [n = 0 to 63]
 EQUD &000F8000 : EQUD &000C6666 : EQUD &000A5555 : EQUD &0008DB6D
 EQUD &0007C000 : EQUD &0006E38E : EQUD &00063333 : EQUD &0005A2E8
 EQUD &00052AAA : EQUD &0004C4EC : EQUD &00046DB6 : EQUD &00042222
 EQUD &0003E000 : EQUD &0003A5A5 : EQUD &000371C7 : EQUD &0003435E
 EQUD &00031999 : EQUD &0002F3CF : EQUD &0002D174 : EQUD &0002B216
 EQUD &00029555 : EQUD &00027AE1 : EQUD &00026276 : EQUD &00024BDA
 EQUD &000236DB : EQUD &0002234F : EQUD &00021111 : EQUD &00020000
 EQUD &0001F000 : EQUD &0001E0F8 : EQUD &0001D2D2 : EQUD &0001C57C
 EQUD &0001B8E3 : EQUD &0001ACF9 : EQUD &0001A1AF : EQUD &000196F9
 EQUD &00018CCC : EQUD &0001831F : EQUD &000179E7 : EQUD &0001711D
 EQUD &000168BA : EQUD &000160B6 : EQUD &0001590B : EQUD &000151B3
 EQUD &00014AAA : EQUD &000143EB : EQUD &00013D70 : EQUD &00013737
 EQUD &0001313B : EQUD &00012B78 : EQUD &000125ED : EQUD &00012094
 EQUD &00011B6D : EQUD &00011674 : EQUD &000111A7 : EQUD &00010D04
 EQUD &00010888 : EQUD &00010432 : EQUD &00010000 : EQUD &0000FBEF

 EQUD &FFFFFFFF : EQUD &003F0000 : EQUD &001F8000 : EQUD &00150000    \ n / 63 [n = 0 to 63]
 EQUD &000FC000 : EQUD &000C9999 : EQUD &000A8000 : EQUD &00090000
 EQUD &0007E000 : EQUD &00070000 : EQUD &00064CCC : EQUD &0005BA2E
 EQUD &00054000 : EQUD &0004D89D : EQUD &00048000 : EQUD &00043333
 EQUD &0003F000 : EQUD &0003B4B4 : EQUD &00038000 : EQUD &000350D7
 EQUD &00032666 : EQUD &00030000 : EQUD &0002DD17 : EQUD &0002BD37
 EQUD &0002A000 : EQUD &0002851E : EQUD &00026C4E : EQUD &00025555
 EQUD &00024000 : EQUD &00022C23 : EQUD &00021999 : EQUD &00020842
 EQUD &0001F800 : EQUD &0001E8BA : EQUD &0001DA5A : EQUD &0001CCCC
 EQUD &0001C000 : EQUD &0001B3E4 : EQUD &0001A86B : EQUD &00019D89
 EQUD &00019333 : EQUD &0001895D : EQUD &00018000 : EQUD &00017711
 EQUD &00016E8B : EQUD &00016666 : EQUD &00015E9B : EQUD &00015726
 EQUD &00015000 : EQUD &00014924 : EQUD &0001428F : EQUD &00013C3C
 EQUD &00013627 : EQUD &0001304D : EQUD &00012AAA : EQUD &0001253C
 EQUD &00012000 : EQUD &00011AF2 : EQUD &00011611 : EQUD &0001115B
 EQUD &00010CCC : EQUD &00010864 : EQUD &00010421 : EQUD &00010000

\ ******************************************************************************
\
\ Two-pass assembly loop
\
\ ******************************************************************************

.endCode

 ]

 NEXT pass%                         : REM Loop back for the second pass

REM ******************************************************************************
REM
REM Save GameCode
REM
REM ******************************************************************************

 OSCLI "SAVE GameCode "+STR$~CODE%+" "+STR$~O%+" "+STR$~Entry+" "+STR$~CODE

 END

 DEF FN_AlignWithZeroes
  a = P% AND 3
  IF a > 0 THEN
   FOR I% = 1 TO 4 - a
    [
     OPT pass%
     EQUB 0
    ]
   NEXT I%
  ENDIF
 =pass%