Skip to navigation

Lander on the Acorn Archimedes

Lander C source

Name: ProjectParticleOntoScreen [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Project a 3D particle coordinate onto the screen Deep dive: Particles and particle clouds Projecting onto the screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * MoveAndDrawParticles (Part 4 of 4) calls ProjectParticleOntoScreen

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawLandscapeAndBuffers (Part 2 of 4) calls ProjectVertexOntoScreen * DrawObject (Part 2 of 5) calls ProjectVertexOntoScreen

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 [Show more] 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
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.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 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Add a vector to the rotated vertex coordinates to get the vertex coordinates in 3D space
Context: See this subroutine on its own page References: This subroutine is called as follows: * AddVectors calls AddVectorToVertices * DrawObject (Part 2 of 5) calls AddVectorToVertices

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 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: An unused routine that multiplies a vector by a constant value Deep dive: Unused code in Lander
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

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 [Show more] Type: Variable Category: Maths (Arithmetic) Summary: The first random seed for the random number generator
Context: See this variable on its own page References: This variable is used as follows: * GetRandomNumbers uses randomSeed1
.randomSeed1 EQUD &4F9C3490
Name: randomSeed2 [Show more] Type: Variable Category: Maths (Arithmetic) Summary: The second random seed for the random number generator
Context: See this variable on its own page References: This variable is used as follows: * GetRandomNumbers uses randomSeed2
.randomSeed2 EQUD &DA0383CF
Name: GetRandomNumbers [Show more] 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 [Show more] 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 [Show more] Type: Variable Category: Drawing the screen Summary: An unused table of grey four-pixel colour words Deep dive: Unused code in Lander
Context: See this variable on its own page References: This variable is used as follows: * greyColourWordsAddr uses greyColourWords
.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 [Show more] 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
Context: See this variable on its own page References: No direct references to this variable in this source file
.greyColourWordsAddr EQUD greyColourWords
Name: Draw1x1ParticleFromBuffer [Show more] Type: Subroutine Category: Particles Summary: Process the "draw 1x1-pixel specified" command from the graphics buffer Deep dive: Depth-sorting with the graphics buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls Draw1x1ParticleFromBuffer

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 [Show more] Type: Subroutine Category: Particles Summary: Process the "draw 2x1-pixel particle" command from the graphics buffer Deep dive: Depth-sorting with the graphics buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls Draw2x1ParticleFromBuffer

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 [Show more] Type: Subroutine Category: Particles Summary: Process the "draw 2x2-pixel particle" command from the graphics buffer Deep dive: Depth-sorting with the graphics buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls Draw2x2ParticleFromBuffer

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 [Show more] Type: Subroutine Category: Particles Summary: Process the "draw 3x2-pixel particle" command from the graphics buffer Deep dive: Depth-sorting with the graphics buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls Draw3x2ParticleFromBuffer

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 [Show more] Type: Subroutine Category: Particles Summary: Process the "draw 3x1-pixel particle" command from the graphics buffer Deep dive: Depth-sorting with the graphics buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls Draw3x1ParticleFromBuffer

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * bufferJump calls DrawTriangleFromBuffer

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * MoveAndDrawParticles (Part 4 of 4) calls DrawParticleToBuffer

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * MoveAndDrawParticles (Part 4 of 4) calls DrawParticleShadowToBuffer

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawObject (Part 4 of 5) calls DrawTriangleShadowToBuffer

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawObject (Part 5 of 5) calls DrawTriangleToBuffer

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 [Show more] 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
Context: See this variable on its own page References: No direct references to this variable in this source file
.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 [Show more] 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 \ Set labOffset to the offset back to the \ 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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * LoseLife calls AddTerminatorsToBuffers * MainLoop calls AddTerminatorsToBuffers
.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 [Show more] 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
Context: See this variable on its own page References: This variable is used as follows: * SwitchScreenBank uses screenBank2Addr
.screenBank2Addr EQUD &01FD8000 + 320 * 16
Name: screenBank1Addr [Show more] 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
Context: See this variable on its own page References: This variable is used as follows: * SwitchScreenBank uses screenBank1Addr
.screenBank1Addr EQUD &01FEC000 + 320 * 16
Name: screenBankNumber [Show more] Type: Variable Category: Drawing the screen Summary: The number of the current screen bank (0 or 1) Deep dive: Screen memory in the Archimedes
Context: See this variable on its own page References: This variable is used as follows: * SwitchScreenBank uses screenBankNumber
.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 [Show more] Type: Variable Category: Maths (Arithmetic) Summary: The address of the division lookup table
.divisionTableAddr EQUD divisionTable
Name: SwitchScreenBank [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * Entry calls SwitchScreenBank * LoseLife calls SwitchScreenBank * MainLoop calls SwitchScreenBank
.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 [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a quadrilateral (i.e. two triangles) Deep dive: Drawing the landscape Drawing triangles
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawLandscapeAndBuffers (Part 2 of 4) calls DrawQuadrilateral

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) [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawQuadrilateral calls DrawTriangle * DrawTriangleFromBuffer calls DrawTriangle

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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Calculate the slope of (x1, y1) to (x2, y2) Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Calculate the slope of (x1, y1) to (x3, y3) Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a triangle that isn't clipped and has a sloping first side Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a triangle with a horizontal edge between (x1, y1) and (x2, y2) Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a clipped triangle that's partly off-screen Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.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) [Show more] 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
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw the bottom part of a clipped triangle Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw the top part of a clipped triangle Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ 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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a clipped triangle with a horizontal edge between (x1, y1) and (x2, y2) Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.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) [Show more] Type: Subroutine Category: Drawing triangles Summary: Draw a triangle, clipping it to the screen as we go Deep dive: Drawing triangles
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.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)