Skip to navigation

Lander on the Acorn Archimedes

Landscape: GetLandscapeAltitude

Name: GetLandscapeAltitude [Show more] Type: Subroutine Category: Landscape Summary: Calculate the altitude of the landscape for a given coordinate Deep dive: Generating the landscape Drawing the landscape
This routine calculates the altitude of the landscape at a given coordinate. The altitude is inverse, with lower values indicating higher altitudes. The launchpad is at LAUNCHPAD_ALTITUDE, while the sea is at SEA_LEVEL. The altitude at landscape coordinate (x, z) is calculated as follows: LAND_MID_HEIGHT - ( 2*sin(x - 2z) + 2*sin(4x + 3z) + 2*sin(3z - 5x) + 2*sin(3x + 3z) + sin(5x + 11z) + sin(10x + 7z) ) / 256 Note that the object map is flat, like a paper map, so the x- and z-axes on the map correspond to the x- and z-axes in the three-dimensional space when the map is laid out on the landscape (as the 3D z-axis goes into the screen). When talking about the map, we are talking about (x, z) coordinates that are a bit like longitude and latitude, and this routine returns the y-coordinate of the point on the landscape (as the y-axis goes down the screen and determines the altitude). Note that more negative values denote lower altitudes, as the y-axis goes down the screen, working down from zero at the very highest altitude in space. This is the opposite to conventional altitudes in the real world. The altitude is capped to a maximum value of SEA_LEVEL, and the altitude on the launchpad is set to LAUNCHPAD_ALTITUDE. The launchpad is defined as (x, z) where 0 <= x < LAUNCHPAD_SIZE and 0 <= z < LAUNCHPAD_SIZE, so that's tiles 0 to 7 along each axis (with the origin being at the front-left corner of the launchpad).
Arguments: R8 The x-coordinate of the landscape coordinate R9 The z-coordinate of the landscape coordinate
Returns: R0 The altitude (y-coordinate) of the landscape at these coordinates, with more negative values denoting lower altitudes, as the y-axis points downwards
.GetLandscapeAltitude LDR R0, [R11, #altitude] \ Set prevAltitude = altitude, so we store STR R0, [R11, #prevAltitude] \ the altitude from the previous calculation \ In the following commentary, we will refer \ to the coordinates in R8 and R9 as x and z SUB R0, R8, R9, LSL #1 \ Set R0 = R8 - R9 << 1 \ = x - 2z LDR R2, sinTableAddr \ Set R2 to the address of the sine lookup \ table BIC R0, R0, #&00300000 \ Clear bits 21 and 22 of R0 so when we \ shift R0 to the right by 20 places in the \ following lookup, the address is aligned \ to the words in the lookup table LDR R0, [R2, R0, LSR #20] \ Set \ \ R0 = (2^31 - 1) \ * SIN(2 * PI * ((R0 >> 20) / 1024)) \ \ So R0 = sin(R0) \ = sin(x - 2z) MOV R0, R0, ASR #7 \ Set R0 = R0 >> 7 \ = sin(x - 2z) / 128 ADD R1, R9, R8, LSL #1 \ R1 = R9 + R8 << 1 \ = 2x + z ADD R1, R9, R1, LSL #1 \ R1 = R9 + R1 << 1 \ = z + (2x + z) << 1 \ = z + 4x + 2z \ = 4x + 3z ADD R3, R1, R8 \ R3 = R1 + R8 \ = 4x + 3z + x \ = 5x + 3z \ So the above gives us: \ \ R0 = sin(x - 2z) / 128 \ R1 = 4x + 3z \ R3 = 5x + 3z LDR R2, sinTableAddr \ Using the same approach as above, set: BIC R1, R1, #&00300000 \ LDR R1, [R2, R1, LSR #20] \ R1 = sin(R1) \ = sin(4x + 3z) ADD R0, R0, R1, ASR #7 \ R0 = R0 + R1 >> 7 \ = (sin(x - 2z) + sin(4x + 3z)) / 128 SUB R1, R9, R8, LSL #1 \ R1 = R9 - R8 << 1 \ = z - 2x RSB R1, R8, R1, LSL #1 \ R1 = (R1 << 1) - R8 \ = (z - 2x) << 1 - x \ = 2z - 4x - x \ = 2z - 5x ADD R1, R1, R9 \ R1 = R1 + R9 \ = 2z - 5x + z \ = 3z - 5x \ So the above gives us: \ \ R0 = (sin(x - 2z) + sin(4x + 3z)) / 128 \ R1 = 3z - 5x LDR R2, sinTableAddr \ Using the same approach as above, set: BIC R1, R1, #&00300000 \ LDR R1, [R2, R1, LSR #20] \ R1 = sin(R1) \ = sin(3z - 5x) ADD R0, R0, R1, ASR #7 \ R0 = R0 + R1 >> 7 \ = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x)) / 128 ADD R1, R9, R8, LSL #1 \ R1 = R9 + R8 << 1 \ = z + 2x \ = 2x + z ADD R1, R9, R1, LSL #2 \ R1 = R9 + R1 << 2 \ = z + (2x + z) << 2 \ = z + 4x + 2z \ = 4x + 3z SUB R1, R1, R8 \ R1 = R1 - R8 \ = 4x + 3z - x \ = 3x + 3z \ So the above gives us: \ \ R0 = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x)) / 128 \ R1 = 3x + 3z LDR R2, sinTableAddr \ Using the same approach as above, set: BIC R1, R1, #&00300000 \ LDR R1, [R2, R1, LSR #20] \ R1 = sin(R1) \ = sin(3x + 3z) ADD R0, R0, R1, ASR #7 \ R0 = R0 + R1 >> 7 \ = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x) \ + sin(3x + 3z)) / 128 ADD R1, R3, R9, LSL #3 \ R1 = R3 + R9 << 3 \ = 5x + 3z + 8z \ = 5x + 11z \ So the above gives us: \ \ R0 = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x) \ + sin(3x + 3z)) / 128 \ R1 = 5x + 11z LDR R2, sinTableAddr \ Using the same approach as above, set: BIC R1, R1, #&00300000 \ LDR R1, [R2, R1, LSR #20] \ R1 = sin(R1) \ = sin(5x + 11z) ADD R0, R0, R1, ASR #8 \ R0 = R0 + R1 >> 8 \ = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x) \ + sin(3x + 3z)) / 128 \ + sin(5x + 11z) / 256 ADD R1, R9, R3, LSL #1 \ R1 = R9 + R3 << 1 \ = z + (5x + 3z) << 1 \ = z + 10x + 6z \ = 10x + 7z \ So the above gives us: \ \ R0 = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x) \ + sin(3x + 3z)) / 128 \ + sin(5x + 11z) / 256 \ R1 = 10x + 7z LDR R2, sinTableAddr \ Using the same approach as above, set: BIC R1, R1, #&00300000 \ LDR R1, [R2, R1, LSR #20] \ R1 = sin(R1) \ = sin(10x + 7z) ADD R0, R0, R1, ASR #8 \ R0 = R0 + R1 >> 8 \ = (sin(x - 2z) + sin(4x + 3z) \ + sin(3z - 5x) \ + sin(3x + 3z)) / 128 \ + \ (sin(5x + 11z) + sin(10x + 7z)) / 256 RSB R0, R0, #LAND_MID_HEIGHT \ R0 = LAND_MID_HEIGHT - R0 \ \ = LAND_MID_HEIGHT - \ (2*sin(x - 2z) + 2*sin(4x + 3z) \ + 2*sin(3z - 5x) \ + 2*sin(3x + 3z) \ + sin(5x + 11z) \ + sin(10x + 7z)) / 256 \ \ which is the result that we want CMP R0, #SEA_LEVEL \ If R0 > SEA_LEVEL, set R0 = SEA_LEVEL so MOVGT R0, #SEA_LEVEL \ we don't create landscapes lower then sea \ level CMP R8, #LAUNCHPAD_SIZE \ If R8 and R9 are both < LAUNCHPAD_SIZE, CMPLO R9, #LAUNCHPAD_SIZE \ then this coordinate is on the launchpad, MOVLO R0, #LAUNCHPAD_ALTITUDE \ so set R0 = LAUNCHPAD_ALTITUDE STR R0, [R11, #altitude] \ Set altitude = R0, so we return the result \ in R0 and set the altitude variable MOV PC, R14 \ Return from the subroutine