Skip to navigation

Revs on the BBC Micro

Drawing the track: MapSegmentsToLines

Name: MapSegmentsToLines [Show more] Type: Subroutine Category: Drawing the track Summary: Map verges in the track segment list to track lines in the track view Deep dive: Drawing the track verges
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * DrawTrack calls MapSegmentsToLines

This routine populates the leftSegment or rightSegment table (depending on the arguments). It does this by working its way through the verge buffer, from distant entries at the start of the buffer, to closer entries at the end of the buffer. As it goes, it looks at the pitch angles of the entries, which equate to track lines in the track view, with high track lines matching high pitch values (which are higher up the screen). It then fills the corresponding entries in the leftSegment or rightSegment table, which have one entry per track line, with the index numbers of the relevant entries from the verge buffer. In other words, if we fill five entries in the rightSegment table with an index n, then that means that the segment that's mapped to entry n in the verge buffer will take up five track lines on-screen, so it will be five pixels tall. It's worth reiterating that the track verge buffer stores distant segments first, coming towards us as we progress through the list, so leftSegment and rightSegment are the reverse of this, with closest segments at the start, furthest segments at the end. This matches the track lines, where small numbers are at the bottom of the screen (i.e. close), high numbers are up the screen (i.e. further away).
Arguments: A The low byte of the table address to populate: * LO(rightSegment) = populate rightSegment * LO(leftSegment) = populate leftSegment Y Index of the last entry in the track verge buffer: * segmentListRight for the right verge * segmentListPointer for the left verge X The index within the track verge buffer of the horizon: * horizonListIndex for the right verge * horizonListIndex + 40 for the left verge
Returns: vergeDepthOfField Updated to skip any segments that are hidden, so we do not waste time drawing their verges N Contains the pitch angle of the last segment to be mapped, which is the closest one to the player
.MapSegmentsToLines STA maps5+1 \ Modify the instruction at maps5 to use the low byte of \ the address in A, so the STA instruction writes to the \ table specified by A STY vergeBufferEnd \ Set vergeBufferEnd to the index in Y, which points to \ the last entry in the track verge buffer for this side \ of the track DEY \ Set U = Y - 1 STY U \ \ In the following loop, X iterates up to U - 1, so this \ ensures that it increments to the end of the track \ verge buffer, but not past the end JSR CheckVergeOnScreen \ Set bit 7 of V if the segment in X is off-screen, or \ clear bit 7 if it is on-screen LDY horizonLine \ Set Y to the track line number of the horizon JMP maps7 \ Jump to maps7 to join the loop below \ \ The loop below uses two loop counters: \ \ * Y is an index into leftSegment or rightSegment, \ starting at horizonLine and decrementing down \ through the track lines, i.e. the ones that show \ land rather than sky \ \ Y decrements from the horizon track line to 1 \ \ * X is an index into xVergeRightHi and yVergeRight, \ starting at horizonListIndex (the entry in the \ verge buffer that's on the horizon) and \ incrementing though the verge buffer, going from \ distant segments back towards the player \ \ X increments from the horizon entry in the verge \ buffer, up to the end of the verge buffer (as per \ the value that we gave U above) \ \ In other words, we work our way through the verge \ buffer from the horizon towards the player, inserting \ values into the leftSegment or rightSegment table \ for each corresponding track line, starting at the \ horizon track line and working down the screen \ \ We also enter the loop with V set to the visibility of \ the horizon's entry in the verge buffer .maps1 \ We loop back here when X is incremented to point to \ the next entry in the verge buffer LDA V \ If bit 7 of V is clear, then we have already reached a BPL maps2 \ visible entry in the verge buffer, so jump to maps2 \ Bit 7 of V is set, so we haven't yet reached a visible \ entry in the verge buffer, so we test the new entry in \ X to see if it is visible \ \ In this way, V is a flag that records when we reach \ the visible entries in the buffer, at which point we \ flip bit 7 of V from set to clear, and stop checking JSR CheckVergeOnScreen \ Set bit 7 of V if the segment in X is off-screen, or \ clear bit 7 if it is on-screen .maps2 LDA yVergeRight,X \ Set A to the pitch angle of the current entry in the \ verge buffer CMP #80 \ If A >= 80, then this pitch angle maps to a track line BCS maps9 \ that's off the top of the screen, so jump to maps9 to \ pad out the rest of the leftSegment or rightSegment \ table and finish off BIT V \ If bit 7 of V is clear, then we have already reached a BPL maps3 \ visible entry in the verge buffer, so jump to maps3 \ If we get here then bit 7 of V is set, so the current \ entry in the verge buffer is not on-screen in the \ x-axis, though it is on-screen in the y-axis as we \ know A < 80 CMP yVergeRight+1,X \ If the pitch angle of this entry in the verge buffer BEQ maps14 \ is the same as the angle of the next entry (i.e. \ the next entry closer to the player), then jump to \ maps14 to set bit 7 of this entry's vergeDataRight \ and move on to the next entry in the verge buffer .maps3 \ If we get here then either this entry in the verge \ buffer is on-screen, or it's off-screen but is at a \ different pitch angle to the next closest entry CMP N \ If A >= N, then the entry is at a higher pitch angle BCS maps14 \ than the current track line in N, so jump to maps14 \ to set bit 7 of the X-th vergeDataRight and on to \ maps8 to move on to the next X \ Otherwise we fall through to fill index Y down to \ index A + 1 with the value in X, which sets the \ leftSegment or rightSegment entries for track lines \ Y down to A + 1 with the index in X .maps4 \ If we get here then we fill index Y down to index \ A + 1 with the value in X STA RR \ Set RR = A TXA \ Set A = X JMP maps6 \ Jump to maps6 to fill index Y down to index RR + 1 \ with the value in A .maps5 STA leftSegment,Y \ Store A in the Y-th entry in leftSegment or \ rightSegment DEY \ Decrement the loop counter in Y to point to the next \ track line down the screen .maps6 \ This loop fills leftSegment or rightSegment with the \ value in A, from index Y down to index RR + 1 CPY RR \ Loop back to maps5 until we have filled from index Y BNE maps5 \ to RR + 1 with the value in A .maps7 \ This is where we first join the loop STY N \ Set N = Y, so N contains the number of the track line \ we are currently processing .maps8 INX \ Increment the loop counter in X to point to the next \ entry in the verge buffer BMI maps11 \ If bit 7 of X is set, then we must have called the \ loop from maps10 below, so we have now finished \ filling the leftSegment or rightSegment table all the \ way back to index 1, so jump to maps11 to exit the \ loop as we are done filling CPX U \ If X < U, then we still have entries in the verge BCC maps1 \ buffer to process, so loop back to maps1 .maps9 \ If we get here then we are nearly done, and just need \ to pad out the rest of the leftSegment or rightSegment \ table with the current entry's index with bit 7 set, \ working back to position 1 in the table \ First we cap the pitch angle of the current entry in \ the verge buffer to a maximum of N LDA yVergeRight,X \ Set A to the pitch angle of the current entry in the \ verge buffer (i.e. the last entry we will fill, which \ is the closest track line to the player) BMI maps10 \ If A is negative, jump to maps10 CMP N \ If A < N, jump to maps10 BCC maps10 \ If we get here then A is positive and A >= N LDA N \ Set the pitch angle of the current entry in the verge STA yVergeRight,X \ buffer to N .maps10 \ We now get ready to loop back to the fill loop above, \ to pad out the remainder of the leftSegment or \ rightSegment table TXA \ Set bit 7 of X, so when we jump back to the fill loop ORA #%10000000 \ via maps4, we fill the rest of the leftSegment or TAX \ rightSegment table with this value, and then exit the \ loop by jumping to maps11 LDA #0 \ Set A = 0, so we fill the leftSegment or rightSegment \ table back to position 1 with the value in X BEQ maps4 \ Jump to maps4 to fill index Y down to index A + 1 with \ the value in X (this BEQ is effectively a JMP as A is \ always zero) .maps11 \ Now we loop X from vergeDepthOfField up until we find \ the first vergeDataRight entry with bit 7 clear, and \ set vergeDepthOfField to the updated X \ \ This moves through the verge buffer, towards the \ player, starting at the horizon line, and skipping any \ entries that have bit 7 set in vergeDataRight, so we \ set the depth of field to skip these segments \ \ We set bit 7 of vergeDataRight for any entries whose \ pitch angles were higher than the current track line \ as we worked down the screen and towards the player in \ the verge buffer \ \ So this stops us from displaying verges on any hills \ between the horizon and the player LDX vergeDepthOfField \ Set X = to the current verge depth of field, which is \ the index within the verge buffer beyond which we do \ not draw verge marks .maps12 LDA vergeDataRight,X \ If bit 7 the X-th vergeDataRight is clear, jump to BPL maps13 \ maps13 to store X in vergeDepthOfField INX \ Increment the loop counter in X CPX U \ If X < U, loop back to maps12 BCC maps12 .maps13 STX vergeDepthOfField \ Set vergeDepthOfField = X RTS \ Return from the subroutine .maps14 LDA #%10000000 \ Set bit 7 of vergeDataRight for the current entry in ORA vergeDataRight,X \ the verge buffer STA vergeDataRight,X BMI maps8 \ Jump to maps8 (this BMI is effectively a JMP as bit 7 \ of A is always set)