Skip to navigation


Revs F source

Name: GetWingSettings [Show more] Type: Subroutine Category: Keyboard Summary: Get the front and rear wing settings from the player
Context: See this subroutine on its own page References: This subroutine is called as follows: * HeadToTrack calls GetWingSettings
.GetWingSettings LDX #5 \ Print "THE PITS" as a double-height header at column JSR PrintHeader \ 11, row 4, in blue text on a yellow background LDX #24 \ Print token 24, which shows the prompt "SELECT WING JSR PrintToken \ SETTINGS > range 0 to 40" and a further prompt of \ "rear > " JSR GetNumberInput \ Fetch a number from the keyboard STA rearWingSetting \ Store the entered number in rearWingSetting LDX #25 \ Print token 25, which shows a prompt of "front > " JSR PrintToken JSR GetNumberInput \ Fetch a number from the keyboard STA frontWingSetting \ Store the entered number in frontWingSetting JSR WaitForSpace \ Print a prompt and wait for SPACE to be pressed RTS \ Return from the subroutine
Name: PrintRaceClass [Show more] Type: Subroutine Category: Text Summary: Print the race class
Context: See this subroutine on its own page References: This subroutine is called as follows: * MainLoop (Part 5 of 6) calls PrintRaceClass * PrintDriverTable calls PrintRaceClass
.PrintRaceClass LDA raceClass \ Set A to the race class + 7, so that gives us: CLC \ ADC #7 \ * 7 for Novice TAX \ * 8 for Amateur \ * 9 for Professional JSR PrintToken \ Print token X, which will be token 7 ("Novice"), token \ 8 ("Amateur") or token 9 ("Professional") RTS \ Return from the subroutine
Name: token50 [Show more] Type: Variable Category: Text Summary: Text for recursive token 50 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * PrintDriverTable uses token50 * tokenHi uses token50 * tokenLo uses token50
.token50 EQUB 31, 24, 2 \ Move text cursor to column 24, row 2 EQUB 200 + 18 \ Print token (configurable token number, default is 18, \ which is " 5") EQUB 200 + 14 \ Print token 14 (" laps") EQUB 255 \ End token
Name: token6 [Show more] Type: Variable Category: Text Summary: Text for recursive token 6 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token6 * tokenLo uses token6
.token6 EQUB 160 + 2 \ Print 2 spaces EQUS "BEST LAP TIMES" \ Print "BEST LAP TIMES" EQUB 160 + 2 \ Print 2 spaces EQUB 255 \ End token
Name: token13 [Show more] Type: Variable Category: Text Summary: Text for recursive token 13 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token13 * tokenLo uses token13
.token13 EQUS " mins" \ Print " mins" EQUB 255 \ End token
Name: token14 [Show more] Type: Variable Category: Text Summary: Text for recursive token 14 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token14 * tokenLo uses token14
.token14 EQUS " laps" \ Print " laps" EQUB 255 \ End token
Name: token15 [Show more] Type: Variable Category: Text Summary: Text for recursive token 15 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token15 * tokenLo uses token15
.token15 EQUS " RACE" \ Print " RACE" EQUB 255 \ End token EQUS "ins" \ These bytes appear to be unused EQUB &FF, &81 EQUB &81, &81 EQUB &81, &81
Name: dashData25 [Show more] Type: Variable Category: Screen buffer Summary: Contains code and part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData25
.dashData25 SKIP 10 \ Populated with part of the dashboard image SKIP 26 \ Populated with code from &7B00 to &7B19
Name: objectIndex [Show more] Type: Variable Category: 3D objects Summary: Index range of an object's data in the object data tables Deep dive: Object definitions
Context: See this variable on its own page References: This variable is used as follows: * DrawObject uses objectIndex

Given an object type, this table contains the index range for the object's data in the objectTop, objectBottom, objectLeft, objectRight and objectColour tables.
.objectIndex EQUB 0 \ Object type 0 = 0 to 4 EQUB 5 \ Object type 1 = 5 to 8 EQUB 9 \ Object type 2 = 9 to 13 EQUB 14 \ Object type 3 = 14 to 17 EQUB 18 \ Object type 4 = 18 to 24 EQUB 25 \ Object type 5 = 25 EQUB 26 \ Object type 6 = 26 EQUB 27 \ Object type 7 = 27 to 29 EQUB 30 \ Object type 8 = 30 to 31 EQUB 32 \ Object type 9 = 32 to 33 EQUB 34 \ Object type 10 = 34 to 36 EQUB 37 \ Object type 11 = 37 to 38 EQUB 39 \ Object type 12 = 39 to 40
Name: scaffoldIndex [Show more] Type: Variable Category: 3D objects Summary: Index of an object's scaffold in the objectScaffold table Deep dive: Scaling objects with scaffolds
Context: See this variable on its own page References: This variable is used as follows: * DrawObject uses scaffoldIndex

Given an object type, this table contains the index range for the object's scaffold in the objectScaffold table.
.scaffoldIndex EQUB 0 \ Object type 0 = 0 to 7 EQUB 8 \ Object type 1 = 8 to 15 EQUB 16 \ Object type 2 = 16 to 23 EQUB 24 \ Object type 3 = 24 to 29 EQUB 30 \ Object type 4 = 30 to 37 EQUB 38 \ Object type 5 = 38 to 40 EQUB 41 \ Object type 6 = 41 to 43 EQUB 44 \ Object type 7 = 44 to 48 EQUB 49 \ Object type 8 = 49 to 52 EQUB 53 \ Object type 9 = 53 to 56 EQUB 57 \ Object type 10 = 57 to 61 EQUB 62 \ Object type 11 = 62 to 65 EQUB 66 \ Object type 12 = 66 to 69 EQUB 70
Name: GetDriverAddress [Show more] Type: Subroutine Category: Text Summary: Get the address of the specified driver's name
Context: See this subroutine on its own page References: This subroutine is called as follows: * GetDriverName calls GetDriverAddress * PrintDriverPrompt calls GetDriverAddress * PrintPositionName calls GetDriverAddress

This routine calculates the address of driver A's name using the following: (Y A) = driverNames1 + (A div 4) * &100 + (A mod 4) * 12 The names of the 20 drivers are stored in in five blocks within the main game code. Each block of contains four names, each of which is 12 characters long. The blocks start every &100 (256) bytes, starting with the first block at driverNames1 and going through to driverNames5. Given driver number A, A div 4 is the block number, while A mod 4 is the number of the 12-character name within that block. So we have: * (A div 4) * &100 is the address of the start of the block containing the name of driver A * (A mod 4) * 12 is the offset of driver A's 12-character name within that block The first block is at driverNames1, so we add them together to arrive at the calculation above.
Arguments: X The driver number (0 to 19)
Returns: (Y A) The address of the driver's 12-character name
.GetDriverAddress TXA \ We start with the high byte of the calculation, which LSR A \ is Y = HI(driverNames1) + (A div 4) LSR A CLC ADC #HI(driverNames1) TAY \ And now we do the low byte calculation, which is \ A = LO(driverNames1) + (A mod 4) * 12 TXA \ Set A = (A mod 4) * 4 AND #3 ASL A ASL A STA T \ Set T = A \ = (A mod 4) * 4 ASL A \ Set A = (A mod 4) * 8 CLC \ Set A = A + T ADC T \ = (A mod 4) * 8 + (A mod 4) * 4 \ = (A mod 4) * 12 ADC #LO(driverNames1) \ Set A = LO(driverNames1) + A \ = LO(driverNames1) + (A mod 4) * 12 RTS \ Return from the subroutine
Name: token27 [Show more] Type: Variable Category: Text Summary: Text for recursive token 27 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token27 * tokenLo uses token27
.token27 EQUB 200 + 54 \ Print token 54 ("FORMULA 3 CHAMPIONSHIP" header) EQUB 200 + 36 \ Print token 36 (menu option 1 with "PRESS" prompt) EQUB 200 + 11 \ Print token 11 ("ENTER ") EQUS "ANOTHER" \ Print "ANOTHER" EQUB 200 + 12 \ Print token 12 (" DRIVER") EQUB 200 + 37 \ Print token 37 (menu option 2) EQUS "START" \ Print "START" EQUB 200 + 15 \ Print token 15 (" RACE") EQUB 255 \ End token
Name: token34 [Show more] Type: Variable Category: Text Summary: Text for recursive token 34
Context: See this variable on its own page References: This variable is used as follows: * PrintHeader uses token34 * tokenHi uses token34 * tokenLo uses token34

The configurable values below are set in the PrintHeader routine. Deep dive: Text tokens
.token34 EQUB 141 \ Set double-height text EQUB 129, 157 \ Set background colour (configurable, default is red) EQUB 131 \ Set foreground colour (configurable, default is yellow \ alphanumeric) EQUB 200+0 \ Print token (configurable token number, default is 0, \ which is "FORMULA 3 CHAMPIONSHIP") EQUB 160+2 \ Print spaces (configurable, default is 2 spaces) EQUB 156 \ Set background colour to black EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81
Name: dashData26 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData26 * fillDataOffset uses dashData26
.dashData26 SKIP 41
Name: PrintSpaces [Show more] Type: Subroutine Category: Text Summary: Print the specified number of spaces
Context: See this subroutine on its own page References: This subroutine is called as follows: * Print234DigitBCD calls PrintSpaces * Print4DigitBCD calls PrintSpaces * PrintDriverTable calls PrintSpaces * PrintToken calls PrintSpaces * UpdateLapTimers calls PrintSpaces

Arguments: A The number of spaces to print (1 to 39)
Returns: Z flag Set (so a BEQ following the routine call will always branch)
.PrintSpaces STA T \ Set T to the number of spaces to print to use as a \ loop counter .spac1 LDA #' ' \ Print a space JSR PrintCharacter DEC T \ Decrement the loop counter BNE spac1 \ Loop back until we have printed the right number of \ spaces RTS \ Return from the subroutine
Name: DrawFence (Part 1 of 2) [Show more] Type: Subroutine Category: Screen buffer Summary: Draw the fence that we crash into when running off the track
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckForCrash calls DrawFence

Other entry points: DrawFence-1 Contains an RTS
.DrawFence LDA #0 \ Set playerMoving = 0 to denote that the player's car STA playerMoving \ is not moving STA T \ Set T = 0, to use as a counter as we work our way \ through the 40 dash data blocks that contain the track \ view STA P \ Set (Q P) to point to the first dash data block at LDA #HI(dashData) \ dashData (this works because the low byte of dashData STA Q \ is zero) .fenc1 LDX T \ If X = 40 then we have drawn the fence across all the CPX #40 \ dash data blocks, so return from the subroutine (as BEQ DrawFence-1 \ DrawFence-1 contains an RTS) LDA dashDataOffset,X \ Set U to the dashDataOffset for the current dash data STA U \ block LDY #70 \ Set Y = 70, to use as a loop counter that works \ through all 70 bytes in the dash data block JMP fenc2 \ We now jump to part 2 to work our way through the 70 \ bytes in the dash data block, each of which represents \ a four-pixel line, with 70 lines stacked one on top of \ the other, in a four-pixel-wide vertical strip
Name: fencePixelsGrass [Show more] Type: Variable Category: Screen buffer Summary: Pixel bytes for the fence with green grass behind it
Context: See this variable on its own page References: This variable is used as follows: * DrawFence (Part 2 of 2) uses fencePixelsGrass
.fencePixelsGrass EQUB %10101010 \ Four pixels: green, black, green, black EQUB %01110111 \ Four pixels: black, green, green, green EQUB %10101010 \ Four pixels: green, black, green, black EQUB %11011101 \ Four pixels: green, green, black, green
Name: fencePixelsSky [Show more] Type: Variable Category: Screen buffer Summary: Pixel bytes for the fence with blue sky behind it
Context: See this variable on its own page References: This variable is used as follows: * DrawFence (Part 2 of 2) uses fencePixelsSky
.fencePixelsSky EQUB %00001010 \ Four pixels: blue, black, blue, black EQUB %00000111 \ Four pixels: black, blue, blue, blue EQUB %00001010 \ Four pixels: blue, black, blue, black EQUB %00001101 \ Four pixels: blue, blue, black, blue
Name: token21 [Show more] Type: Variable Category: Text Summary: Text for recursive token 21 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token21 * tokenLo uses token21
.token21 EQUB 200 + 54 \ Print token 54 ("FORMULA 3 CHAMPIONSHIP" header) EQUB 200 + 36 \ Print token 36 (menu option 1 with "PRESS" prompt) EQUB 200 + 7 \ Print token 7 ("Novice") EQUB 200 + 37 \ Print token 37 (menu option 2) EQUB 200 + 8 \ Print token 8 ("Amateur") EQUB 200 + 38 \ Print token 38 (menu option 3) EQUB 200 + 9 \ Print token 9 ("Professional") EQUB 200 + 35 \ Print token 35 (cyan, move cursor to prompt position) EQUB 160 + 4 \ Print 4 spaces EQUB 200 + 10 \ Print token 10 ("SELECT ") EQUS "THE CLASS OF" \ Print "THE CLASS OF" EQUB 200 + 15 \ Print token 15 (" RACE") EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81
Name: dashData27 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData27 * fillDataOffset uses dashData27
.dashData27 SKIP 52
Name: segmentStep [Show more] Type: Variable Category: Track geometry Summary: The number of segments we step over when working backwards through the track segment buffer in GetSegmentAngles
Context: See this variable on its own page References: This variable is used as follows: * GetSegmentAngles (Part 3 of 3) uses segmentStep
.segmentStep EQUB 0 \ Not used as segmentStep is only accessed with indexes \ greater than zero, but this represents starting at \ the front segment in the track segment buffer, so we \ step backwards through the buffer \ \ Entry 32 in the track segment buffer is the player's \ car (as it is 32 segments behind the front segment), \ so the player's car is at entry 13 in the track \ segment list EQUB 13 * 3 \ Step back 13 segments to 13 for segment list entry 1 EQUB 6 * 3 \ Step back 6 segments to 19 for segment list entry 2 EQUB 3 * 3 \ Step back 3 segments to 22 for segment list entry 3 EQUB 3 \ Step back 1 segment to 23 for segment list entry 4 EQUB 3 \ Step back 1 segment to 24 for segment list entry 5 EQUB 3 \ Step back 1 segment to 25 for segment list entry 6 EQUB 3 \ Step back 1 segment to 26 for segment list entry 7 EQUB 3 \ Step back 1 segment to 27 for segment list entry 8 EQUB 3 \ Step back 1 segment to 28 for segment list entry 9 EQUB 3 \ Step back 1 segment to 29 for segment list entry 10 EQUB 3 \ Step back 1 segment to 30 for segment list entry 11 EQUB 3 \ Step back 1 segment to 31 for segment list entry 12 EQUB 3 \ Step back 1 segment to 32 for segment list entry 13 EQUB 3 \ Step back 1 segment to 33 for segment list entry 14 EQUB 3 \ Step back 1 segment to 34 for segment list entry 15 EQUB 3 \ Step back 1 segment to 35 for segment list entry 16 EQUB 3 \ Not used as there is a maximum of 16 segments in the \ track segment list
Name: shiftedKeys [Show more] Type: Variable Category: Keyboard Summary: Negative inkey values for the configuration keys that are pressed in combination with SHIFT
Context: See this variable on its own page References: This variable is used as follows: * ProcessShiftedKeys uses shiftedKeys
.shiftedKeys EQUB &86 \ Right arrow EQUB &8E \ f1 EQUB &8D \ f2 EQUB &8D \ f2 EQUB &EB \ f4 EQUB &8B \ f5 EQUB &DF \ f0 EQUB &96 \ COPY EQUB &A6 \ DELETE EQUB &E9 \ f7 IF _SUPERIOR OR _REVSPLUS EQUB &8C \ f3 EQUB &8A \ f6 ENDIF
Name: menuKeys [Show more] Type: Variable Category: Keyboard Summary: Negative inkey values for the menu keys (SPACE, "1", "2" and "3") for the Acornsoft release
Context: See this variable on its own page References: This variable is used as follows: * GetMenuOption uses menuKeys
IF _ACORNSOFT OR _4TRACKS .menuKeys EQUB &9D \ Negative inkey value for SPACE EQUB &CF \ Negative inkey value for "1" EQUB &CE \ Negative inkey value for "2" EQUB &EE \ Negative inkey value for "3" ELIF _SUPERIOR OR _REVSPLUS EQUB &CE, &EE \ These bytes appear to be unused ENDIF
Name: timeFromOption [Show more] Type: Variable Category: Keyboard Summary: Table to convert from the option numbers in the qualifying lap duration menu to the actual number of minutes
Context: See this variable on its own page References: This variable is used as follows: * MainLoop (Part 2 of 6) uses timeFromOption

Interestingly, the menu offers 5, 10 and 20 minutes, but these translate into 5, 10 and 26 minutes of actual qualifying time.
.timeFromOption EQUB 4, 9, 25 EQUB &00 \ This byte appears to be unused
Name: lapsFromOption [Show more] Type: Variable Category: Keyboard Summary: Table to convert from the option numbers in the laps menu to the actual number of laps
Context: See this variable on its own page References: This variable is used as follows: * MainLoop (Part 5 of 6) uses lapsFromOption
.lapsFromOption EQUB 5, 10, 20
Name: pointsForPlace [Show more] Type: Variable Category: Drivers Summary: The points awarded for the top six places, plus the fastest lap
Context: See this variable on its own page References: This variable is used as follows: * AwardRacePoints uses pointsForPlace
.pointsForPlace EQUB 9 \ Points for first place EQUB 6 \ Points for second place EQUB 4 \ Points for third place EQUB 3 \ Points for fourth place EQUB 2 \ Points for fifth place EQUB 1 \ Points for sixth place EQUB 1 \ Points for the fastest lap EQUB &00, &00 \ These bytes appear to be unused
Name: token29 [Show more] Type: Variable Category: Text Summary: Text for recursive token 29 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token29 * tokenLo uses token29
.token29 EQUB 200 + 54 \ Print token 54 ("FORMULA 3 CHAMPIONSHIP" header) EQUB 200 + 35 \ Print token 35 (cyan, move cursor to prompt position) EQUB 160 + 5 \ Print 5 spaces EQUB 130 \ Set foreground colour to green alphanumeric EQUB 200 + 12 \ Print token 12 (" DRIVER") EQUB 200 + 16 \ Print token 16 (" > ") EQUB 255 \ End token
Name: token9 [Show more] Type: Variable Category: Text Summary: Text for recursive token 9 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token9 * tokenLo uses token9
.token9 EQUS "Professional" \ Print "Professional" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81
Name: dashData28 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData28 * fillDataOffset uses dashData28
.dashData28 SKIP 56
Name: jumpShallowRight [Show more] Type: Variable Category: Drawing the track Summary: Branch labels for self-modifying code in the DrawShallowToRight routine Deep dive: Drawing the track verges
Context: See this variable on its own page References: This variable is used as follows: * DrawShallowToRight uses jumpShallowRight
.jumpShallowRight EQUB shlr4 - shlr2 EQUB shlr6 - shlr2 EQUB shlr8 - shlr2 EQUB shlr10 - shlr2 EQUB shlr13 - shlr2 EQUB shlr15 - shlr2 EQUB shlr17 - shlr2 EQUB shlr19 - shlr2 EQUB shlr3 - shlr2 \ These are not used EQUB shlr5 - shlr2 EQUB shlr7 - shlr2 EQUB shlr9 - shlr2 EQUB shlr12 - shlr2 EQUB shlr14 - shlr2 EQUB shlr16 - shlr2 EQUB shlr18 - shlr2
Name: SetRowColours [Show more] Type: Subroutine Category: Text Summary: Set the foreground and background colours for a table row
Context: See this subroutine on its own page References: This subroutine is called as follows: * PrintDriverTable calls SetRowColours

Arguments: Y Table row number colourScheme Colour scheme: 0, 4, 8
.SetRowColours TYA \ Set X = colourScheme if Y is even AND #1 \ = colourScheme + 1 if Y is odd CLC \ ADC colourScheme \ So X is now one of the following, depending on the TAX \ colour scheme and whether the row number is even or \ odd: \ \ * Scheme 0: 0 (even) or 1 (odd) \ * Scheme 4: 4 (even) or 5 (odd) \ * Scheme 8: 8 (even) or 9 (odd) \ \ We now fetch the colour palette from the rowColours \ table using the following offsets: \ \ * Scheme 0: 0 on 2 (even) or 1 on 3 (odd) \ * Scheme 4: 4 on 6 (even) or 5 on 7 (odd) \ * Scheme 8: 8 on 10 (even) or 9 on 11 (odd) LDA rowColours,X \ Set the configurable foreground colour in token 31 to STA token31+5 \ the X-th entry in the rowColours table LDA rowColours+2,X \ Set the configurable background colour in token 31 to STA token31+3 \ the X+2-th entry in the rowColours table RTS \ Return from the subroutine
Name: rowColours [Show more] Type: Variable Category: Text Summary: Three different palettes for displaying even-odd rows in tables
Context: See this variable on its own page References: This variable is used as follows: * SetRowColours uses rowColours
.rowColours EQUB 132, 133 \ Scheme 0: Even rows: 132 on 134 (blue on cyan) EQUB 134, 135 \ Odd rows: 134 on 135 (cyan on white) EQUB 129, 132 \ Scheme 4: Even rows: 129 on 132 (red on blue) EQUB 131, 130 \ Odd rows: 131 on 130 (yellow on green) EQUB 131, 132 \ Scheme 8: Even rows: 131 on 132 (yellow on blue) EQUB 129, 135 \ Odd rows: 129 on 135 (red on white)
Name: token10 [Show more] Type: Variable Category: Text Summary: Text for recursive token 10 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token10 * tokenLo uses token10
.token10 EQUS "SELECT " \ Print "SELECT " EQUB 255 \ End token
Name: token12 [Show more] Type: Variable Category: Text Summary: Text for recursive token 12 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token12 * tokenLo uses token12
.token12 EQUS " DRIVER" \ Print " DRIVER" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81
Name: dashData29 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData29 * fillDataOffset uses dashData29
.dashData29 SKIP 60
Name: jumpSteepRight [Show more] Type: Variable Category: Drawing the track Summary: Branch labels for self-modifying code in the DrawSteepToRight routine Deep dive: Drawing the track verges
Context: See this variable on its own page References: This variable is used as follows: * DrawSteepToRight uses jumpSteepRight
.jumpSteepRight EQUB stlr2 - stlr2 EQUB stlr3 - stlr2 EQUB stlr4 - stlr2 EQUB stlr5 - stlr2 EQUB stlr6 - stlr2 EQUB stlr7 - stlr2 EQUB stlr8 - stlr2 EQUB stlr9 - stlr2
Name: jumpSteepLeft [Show more] Type: Variable Category: Drawing the track Summary: Branch labels for self-modifying code in the DrawSteepToLeft routine Deep dive: Drawing the track verges
Context: See this variable on its own page References: This variable is used as follows: * DrawSteepToLeft uses jumpSteepLeft
.jumpSteepLeft EQUB strl9 - strl2 EQUB strl8 - strl2 EQUB strl7 - strl2 EQUB strl6 - strl2 EQUB strl5 - strl2 EQUB strl4 - strl2 EQUB strl3 - strl2 EQUB strl2 - strl2
Name: GetNumberInput [Show more] Type: Subroutine Category: Keyboard Summary: Fetch a number between 0 and 40 from the keyboard
Context: See this subroutine on its own page References: This subroutine is called as follows: * GetWingSettings calls GetNumberInput

Returns: A The value of the number entered (0 to 40)
.GetNumberInput LDA #LO(T) \ Set (Y A) = T LDY #HI(T) LDX #2 \ Fetch a string of up to two characters from the JSR GetTextInput \ keyboard, store the characters at location T and U (as \ U = T + 1), and set Y to the number of characters \ entered JSR GetNumberFromText \ Convert the two-character input into a number in A, \ and report the conversion status in the C flag BCC numb2 \ If we got a valid number that is 40 or less then the C \ flag will be clear, so jump to numb2 to return from \ the subroutine .numb1 DEY \ Decrement the number of characters in the entered \ string, which is in Y BMI GetNumberInput \ If Y is now negative, then there are no characters \ left on-screen, so jump back to the start of the \ routine to try fetching another number LDA #127 \ Otherwise delete the last character shown on-screen by JSR OSWRCH \ printing a delete character (ASCII 127) JMP numb1 \ Jump back to numb1 to delete any other on-screen \ characters before starting again .numb2 RTS \ Return from the subroutine
Name: startMirror [Show more] Type: Variable Category: Dashboard Summary: The offset from mirrorAddress for the start of each mirror segment
Context: See this variable on its own page References: This variable is used as follows: * DrawCarInMirror uses startMirror
.startMirror EQUB &AA \ Mirror segment 0 (left mirror, outer segment) EQUB &AC \ Mirror segment 1 (left mirror, middle segment) EQUB &B0 \ Mirror segment 2 (left mirror, inner segment) EQUB &B0 \ Mirror segment 3 (right mirror, inner segment) EQUB &AC \ Mirror segment 4 (right mirror, middle segment) EQUB &AA \ Mirror segment 5 (right mirror, outer segment)
Name: token37 [Show more] Type: Variable Category: Text Summary: Text for recursive token 37 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token37 * tokenLo uses token37
.token37 EQUB 31, 5, 18 \ Move text cursor to column 5, row 18 EQUB 132, 157 \ Set background colour to blue EQUB 134 \ Set foreground colour to cyan alphanumeric EQUS "2" \ Print "2" EQUB 160 + 2 \ Print 2 spaces EQUB 156 \ Set background colour to black EQUB 160 + 5 \ Print 5 spaces EQUB 131 \ Set foreground colour to yellow alphanumeric EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81
Name: dashData30 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData30 * fillDataOffset uses dashData30
.dashData30 SKIP 64
Name: fillDataOffset [Show more] Type: Variable Category: Dashboard Summary: Dash data offsets, tweaked to give bottom line values that are compatible with the process of filling blocks to the left Deep dive: Creating objects from edges
Context: See this variable on its own page References: This variable is used as follows: * FillInsideObject uses fillDataOffset

Contains dash data offsets that are skewed so that dash data blocks whose left neighbours start lower down the screen than they do (i.e. they have a smaller offset) contain the left neighbour's offset rather than their own. This ensures that when filling blocks to the left of the current block, the value of the bottom line (i.e. the offset) is set to the bottom of the column being filled, rather than the smaller column to its right.
.fillDataOffset EQUB dashData0 - (dashData + &80 * 0) - 1 \ These match dashDataOffset EQUB dashData1 - (dashData + &80 * 1) - 1 EQUB dashData2 - (dashData + &80 * 2) - 1 EQUB dashData3 - (dashData + &80 * 3) - 1 EQUB dashData4 - (dashData + &80 * 4) - 1 EQUB dashData5 - (dashData + &80 * 5) - 1 EQUB dashData6 - (dashData + &80 * 6) - 1 EQUB dashData6 - (dashData + &80 * 6) - 1 \ dashData6 repeated, as block 7 \ is smaller than block 6 EQUB dashData7 - (dashData + &80 * 7) - 1 \ These are out by one, so the EQUB dashData8 - (dashData + &80 * 8) - 1 \ X-th entry is the offset for EQUB dashData9 - (dashData + &80 * 9) - 1 \ block X - 1 EQUB dashData10 - (dashData + &80 * 10) - 1 EQUB dashData11 - (dashData + &80 * 11) - 1 EQUB dashData12 - (dashData + &80 * 12) - 1 EQUB dashData13 - (dashData + &80 * 13) - 1 EQUB dashData14 - (dashData + &80 * 14) - 1 EQUB dashData15 - (dashData + &80 * 15) - 1 EQUB dashData16 - (dashData + &80 * 16) - 1 EQUB dashData17 - (dashData + &80 * 17) - 1 EQUB dashData18 - (dashData + &80 * 18) - 1 EQUB dashData19 - (dashData + &80 * 19) - 1 EQUB dashData20 - (dashData + &80 * 20) - 1 EQUB dashData21 - (dashData + &80 * 21) - 1 EQUB dashData22 - (dashData + &80 * 22) - 1 EQUB dashData23 - (dashData + &80 * 23) - 1 EQUB dashData24 - (dashData + &80 * 24) - 1 \ dashData25 skipped as block 26 \ is larger than block 25 EQUB dashData26 - (dashData + &80 * 26) - 1 \ These match dashDataOffset EQUB dashData27 - (dashData + &80 * 27) - 1 EQUB dashData28 - (dashData + &80 * 28) - 1 EQUB dashData29 - (dashData + &80 * 29) - 1 EQUB dashData30 - (dashData + &80 * 30) - 1 EQUB dashData31 - (dashData + &80 * 31) - 1 EQUB dashData32 - (dashData + &80 * 32) - 1 EQUB dashData33 - (dashData + &80 * 33) - 1 EQUB dashData34 - (dashData + &80 * 34) - 1 EQUB dashData34 - (dashData + &80 * 34) - 1 \ dashData34 repeated, as block \ 35 is smaller than block 34 EQUB dashData35 - (dashData + &80 * 35) - 1 \ These are out by one, so the EQUB dashData36 - (dashData + &80 * 36) - 1 \ X-th entry is the offset for \ block X - 1 \ dashData37 skipped as block 38 \ is larger than block 37 EQUB dashData38 - (dashData + &80 * 38) - 1 \ These match dashDataOffset EQUB dashData39 - (dashData + &80 * 39) - 1 EQUB dashData40 - (dashData + &80 * 40) - 1 \ Not used for the screen buffer EQUB &20, &20 \ These bytes appear to be unused EQUB &20, &42 EQUB &65, &68 EQUB &69
Name: token7 [Show more] Type: Variable Category: Text Summary: Text for recursive token 7 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token7 * tokenLo uses token7
.token7 EQUS "Novice" \ Print "Novice" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81
Name: dashData31 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData31 * fillDataOffset uses dashData31
.dashData31 SKIP 68 EQUB &88, &EA \ These bytes appear to be unused EQUB &EA, &C8 EQUB &EA, &88 EQUB &C8, &EA EQUB &C8, &EA EQUB &EA, &88 EQUB &EA, &C8 EQUB &88, &EA
Name: yLookupLo [Show more] Type: Variable Category: Drawing pixels Summary: Lookup table for converting pixel y-coordinate to low byte of screen address
Context: See this variable on its own page References: This variable is used as follows: * DrawDashboardLine uses yLookupLo * DrawObjectEdge (Part 3 of 5) uses yLookupLo * GetScreenAddress uses yLookupLo

For character rows 0 to 7 and 16 to 31, this table returns the low byte of the screen address of the start of the row, for the custom screen mode. For character rows 8 to 15, the table is reused, as these locations would point to the blue sky, and we don't draw in the sky as it contains working game code. Instead, the lookup table at yLookupLo+8 contains pixel masks for use in the line-drawing routine at DrawDashboardLine.
.yLookupLo FOR I%, 0, 7 EQUB LO(&5800 + (I% * &140)) NEXT EQUB %01110111 \ Clear the first pixel of a mode 5 pixel byte EQUB %10111011 \ Clear the second pixel of a mode 5 pixel byte EQUB %11011101 \ Clear the third pixel of a mode 5 pixel byte EQUB %11101110 \ Clear the fourth pixel of a mode 5 pixel byte EQUB %01110111 \ Clear the first pixel of a mode 5 pixel byte EQUB %10111011 \ Clear the second pixel of a mode 5 pixel byte EQUB %11011101 \ Clear the third pixel of a mode 5 pixel byte EQUB %11101110 \ Clear the fourth pixel of a mode 5 pixel byte FOR I%, 16, 31 EQUB LO(&5800 + (I% * &140)) NEXT EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81, &81 EQUB &81
Name: dashData32 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData32 * fillDataOffset uses dashData32
.dashData32 SKIP 73
Name: driverNames1 [Show more] Type: Variable Category: Text Summary: The first batch of driver names (1 of 5)
Context: See this variable on its own page References: This variable is used as follows: * GetDriverAddress uses driverNames1
.driverNames1 EQUS "Max Throttle" EQUS "Johnny Turbo" EQUS "Davey Rocket" EQUS "Gloria Slap " EQUB &81, &81 \ These bytes appear to be unused EQUB &81
Name: dashData33 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData33 * fillDataOffset uses dashData33
.dashData33 SKIP 77
Name: jumpShallowLeft [Show more] Type: Variable Category: Drawing the track Summary: Branch labels for self-modifying code in the DrawShallowToLeft routine Deep dive: Drawing the track verges
Context: See this variable on its own page References: This variable is used as follows: * DrawShallowToLeft uses jumpShallowLeft
.jumpShallowLeft EQUB shrl19 - shrl2 EQUB shrl17 - shrl2 EQUB shrl15 - shrl2 EQUB shrl13 - shrl2 EQUB shrl10 - shrl2 EQUB shrl8 - shrl2 EQUB shrl6 - shrl2 EQUB shrl4 - shrl2 EQUB shrl18 - shrl2 \ These are not used EQUB shrl16 - shrl2 EQUB shrl14 - shrl2 EQUB shrl12 - shrl2 EQUB shrl9 - shrl2 EQUB shrl7 - shrl2 EQUB shrl5 - shrl2 EQUB shrl3 - shrl2
Name: token33 [Show more] Type: Variable Category: Text Summary: Text for recursive token 33
Context: See this variable on its own page References: This variable is used as follows: * PrintHeader uses token33 * tokenHi uses token33 * tokenLo uses token33

The configurable values below are set in the PrintHeader routine. Deep dive: Text tokens
.token33 EQUB 12 \ Clear text area (clear screen) EQUB 31, 4, 3 \ Move text cursor (configurable, default is column 4, \ row 3) EQUB 200 + 34 \ Print token 34 (double-height, text and colours are \ configurable) EQUB 160 + 10 \ Print spaces (configurable, default is 10 spaces) EQUB 200 + 34 \ Print token 34 (double-height, text and colours are \ configurable) EQUB 31, 36, 2 \ Move text cursor to column 36, row 2 EQUB 255 \ End token
Name: ResetBestLapTime [Show more] Type: Subroutine Category: Drivers Summary: Reset the best lap time to 10:00.0 for a specific driver
Context: See this subroutine on its own page References: This subroutine is called as follows: * MainLoop (Part 3 of 6) calls ResetBestLapTime * ResetBestLapTimes calls ResetBestLapTime

Arguments: X The driver number (0 to 19)
.ResetBestLapTime LDA #0 \ Zero the bestLapTenths entry for driver X STA bestLapTenths,X STA bestLapSeconds,X \ Zero the bestLapSeconds entry for driver X LDA #&10 \ Set the bestLapMinutes for driver X to 10 (as is it a STA bestLapMinutes,X \ BCD number) RTS \ Return from the subroutine EQUB &77 \ This byte appears to be unused
Name: endMirror [Show more] Type: Variable Category: Dashboard Summary: The offset from mirrorAddress for the end of each mirror segment
Context: See this variable on its own page References: This variable is used as follows: * DrawCarInMirror uses endMirror
.endMirror EQUB &C2 \ Mirror segment 0 (left mirror, outer segment) EQUB &C0 \ Mirror segment 1 (left mirror, middle segment) EQUB &BC \ Mirror segment 2 (left mirror, inner segment) EQUB &BC \ Mirror segment 3 (right mirror, inner segment) EQUB &C0 \ Mirror segment 4 (right mirror, middle segment) EQUB &C2 \ Mirror segment 5 (right mirror, outer segment) EQUB &81, &81 \ These bytes appear to be unused EQUB &81
Name: dashData34 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData34 * fillDataOffset uses dashData34
.dashData34 SKIP 77
Name: driverNames2 [Show more] Type: Variable Category: Text Summary: The second batch of driver names (2 of 5)
Context: See this variable on its own page References: No direct references to this variable in this source file
.driverNames2 EQUS "Hugh Jengine" EQUS "Desmond Dash" EQUS "Percy Veer " EQUS "Gary Clipper" EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81
Name: dashData35 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData35 * fillDataOffset uses dashData35
.dashData35 SKIP 76
Name: PrintHeader [Show more] Type: Subroutine Category: Text Summary: Configure and print a double-height header in screen mode 7 Deep dive: Text tokens
Context: See this subroutine on its own page References: This subroutine is called as follows: * GetWingSettings calls PrintHeader * MainLoop (Part 1 of 6) calls PrintHeader * PrintDriverTable calls PrintHeader * PrintToken calls PrintHeader

Prints a token as a double-height header, with the position and colours given in the header tables, and a specific number of spaces between the top and bottom parts of the double-height text (to ensure they line up). The tokens are formatted as follows: * Token 0 ("FORMULA 3 CHAMPIONSHIP") Column 4, row 3 Yellow on red 10 spaces * Token 1 (" POINTS ") Column 7, row 0 Yellow on red 15 spaces * Token 2 ("GRID POSITIONS") Column 9, row 0 White on magenta 19 spaces * Token 3 ("ACCUMULATED POINTS") Column 7, row 0 White on blue 15 spaces * Token 4 ("REVS REVS REVS") Column 0, row 4 The colours of each letter in REVS are magenta/yellow/cyan/green 2 spaces This token actually prints characters 141, 163, 157, 127 before printing token 4 (which it does twice, one for each part of the header). 127 is the DEL character, so this is the same as printing just 141 and 163, which sets double-height, and then shows graphics character 163. This latter character will blank as we are still in alphanumeric text mode... so overall this just displays a double-height token 4, as token 4 contains all the colour information for the individual letters * Token 5 ("THE PITS") Column 11, row 4 Blue on yellow 24 spaces * Token 6 (" BEST LAP TIMES ") Column 7, row 0 White on magenta 15 spaces
Arguments: X The number of the token to print as a double-height header
.PrintHeader LDA xHeader,X \ Set the x-coordinate for the text in token 33 STA token33+2 LDA yHeader,X \ Set the y-coordinate for the text in token 33 STA token33+3 LDA headerSpaces,X \ Set the number of spaces in token 33 STA token33+5 LDA headerBackground,X \ Set the background colour in token 34 STA token34+1 LDA headerForeground,X \ Set the foreground colour in token 34 STA token34+3 TXA \ Set the token embedded in token 34 to token X CLC ADC #200 STA token34+4 LDX #33 \ Print token 33, which prints token 34 in double-height JSR PrintToken \ text with the colours and position configured above RTS \ Return from the subroutine EQUB &79, &7B \ These bytes appear to be unused EQUB &7C, &7D EQUB &7E
Name: token1 [Show more] Type: Variable Category: Text Summary: Text for recursive token 1 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token1 * tokenLo uses token1
.token1 EQUB 160 + 5 \ Print 5 spaces EQUB 200 + 51 \ Print token 51 (" POINTS") EQUB 160 + 6 \ Print 6 spaces EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81
Name: dashData36 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData36 * fillDataOffset uses dashData36
.dashData36 SKIP 58
Name: driverNames3 [Show more] Type: Variable Category: Text Summary: The third batch of driver names (3 of 5)
Context: See this variable on its own page References: No direct references to this variable in this source file
.driverNames3 EQUS "Willy Swerve" EQUS "Sid Spoiler " EQUS "Billy Bumper" EQUS "Slim Chance "
Name: token40 [Show more] Type: Variable Category: Text Summary: Text for recursive token 40 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token40 * tokenLo uses token40
.token40 EQUS "Lap Time" \ Print "Lap Time" EQUB 160 + 3 \ Print 3 spaces EQUS ":" \ Print ":" EQUB 160 + 9 \ Print 9 spaces EQUS "Best Time" \ Print "Best Time" EQUB 160 + 8 \ Print 8 spaces EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81, &81
Name: dashData37 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData37 * ShowStartingLights uses dashData37
.dashData37 SKIP 52
Name: PrintGearNumber [Show more] Type: Subroutine Category: Text Summary: Print the number of the current gear in double-width characters on the gear stick
Context: See this subroutine on its own page References: This subroutine is called as follows: * ProcessDrivingKeys (Part 6 of 6) calls PrintGearNumber * ResetVariables calls PrintGearNumber

Arguments: X The gear number to print on the stick: * 0 = reverse * 1 = neutral * 2-7 = 1 to 5
.PrintGearNumber LDA #34 \ Move the cursor to character column 34 STA xCursor STA W \ Set W to a non-zero value with bit 7 clear, so the \ call to PrintCharacter-6 prints the left half of the \ double-width character LDA #215 \ Move the cursor to pixel row 215 STA yCursor LDX gearNumber \ Set X to the current gear number LDA gearNumberText,X \ Set A to the character to print for this gear number JSR PrintCharacter-6 \ Print the left half of the double-width character LDX #&FF \ Set W to a non-zero value with bit 7 set, so the STX W \ call to PrintCharacter-6 prints the right half of the \ double-width character JSR PrintCharacter-6 \ Print the right half of the double-width character RTS \ Return from the subroutine
Name: ResetBestLapTimes [Show more] Type: Subroutine Category: Drivers Summary: Reset the best lap times to 10:00.0 for all drivers
Context: See this subroutine on its own page References: This subroutine is called as follows: * MainLoop (Part 1 of 6) calls ResetBestLapTimes * MainLoop (Part 3 of 6) calls ResetBestLapTimes * MainLoop (Part 5 of 6) calls ResetBestLapTimes
.ResetBestLapTimes LDX #19 \ We are about to reset the current lap times for \ all 20 drivers, so set a driver counter in X .rall1 JSR ResetBestLapTime \ Reset the best lap time to 10:00.0 for driver X DEX \ Decrement the driver counter BPL rall1 \ Loop back to reset the next set of bytes until we have \ reset all 20 drivers RTS \ Return from the subroutine
Name: token52 [Show more] Type: Variable Category: Text Summary: Text for recursive token 52 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token52 * tokenLo uses token52
.token52 EQUB 133 \ Set foreground colour to magenta alphanumeric EQUS "R" \ Print "R" EQUB 131 \ Set foreground colour to yellow alphanumeric EQUS "E" \ Print "E" EQUB 134 \ Set foreground colour to cyan alphanumeric EQUS "V" \ Print "V" EQUB 130 \ Set foreground colour to green alphanumeric EQUS "S" \ Print "S" EQUB 255 \ End token EQUB &75, &75 \ These bytes appear to be unused
Name: token28 [Show more] Type: Variable Category: Text Summary: Text for recursive token 28 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token28 * tokenLo uses token28
.token28 EQUB 200 + 54 \ Print token 54 ("FORMULA 3 CHAMPIONSHIP" header) EQUB 200 + 35 \ Print token 35 (cyan, move cursor to prompt position) EQUB 160 + 6 \ Print 6 spaces EQUB 200 + 10 \ Print token 10 ("SELECT ") EQUS "NUMBER OF LAPS" \ Print "NUMBER OF LAPS" EQUB 200 + 36 \ Print token 36 (menu option 1 with "PRESS" prompt) EQUB 200 + 18 \ Print token 18 (" 5") EQUB 200 + 14 \ Print token 14 (" laps") EQUB 200 + 37 \ Print token 37 (menu option 2) EQUB 200 + 19 \ Print token 19 ("10") EQUB 200 + 14 \ Print token 14 (" laps") EQUB 200 + 38 \ Print token 38 (menu option 3) EQUB 200 + 20 \ Print token 20 ("20") EQUB 200 + 14 \ Print token 14 (" laps") EQUB 255 \ End token
Name: dashData38 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData38 * fillDataOffset uses dashData38
.dashData38 SKIP 52
Name: driverNames4 [Show more] Type: Variable Category: Text Summary: The fourth batch of driver names (4 of 5)
Context: See this variable on its own page References: No direct references to this variable in this source file
.driverNames4 EQUS "Harry Fume " EQUS "Dan Dipstick" EQUS "Wilma Cargo " EQUS "Miles Behind"
Name: token5 [Show more] Type: Variable Category: Text Summary: Text for recursive token 5 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token5 * tokenLo uses token5
.token5 EQUS "THE PITS" \ Print "THE PITS" EQUB 255 \ End token
Name: token36 [Show more] Type: Variable Category: Text Summary: Text for recursive token 36 Deep dive: Text tokens
Context: See this variable on its own page References: This variable is used as follows: * tokenHi uses token36 * tokenLo uses token36
.token36 EQUB 31, 4, 14 \ Move text cursor to column 4, row 14 EQUB 136 \ Set flashing text EQUB 134 \ Set foreground colour to cyan alphanumeric EQUB 200 + 17 \ Print token 17 ("PRESS ") EQUB 31, 5, 16 \ Move text cursor to column 5, row 16 EQUB 132, 157 \ Set background colour to blue EQUB 134 \ Set foreground colour to cyan alphanumeric EQUS "1" \ Print "1" EQUB 160 + 2 \ Print 2 spaces EQUB 156 \ Set background colour to black EQUB 160 + 5 \ Print 5 spaces EQUB 131 \ Set foreground colour to yellow alphanumeric EQUB 255 \ End token
Name: dashData39 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData39 * DrawShallowToRight uses dashData39 * DrawSteepToRight uses dashData39 * fillDataOffset uses dashData39
.dashData39 SKIP 52
Name: Print234DigitBCD [Show more] Type: Subroutine Category: Text Summary: Print a specific driver's accumulated points as a padded two-, three- or four-digit number
Context: See this subroutine on its own page References: This subroutine is called as follows: * PrintDriverTable calls Print234DigitBCD

Print (totalPointsHi totalPointsLo) for driver X as a four-digit number, followed by a space. The first two digits are printed as spaces if the high byte is zero, and the third digit is printed as a space if applicable.
.Print234DigitBCD LDA #2 \ Print two spaces JSR PrintSpaces LDA #%00100000 \ Set G = %00100000, so if we print the high byte and STA G \ the first digit is 0, it will be replaced by a space LDA totalPointsHi,X \ Set A to the X-th totalPointsHi value BNE Print4DigitBCD \ If A is non-zero, jump to Print4DigitBCD to print the \ (totalPointsHi totalPointsLo) for driver X as a \ four-digit number LDA #2 \ Otherwise print two spaces for the first two digits, JSR PrintSpaces \ as the high byte is zero LSR G \ Shift G right one place to give to %00010000, so the \ next call to Print2DigitBCD will print a space for the \ first digit if it is zero BNE Print4DigitBCD+3 \ Jump to Print4DigitBCD+3 to print the second two \ digits in totalPointsLo (this BNE is effectively a JMP \ as the result of the LSR is never zero)
Name: Print4DigitBCD [Show more] Type: Subroutine Category: Text Summary: Print a specific driver's accumulated points as a four-digit number
Context: See this subroutine on its own page References: This subroutine is called as follows: * Print234DigitBCD calls Print4DigitBCD * PrintDriverTable calls Print4DigitBCD * Print234DigitBCD calls via Print4DigitBCD+3

Print (totalPointsHi totalPointsLo) for driver X as a 4-digit number, followed by a space. The second digit is always printed.
Arguments: A Always called with totalPointsHi,X
Other entry points: Print4DigitBCD+3 Do not print the first two digits (i.e. omit printing A)
.Print4DigitBCD JSR Print2DigitBCD \ Print the binary coded decimal (BCD) number in A LDA totalPointsLo,X \ Print the low byte of the total accumulated points for JSR Print2DigitBCD \ driver X, which is a binary coded decimal (BCD) number LDA #1 \ Print a space JSR PrintSpaces RTS \ Return from the subroutine
Name: FlushSoundBuffers [Show more] Type: Subroutine Category: Sound Summary: Flush all four specified sound buffers Deep dive: The engine sounds
Context: See this subroutine on its own page References: This subroutine is called as follows: * CheckForCrash calls FlushSoundBuffers * KillCustomScreen calls FlushSoundBuffers * MainDrivingLoop (Part 4 of 5) calls FlushSoundBuffers * MakeDrivingSounds calls FlushSoundBuffers * ProcessShiftedKeys calls FlushSoundBuffers
.FlushSoundBuffers LDX #3 \ We are about to flush all four sound channel buffers \ (0 to 3), so set a loop counter in X .flub1 JSR FlushSoundBuffer \ Flush the buffer for sound channel X DEX \ Decrement the loop counter BPL flub1 \ Loop back until we have flushed all four buffers RTS \ Return from the subroutine EQUB &00 \ This byte appears to be unused
Name: dashRightEdge [Show more] Type: Variable Category: Dashboard Summary: Storage for the first track pixel byte along the right edge of the dashboard
Context: See this variable on its own page References: This variable is used as follows: * CopyTyreDashEdges uses dashRightEdge * DrawFence (Part 2 of 2) uses dashRightEdge * DrawTrackView (Part 2 of 4) uses dashRightEdge * DrawTrackView (Part 3 of 4) uses dashRightEdge

This table is used to store the track pixel byte that would be shown along the right edge of the dashboard, but which is partially obscured by the edge. This is stored so we can retrieve it when masking the pixel byte with the dashboard edge when we draw the track line that starts at the right edge of the dashboard. There is a byte for each track line from 43 (the track line at the top of the dashboard) down to 3 (the lowest track line, just above where the wing mirror joins the car body). Lines 0 to 2 are not used.
.dashRightEdge EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81 EQUB &81, &81
Name: dashData40 [Show more] Type: Variable Category: Screen buffer Summary: Contains part of the dashboard image that gets moved into screen memory Deep dive: The jigsaw puzzle binary
Context: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData40 * fillDataOffset uses dashData40
.dashData40 SKIP 52
Name: driverNames5 [Show more] Type: Variable Category: Text Summary: The fifth batch of driver names (5 of 5)
Context: See this variable on its own page References: No direct references to this variable in this source file

The last driver name in this batch is used to store the player's name.
.driverNames5 EQUS "Roland Slide" EQUS "Rick Shaw " EQUS "Peter Out " EQUS "Dummy Driver"
Name: objectScaffold [Show more] Type: Variable Category: 3D objects Summary: The scaffold used to construct each object, in a scalable format Deep dive: Scaling objects with scaffolds
Context: See this variable on its own page References: This variable is used as follows: * ScaleObject uses objectScaffold

This table contains an object's scaffold, in a format that supports quick and easy scaling (see the ScaleObject routine). Each object has its own scaffold, which contains all the measurements that we need to build that object. The object is constructed using only measurements from the scaffold, so if we want to scale the object, we can just scale the scaffold. Each object has a number of entries in this table, one for each scaffold measurement, in decreasing order of size (so the largest measurements come first). Each scaffold measurement is in one of these binary formats: %00000ccc %1abbbccc where a = %a, b = %bbb and c = %ccc. The value represented by %00000ccc is: 1 --------- 2^(c - 2) and the value represented by %1abbbccc is: a 1 1 - + --------- + --------- 2 2^(b - 2) 2^(c - 2) In both cases, the result is a multiple of 1/32, so each of these entries represents a fraction of the form n/32. The ScaleObject routine takes the scaffold for a specific object and scales it by multiplying each scaffold measurement by the following: scaleUp ----------- 2^scaleDown The resulting values are stored in the scaledScaffold table, which uses the same structure as the object's section in the objectScaffold table, but contains the scaled scaffold to use when drawing the scaled object.
.objectScaffold \ Object type 0 = 24, 22, 18, 17, 16, 8, 5, 4 EQUB %10011100 \ 1 0 011 100 a = 0 b = 3 c = 4 \ 0/2 + 1/2^1 + 1/2^2 = 24/32 EQUB %11101110 \ 1 1 101 110 a = 1 b = 5 c = 6 \ 1/2 + 1/2^3 + 1/2^4 = 22/32 EQUB %10011110 \ 1 0 011 110 a = 0 b = 3 c = 6 \ 0/2 + 1/2^1 + 1/2^4 = 18/32 EQUB %10011111 \ 1 0 011 111 a = 0 b = 3 c = 7 \ 0/2 + 1/2^1 + 1/2^5 = 17/32 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %00000100 \ 0 0 000 100 c = 4 \ 1/2^2 = 8/32 EQUB %10101111 \ 1 0 101 111 a = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32 EQUB %00000101 \ 0 0 000 101 c = 5 \ 1/2^3 = 4/32 \ Object type 1 = 20, 12, 9, 8, 6, 5, 2, 1 EQUB %10011101 \ 1 0 011 101 a = 0 b = 3 c = 5 \ 0/2 + 1/2^1 + 1/2^3 = 20/32 EQUB %10100101 \ 1 0 100 101 a = 0 b = 4 c = 5 \ 0/2 + 1/2^2 + 1/2^3 = 12/32 EQUB %10100111 \ 1 0 100 111 a = 0 b = 4 c = 7 \ 0/2 + 1/2^2 + 1/2^5 = 9/32 EQUB %00000100 \ 0 0 000 100 c = 4 \ 1/2^2 = 8/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %10101111 \ 1 0 101 111 a = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32 EQUB %00000110 \ 0 0 000 110 c = 6 \ 1/2^4 = 2/32 EQUB %00000111 \ 0 0 000 111 c = 7 \ 1/2^5 = 1/32 \ Object type 2 = 26, 24, 18, 17, 16, 5, 3, 2 EQUB %11100110 \ 1 1 100 110 a = 1 b = 4 c = 6 \ 1/2 + 1/2^2 + 1/2^4 = 26/32 EQUB %10011100 \ 1 0 011 100 a = 0 b = 3 c = 4 \ 0/2 + 1/2^1 + 1/2^2 = 24/32 EQUB %10011110 \ 1 0 011 110 a = 0 b = 3 c = 6 \ 0/2 + 1/2^1 + 1/2^4 = 18/32 EQUB %10011111 \ 1 0 011 111 a = 0 b = 3 c = 7 \ 0/2 + 1/2^1 + 1/2^5 = 17/32 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10101111 \ 1 0 101 111 a = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 EQUB %00000110 \ 0 0 000 110 c = 6 \ 1/2^4 = 2/32 \ Object type 3 = 16, 10, 6, 4, 3, 1 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %00000101 \ 0 0 000 101 c = 5 \ 1/2^3 = 4/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 EQUB %00000111 \ 0 0 000 111 c = 7 \ 1/2^5 = 1/32 \ Object type 4 = 26, 17, 16, 12, 6, 5, 3, 1 EQUB %11100110 \ 1 1 100 110 a = 1 b = 4 c = 6 \ 1/2 + 1/2^2 + 1/2^4 = 26/32 EQUB %10011111 \ 1 0 011 111 a = 0 b = 3 c = 7 \ 0/2 + 1/2^1 + 1/2^5 = 17/32 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10100101 \ 1 0 100 101 a = 0 b = 4 c = 5 \ 0/2 + 1/2^2 + 1/2^3 = 12/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %10101111 \ 1 0 101 111 a = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 EQUB %00000111 \ 0 0 000 111 c = 7 \ 1/2^5 = 1/32 \ Object type 5 = 26, 17, 3 EQUB %11100110 \ 1 1 100 110 a = 1 b = 4 c = 6 \ 1/2 + 1/2^2 + 1/2^4 = 26/32 EQUB %10011111 \ 1 0 011 111 a = 0 b = 3 c = 7 \ 0/2 + 1/2^1 + 1/2^5 = 17/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 \ Object type 6 = 16, 10, 1 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %00000111 \ 0 0 000 111 c = 7 \ 1/2^5 = 1/32 \ Object type 7 = 28, 20, 18, 16, 8 EQUB %11100101 \ 1 1 100 101 a = 1 b = 4 c = 5 \ 1/2 + 1/2^2 + 1/2^3 = 28/32 EQUB %10011101 \ 1 0 011 101 a = 0 b = 3 c = 5 \ 0/2 + 1/2^1 + 1/2^3 = 20/32 EQUB %10011110 \ 1 0 011 110 a = 0 b = 3 c = 6 \ 0/2 + 1/2^1 + 1/2^4 = 18/32 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %00000100 \ 0 0 000 100 c = 4 \ 1/2^2 = 8/32 \ Object type 8 = 18, 16, 3, 2 EQUB %10011110 \ 1 0 011 110 a = 0 b = 3 c = 6 \ 0/2 + 1/2^1 + 1/2^4 = 18/32 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 EQUB %00000110 \ 0 0 000 110 c = 6 \ 1/2^4 = 2/32 \ Object type 9 = 16, 12, 10, 3 EQUB %00000011 \ 0 0 000 011 c = 3 \ 1/2^1 = 16/32 EQUB %10100101 \ 1 0 100 101 a = 0 b = 4 c = 5 \ 0/2 + 1/2^2 + 1/2^3 = 12/32 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %10110111 \ 1 0 110 111 a = 0 b = 6 c = 7 \ 0/2 + 1/2^4 + 1/2^5 = 3/32 \ Object type 10 = 10, 9, 6, 4, 1 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %10100111 \ 1 0 100 111 a = 0 b = 4 c = 7 \ 0/2 + 1/2^2 + 1/2^5 = 9/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %00000101 \ 0 0 000 101 c = 5 \ 1/2^3 = 4/32 EQUB %00000111 \ 0 0 000 111 c = 7 \ 1/2^5 = 1/32 \ Object type 11 = 10, 8, 6, 5 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %00000100 \ 0 0 000 100 c = 4 \ 1/2^2 = 8/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %10101111 \ 1 0 101 111 a = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32 \ Object type 12 = 10, 8, 6, 5 EQUB %10100110 \ 1 0 100 110 a = 0 b = 4 c = 6 \ 0/2 + 1/2^2 + 1/2^4 = 10/32 EQUB %00000100 \ 0 0 000 100 c = 4 \ 1/2^2 = 8/32 EQUB %10101110 \ 1 0 101 110 a = 0 b = 5 c = 6 \ 0/2 + 1/2^3 + 1/2^4 = 6/32 EQUB %10101111 \ 1 0 101 111 c = 0 b = 5 c = 7 \ 0/2 + 1/2^3 + 1/2^5 = 5/32
Name: GetSectionSteering [Show more] Type: Subroutine Category: Tactics Summary: Calculate the optimum steering for each track section Deep dive: Tactics of the non-player drivers
Context: See this subroutine on its own page References: This subroutine is called as follows: * InitialiseDrivers calls GetSectionSteering * MainLoop (Part 2 of 6) calls GetSectionSteering * MainLoop (Part 5 of 6) calls GetSectionSteering

Arguments: X The class of race: * 0 = Novice * 1 = Amateur * 2 = Professional
Returns: X X is unchanged
.GetSectionSteering LDA trackBaseSpeed,X \ Set baseSpeed = the X-th byte of trackBaseSpeed STA baseSpeed \ \ so baseSpeed contains the base speed for cars at the \ chosen class or race, on this track \ \ For Silverstone, this is: \ \ * 134 for Novice \ * 146 for Amateur \ * 152 for Professional STA U \ Set U = baseSpeed LDA trackSectionCount \ Set Y = trackSectionCount >> 3 LSR A \ LSR A \ so Y contains the number of sections in this track LSR A TAY \ Now we copy Y bytes (one per track section) from \ trackSteering to sectionSteering, processing each \ byte as we go (i.e. taking the input from \ trackSteering and storing the result in \ sectionSteering): \ \ * Bit 7 of the result = bit 0 of the input \ \ * Bit 6 of the result = 0 \ \ * Bits 0-5 of the result are: \ \ * A >> 2 * U / 256 if bit 1 of the input is clear \ * A >> 2 if bit 1 of the input is set \ \ where A is the input from trackSteering and U is the \ base speed from above .slin1 LDA trackSteering,Y \ Fetch the Y-th byte from trackSteering as the input LSR A \ Shift bit 0 of the input into the C flag and store it PHP \ on the stack so we can put it into bit 7 of the result LSR A \ Shift bit 1 of the input into the C flag BCS slin2 \ If bit 1 of the input is set, skip the following \ instruction JSR Multiply8x8 \ Bit 1 of the input is clear, so set (A T) = A * U \ \ i.e. A = A * U / 256 .slin2 ASL A \ Set bit 7 of the result to bit 0 of the input PLP ROR A STA sectionSteering,Y \ Store the result in the Y-th byte of sectionSteering DEY \ Decrement the loop counter BPL slin1 \ Loop back until we have processed all Y bytes RTS \ Return from the subroutine
Name: ApplyElevation (Part 1 of 5) [Show more] Type: Subroutine Category: Driving model Summary: Calculate changes in the car's elevation Deep dive: The core driving model Jumps and drops
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ApplyElevation

Calculate the following: * liftFromTorque = the lift of the front of the car caused by accelerating or braking, in the range -5 to +3 A positive value of liftFromTorque means the horizon is drawn lower on the screen, which means the nose is being pulled up. The value of liftFromTorque is only changed if the car is on the ground. The logic is as follows: * Horizon goes down when we are not in reverse, we are applying the throttle and engine torque is non-zero (i.e. we pull the nose of the car up by incrementing liftFromTorque, to a maximum of 3) * Horizon goes up when we are not in reverse, we are applying the brakes and we are moving (i.e. we push the nose of the car down by decrementing liftFromTorque, to a minimum of -5) * Otherwise, gradually bring the nose back to the normal level of zero, with the value incrementing towards zero at twice the rate that it decrements towards zero (so the nose rises at twice the speed that it falls, as the suspension springs are more powerful than gravity)
.ApplyElevation LDA heightAboveTrack \ Set A = heightAboveTrack BEQ elev1 \ If heightAboveTrack = 0, jump to elev1 DEC yJumpHeight \ Set yJumpHeight = yJumpHeight - 2 DEC yJumpHeight JMP elev7 \ Jump to elev7 .elev1 \ If we get here then heightAboveTrack = 0 and A = 0 STA yJumpHeight \ Set yJumpHeight = 0 STA yGravityDelta \ Set yGravityDelta = 0 LDY liftFromTorque \ Set Y = liftFromTorque LDA gearNumber \ If gearNumber = 0, then we are in reverse, so jump to BEQ elev3 \ elev3 LDA throttleBrakeState \ If throttleBrakeState is negative, then there is no BMI elev3 \ brake or throttle key press, so jump to elev3 BEQ elev2 \ If throttleBrakeState = 0, then the brakes are being \ applied, so jump to elev2 \ If we get here then the throttle is being applied LDA engineTorque \ Set A to the engine torque BNE elev4 \ If the engine torque is non-zero, jump to elev4 to \ increment the lift in Y BEQ elev3 \ Jump to elev3 (this BEQ is effectively a JMP as A is \ always zero) .elev2 \ If we get here then we are not in reverse gear and the \ brakes are being applied LDA playerSpeedHi \ Set A = playerSpeedHi BNE elev5 \ If A is non-zero, then we are moving, so jump to elev5 \ to decrement the lift in Y .elev3 \ This part of the routine brings the car nose back \ towards the normal level, if it isn't there already \ \ We get here when any of the following are true: \ \ * In reverse \ \ * Not in reverse, no brake or throttle key press \ \ * Not in reverse, brakes are being applied, we are \ not moving \ \ * Not in reverse, no brakes, throttle is being \ applied, engine torque is zero \ \ In other words, we are not accelerating or braking, so \ we slowly cancel any rise or fall of the car nose that \ we may have applied previously, as follows: \ \ * If the lift in Y is positive, we decrement it \ towards zero via elev5 \ \ * If the lift in Y is negative, we increment it \ towards zero and then increment it again via elev4 \ \ This means the list increments towards zero at twice \ the rate that it decrements towards zero TYA \ Set A = Y \ = liftFromTorque BEQ elev7 \ If liftFromTorque = 0, jump to elev7 to move on to \ part 2 without updating liftFromTorque BPL elev5 \ If liftFromTorque > 0, jump to elev5 to decrement the \ lift in Y \ If we get here then liftFromTorque < 0, so the nose of \ the car is already being pushed up and the horizon is \ lower than it would normally be INY \ Set Y = Y + 1 \ = liftFromTorque + 1 \ \ So this pulls the nose of the car up, making the \ horizon fall back towards the normal level .elev4 \ This part of the routine pulls the nose of the car up \ by incrementing Y (to a maximum of 3), making the \ horizon fall \ \ We jump here if we are not in reverse, we are applying \ the throttle, and engine torque is non-zero INY \ Set Y = Y + 1 \ = liftFromTorque + 1 \ \ So this pulls the nose of the car up, making the \ horizon fall down BMI elev6 \ If Y is negative, jump to elev6 to set liftFromTorque \ to Y CPY #4 \ If Y < 4, jump to elev6 to set liftFromTorque = Y BCC elev6 LDY #3 \ Set Y = 3, so Y has a maximum value of 3 BCS elev6 \ Jump to elev6 (this BCS is effectively a JMP as we \ just passed through a BCC) .elev5 \ This part of the routine pushes the nose of the car \ down by decrementing Y (to a minimum of -5), making \ the horizon rise \ \ We jump here if we are not in reverse, we are applying \ the brakes, and we are moving DEY \ Set Y = Y - 1 \ \ So this pushes the nose of the car down, making the \ horizon rise up BPL elev6 \ If Y is positive, jump to elev6 to set liftFromTorque \ to Y CPY #&FB \ If Y >= -5, jump to elev6 to set liftFromTorque = Y BCS elev6 LDY #&FB \ Set Y = -5, so Y has a minimum value of -5 .elev6 STY liftFromTorque \ Set liftFromTorque = Y
Name: ApplyElevation (Part 2 of 5) [Show more] Type: Subroutine Category: Driving model Summary: Calculate the player's heading and sideways flag Deep dive: The core driving model Jumps and drops
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Calculate the following: * playerHeading, which is the player's yaw angle, relative to the direction of the track, where a heading of 0 means the player is pointing straight along the track * playerSideways, which contains the following information: * If playerSideways < 40, then the player's car is facing forwards or backwards along the track * If playerSideways >= 40, then the player's car is facing sideways, relative to the direction of the track
.elev7 LDX playerSegmentIndex \ Set X to the number of the player's track segment from \ the track segment buffer LDY segmentVector,X \ Fetch the segment vector number for track segment X, \ which gives us the number of the segment vector for \ the track segment containing the player LDA playerPitchAngle \ Set V = playerPitchAngle STA V LDA xTrackSegmentI,Y \ Store the sign of the segment vector's x-coordinate EOR zTrackSegmentI,Y \ multiplied by the segment vector's z-coordinate on the PHP \ stack, i.e. xTrackSegmentI * zTrackSegmentI LDA zTrackSegmentI,Y \ Set A to the segment vector's z-coordinate in \ zTrackSegmentI PHP \ Store the sign of zTrackSegmentI on the stack JSR Absolute8Bit \ Set A = |A| \ = |zTrackSegmentI| CMP #60 \ Store the comparison of |zTrackSegmentI| and 60 on the PHP \ stack BCC elev8 \ If |zTrackSegmentI| < 60, jump to elev8 LDA xTrackSegmentI,Y \ Set A to the segment vector's x-coordinate JSR Absolute8Bit \ Set A = |A| \ = |xTrackSegmentI| .elev8 STA T \ Set T = A \ = |zTrackSegmentI| if |zTrackSegmentI| < 60 \ |xTrackSegmentI| if |zTrackSegmentI| >= 60 LSR A \ Set A = (A / 2 + T) / 4 CLC \ = (A / 2 + A) / 4 ADC T \ = A * 3 / 8 LSR A LSR A PLP \ Pull the result of the comparison of |zTrackSegmentI| \ and 60 from the stack BCS elev9 \ If |zTrackSegmentI| >= 60, jump to elev9 EOR #%00111111 \ We know bits 6 and 7 of A are clear, as we just \ shifted A to the right twice, so this flips bits 0 to \ 5, so the range 0 to 63 gets flipped around to 63 to 0 .elev9 PLP \ Pull the sign of zTrackSegmentI from the stack BPL elev10 \ If zTrackSegmentI is positive, jump to elev10 EOR #%10000000 \ Flip the bit 7 of A, so the range 0 to 63 gets \ changed to -65 to -128 .elev10 PLP \ Pull the sign of xTrackSegmentI * zTrackSegmentI from \ the stack JSR Absolute8Bit \ Set the sign of A to the sign of xTrackSegmentI * \ zTrackSegmentI SEC \ Set A = A - playerYawAngleHi SBC playerYawAngleHi STA playerHeading \ Set playerHeading = A \ A is an angle that represents the player's heading, \ relative to the current segment, like this: \ \ 0 \ -32 | +32 Overhead view of car \ \ | / \ \ | / 0 = looking straight ahead \ \|/ +64 = looking sharp right \ -64 -----+----- +64 -64 = looking sharp left \ /|\ \ / | \ \ / | \ \ -96 | +96 \ 128 \ \ An angle of 0 means our car is facing forwards along \ the track, while an angle of +32 means we are facing \ 45 degrees to the right of straight on, and an angle \ of 128 means we are facing backwards along the track BPL elev11 \ If A is positive, jump to elev11 to skip the following EOR #&FF \ Invert A, so this effectively reflects the angle into \ the right half of the above diagram: \ \ 0 \ | 32 \ | / \ | / \ |/ \ +----- 64 \ |\ \ | \ \ | \ \ | 96 \ 127 .elev11 CMP #64 \ If A < 64, then the player's heading is forwards, so BCC elev12 \ jump to elev12 EOR #%01111111 \ A >= 64, so the player's heading is backwards and in \ the bottom-right quadrant, so flip bits 0-6 of A, \ changing the range of the bottom-right quadrant from \ 64 to 127 to 63 to 0, to give this: \ \ 0 \ | 32 \ | / \ | / \ |/ \ +----- 63 \ |\ \ | \ \ | \ \ | 32 \ 0 \ \ If the value in A is greater than 40, then we are \ looking sideways compared to the track direction, and \ conversely, if A is less than 40, we are looking \ forwards or backwards .elev12 STA playerSideways \ Store the value of A in playerSideways, so we can test \ whether the player is facing sideways on the track
Name: ApplyElevation (Part 3 of 5) [Show more] Type: Subroutine Category: Driving model Summary: Calculate player's elevation above the track Deep dive: The core driving model Jumps and drops
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Calculate the following: * Set A to the elevation change of the car due to the sideways angle of the car in the current segment * playerPitchAngle = (A + bumpyGrassHeight + liftFromTorque + yJumpHeight + playerPitchAngle) / 2 * spinPitchAngle = playerPitchAngle - original value of playerPitchAngle
EOR #%00111111 \ We know bits 6 and 7 of A are clear, as we know A is \ in the range 0 to 63, so this flips bits 0 to 5, so \ the range 0 to 63 gets flipped around to 63 to 0, to \ give this in A: \ \ 63 \ | 32 \ | / \ | / \ |/ \ +----- 0 \ |\ \ | \ \ | \ \ | 32 \ 63 STA T \ Store the flipped range in T LSR A \ Set A = A / 2 + T CLC \ = 1.5 * T ADC T \ \ So we now have this in A, depending on the heading of \ the player within the segment: \ \ 94 \ | 48 \ | / \ | / \ |/ \ +----- 0 \ |\ \ | \ \ | \ \ | 48 \ 94 JSR MultiplyHeight \ Set: \ \ A = A * yTrackSegmentI \ \ flipping the sign if we are facing backwards \ \ The value given in yTrackSegmentI is the y-coordinate \ of the segment vector, i.e. the vector from this \ segment to the next, which is the same as the change \ in height as we move through the segment \ \ The calculation above effectively works out the \ difference in elevation of the car within the segment \ with respect to its heading, and puts it in A CLC \ Set A = A + bumpyGrassHeight + liftFromTorque ADC bumpyGrassHeight \ + yJumpHeight + playerPitchAngle CLC ADC liftFromTorque CLC ADC yJumpHeight CLC ADC playerPitchAngle CLC \ Clear the C flag, to use when A is positive BPL elev13 \ If A is positive, jump to elev13 SEC \ Set to C flag, so it contains the correct sign bit \ for the value of A .elev13 ROR A \ Set A = A / 2, keeping the sign of A intact STA playerPitchAngle \ Set playerPitchAngle = A SEC \ Set spinPitchAngle = A - V SBC V \ = playerPitchAngle - V STA spinPitchAngle \ \ We set V to the original value of playerPitchAngle \ above, so spinPitchAngle now contains the change in \ elevation of the car
Name: ApplyElevation (Part 4 of 5) [Show more] Type: Subroutine Category: Driving model Summary: Calculate the height of the car above the track Deep dive: The core driving model Jumps and drops
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Calculate the following: * yGravityDelta = max(yGravityDelta - 4, -56) * If the car falls past ground level, calculate the following to make the car bounce upwards by half the rate that it's falling: * yGravityDelta = |yGravityDelta| / 2 * yJumpHeight = |yGravityDelta| / 4 * spinYawAngleHi = spinYawAngleHi >> 1 with bit 7 set * heightAboveTrack = 1 and make the crash/contact sound, otherwise set: heightAboveTrack = heightAboveTrack + yGravityDelta and clip the result to the range 0 to 127
LDA #0 \ Set W = 0, to use as the high byte of (W A) below STA W LDA yGravityDelta \ Set A = yGravityDelta - 4 SEC \ SBC #4 \ This applies the effect of gravity on the car, which \ makes the car fall faster all the time that it's \ falling BVC elev14 \ The overflow flag is set if yGravityDelta is negative LDA #&C8 \ but the result is positive, in which case we set \ A = -56, so this sets a minimum value for A of -56: \ \ A = max(yGravityDelta - 4, -56) .elev14 STA yGravityDelta \ Set yGravityDelta = A \ = max(yGravityDelta - 4, -56) CLC \ Set A = A + heightAboveTrack ADC heightAboveTrack \ = max(yGravityDelta - 4, -56) + heightAboveTrack BEQ elev15 \ If A = 0, jump to elev15 BVS elev17 \ The overflow flag is set if: \ \ * yGravityDelta + heightAboveTrack > 127 \ and both are < 128 \ (i.e. adding two positives gives a negative) \ \ * yGravityDelta + heightAboveTrack < 128 \ and both are > 127 \ (i.e. adding two negatives gives a positive) \ \ In either case, jump to elev17 to set \ heightAboveTrack = 127 BPL elev18 \ If A is positive, jump to elev18 to set \ heightAboveTrack = A .elev15 LDA yGravityDelta \ Set A = yGravityDelta JSR Absolute8Bit \ Set A = |A| \ = |yGravityDelta| CMP #5 \ If A < 5, jump to elev16 to set heightAboveTrack = 0 BCC elev16 \ If we get here then the car has fallen past ground \ level, so we need to bounce it upwards JSR ApplyBounce \ Calculate the following to make the car bounce upwards \ by half the rate that it's falling: \ \ yGravityDelta = |yGravityDelta| / 2 \ \ yJumpHeight = |yGravityDelta| / 4 \ \ heightAboveTrack = heightAboveTrack + 1 \ \ spinYawAngleHi = spinYawAngleHi >> 1 with bit 7 set \ \ and make the crash/contact sound LDA #1 \ Set A = 1, to use as the value of heightAboveTrack, \ overriding the calculation we just did BNE elev18 \ Jump to elev18 (this BNE is effectively a JMP as A is \ never zero) .elev16 LDA #0 \ Set A = 0, to use as the value of heightAboveTrack BEQ elev18 \ Jump to elev18 (this BEQ is effectively a JMP as A is \ always zero) .elev17 LDA #127 \ Set A = 127, to use as the value of heightAboveTrack .elev18 STA heightAboveTrack \ Store the value of A in heightAboveTrack
Name: ApplyElevation (Part 5 of 5) [Show more] Type: Subroutine Category: Driving model Summary: Calculate the player's 3D y-coordinate and progress speed Deep dive: The core driving model Jumps and drops
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Calculate the following: * (yPlayerCoordTop yPlayerCoordHi) = (ySegmentCoordIHi ySegmentCoordILo) + carProgress * yTrackSegmentI + heightAboveTrack * 4 + 172 * carSpeedHi for the player's car = playerSpeedHi * 2.13
ASL A \ Set (W A) = (0 A) << 2 ROL W \ = (W A) << 2 ASL A \ = heightAboveTrack << 2 ROL W \ = heightAboveTrack * 4 STA V \ Set (W V) = (W A) \ = heightAboveTrack * 4 LDX currentPlayer \ Set X to the driver number of the current player LDA carProgress,X \ Set A to the lowest byte of the player's progress \ through the current segment JSR MultiplyHeight \ Set: \ \ A = A * yTrackSegmentI \ = carProgress * yTrackSegmentI \ \ flipping the sign if we are facing backwards \ \ The value given in yTrackSegmentI is the y-coordinate \ of the segment vector, i.e. the vector from this \ segment to the next, which is the same as the change \ in height as we move through the segment \ \ The calculation above effectively works out the \ difference in elevation of the car as it progresses \ through the segment, and puts it in A BPL elev19 \ If A is positive, jump to elev19 to skip the following \ instruction DEC W \ A is negative, so set W = W - 1 .elev19 LDY playerSegmentIndex \ Set Y to the index of the player's track segment from \ the track segment buffer CLC \ Set: ADC ySegmentCoordILo,Y \ \ A = A + ySegmentCoordILo \ = carProgress * yTrackSegmentI + ySegmentCoordILo \ \ ySegmentCoordILo is the low byte of the 3D \ y-coordinate for the player's track segment \ \ So A now contains the segment's y-coordinate, plus the \ elevation of the car due to the track's progress \ through the segment PHP \ Store the C flag on the stack, which will be set if \ the addition just overflowed CLC \ Set A = A + 172 ADC #172 PHP \ Store the C flag on the stack, which will be set if \ the addition just overflowed \ We now calculate the following for the y-coordinate of \ the player's 3D coordinates: \ \ (yPlayerCoordTop yPlayerCoordHi) \ \ = A \ + (W V) \ + (ySegmentCoordIHi 0) \ + C flag from the first addition \ + C flag from the second addition \ \ = carProgress * yTrackSegmentI + ySegmentCoordILo \ + 172 \ + heightAboveTrack * 4 \ + (ySegmentCoordIHi 0) \ + C flag from the first addition \ + C flag from the second addition \ \ = carProgress * yTrackSegmentI \ + 172 \ + heightAboveTrack * 4 \ + (ySegmentCoordIHi ySegmentCoordILo) \ \ with all the correct carry bits included CLC \ We start with the low bytes ADC V STA yPlayerCoordHi LDA ySegmentCoordIHi,Y \ And then the high bytes ADC W PLP ADC #0 PLP ADC #0 STA yPlayerCoordTop LDA playerSpeedHi \ Set U = playerSpeedHi, which is the player's speed in STA U \ mph LDA #33 \ Set A = 33 JSR Multiply8x8 \ Set (A T) = A * U \ = playerSpeedHi * 33 \ \ So A = playerSpeedHi * 33 / 256 ASL U \ Set A = A + U * 2 CLC \ = playerSpeedHi * 33 / 256 + playerSpeedHi * 2 ADC U \ = playerSpeedHi * 2.13 STA carSpeedHi,X \ Set carSpeedHi for the player's car to playerSpeedHi * \ 2.13 \ \ carSpeedHi is now the speed of the car in terms of \ 1/256-ths of a segment, with 120 mph in playerSpeedHi \ corresponding to a whole segment in carSpeedHi (as \ 120 * 2.13 = 255) RTS \ Return from the subroutine
Name: MultiplyHeight [Show more] Type: Subroutine Category: Track geometry Summary: Multiply the height above ground of a specified track segment by A
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyElevation (Part 3 of 5) calls MultiplyHeight * ApplyElevation (Part 5 of 5) calls MultiplyHeight

For segment vector Y, calculate: A = A * yTrackSegmentI flipping the sign if we are facing backwards.
Arguments: A The number to multiply the height by Y Index of the segment vector to multiply
Returns: N flag Set according to the result in A
Other entry points: MultiplyHeight+11 Set A = A * U while retaining the sign in A (the sign of A must be on the stack before calling this entry point)
.MultiplyHeight STA U \ Set U to the multiplication factor in A LDA yTrackSegmentI,Y \ Store the sign of the height * directionFacing on EOR directionFacing \ the stack PHP LDA yTrackSegmentI,Y \ Set A to the track height in the Y-th yTrackSegmentI JSR Absolute8Bit \ Set A = |A| \ = |yTrackSegmentI| JSR Multiply8x8 \ Set (A T) = A * U \ = U * |yTrackSegmentI| PLP \ Set the N flag to the sign of yTrackSegmentI * \ directionFacing, so this will be set if the height \ sign is different to bit 7 of directionFacing, clear \ if the height sign matches bit 7 of directionFacing JSR Absolute8Bit \ Give A the sign in the N flag RTS \ Return from the subroutine
Name: MovePlayerOnTrack [Show more] Type: Subroutine Category: Car geometry Summary: Update the position of the player's car within the current track segment Deep dive: Placing cars on the track
Context: See this subroutine on its own page References: This subroutine is called as follows: * MainDrivingLoop (Part 2 of 5) calls MovePlayerOnTrack

This routine sets the racing line and progress for the player's car.
.MovePlayerOnTrack \ We start by calculating the player's heading within \ the current segment, so we know which direction the \ car is heading in, and therefore how to alter the \ car's position with the track segment LDA edgeYawAngle \ Set A = edgeYawAngle - playerHeading SEC \ SBC playerHeading \ So A contains the yaw angle of the closest segment, \ from the point of view of the player's car JSR Absolute8Bit \ Set A = |A| \ \ So the angle is now like this: \ \ 0 \ | 32 \ | / \ | / \ |/ \ +----- 64 \ |\ \ | \ \ | \ \ | 96 \ 127 CMP #64 \ If A < 64, clear the C flag, if A >= 64, set the C \ flag ROR playerPastSegment \ Store the C flag in bit 7 of playerPastSegment, so \ this will be set if the yaw angle is >= 64, which is \ 90 degrees \ \ So bit 7 will be set if the closest segment is at a \ yaw angle of more than 90 degrees from the point of \ view of the player - in other words, when the player \ has driven past the segment BPL mseg1 \ If bit 7 of playerPastSegment is clear, i.e. the C \ flag is clear, then A < 64 and the player has not gone \ past the closest segment, so jump to mseg1 \ The player has gone past the closest segment, so A is \ in the range 64 to 127, with the higher figure \ indicating that the segment is way behind us, so we \ now flip that around to the range 63 to 0, where 0 \ indicates that the segment is way behind us, and 63 \ indicates that we've just passed it \ \ We use this value later to calculate the player's \ carProgress EOR #%01111111 \ Negate A using two's complement, leaving bit 7 alone CLC \ to leave the result in the range 63 to 0, so the angle ADC #1 \ is now like this: \ \ 0 \ | 32 \ | / \ | / \ |/ \ +----- 64 \ |\ \ | \ \ | \ \ | 32 \ 0 \ \ So the value of A is zero if the player's car is \ pointing straight along the track segment, or 64 if \ the car is pointing sideways, towards the verge .mseg1 PHA \ Store the player's direction in A on the stack, which \ we retrieve below to use when calculating carProgress \ for the player's car \ We now calculate the carRacingLine value for the \ player's car, which is the left-right position within \ the track LDY #186 \ Set A = edgeDistanceLo * (A' / 256) * (186 / 256) JSR GetCarInSegment \ \ where A' is A, scaled by the ScaleCarInSegment routine LDX edgeSegmentPointer \ Set X to the index of the segment within the track \ verge buffer that is closest to the player's car CPX #40 \ If X < 40, then the segment is along the inner edge of BCC mseg2 \ the track, so jump to mseg2 to skip the following \ instruction EOR #&FF \ Set A = ~A .mseg2 LDX currentPlayer \ Set X to the driver number of the current player BIT directionFacing \ Set the sign of A to that of directionFacing, so A JSR Absolute8Bit \ gets negated when we are facing backwards \ The value in A is the updated value of carRacingLine \ for the player, but before we store it, we need to use \ it to calculate the player's drift PHA \ Store A on the stack, so we can retrieve bit below \ when storing it as the updated carRacingLine for the \ player's car \ Next, we set bit 7 of playerDrift according to the \ amount of sideways movement of the player's car, with \ a set bit 7 meaning the player's car is drifting \ sideways by more than 22 in this iteration around the \ main driving loop (where the full track width is 256) SBC carRacingLine,X \ Set A = A - the racing line for the player \ \ So A contains the distance that the car has moved in \ terms of racing line, i.e. left to right BCS mseg3 \ If the subtraction didn't underflow, jump to mseg3 to \ skip the following instruction EOR #&FF \ If we get here, then A < then player's racing line and \ the subtraction underflowed, so set A = ~A to make \ A positive, so A = |A| .mseg3 CMP #22 \ If A < 22, clear the C flag, if A >= 22, set the C \ flag IF _ACORNSOFT OR _4TRACKS ROR playerDrift \ Store the C flag in bit 7 of playerDrift, so this will \ be set if A >= 22 ELIF _SUPERIOR OR _REVSPLUS JSR SetPlayerDriftSup \ Set bit 7 of playerDrift if both A >= 22 and the \ objSectionSegmt for the player is >= 3, so the \ Superior version does not record drift in the first \ three segments of a new track section ENDIF PLA \ Set carRacingLine,X to the second value we stored on STA carRacingLine,X \ the stack above \ Finally, we calculate the carProgress value for the \ player's car, which is the progress within the segment PLA \ Set A to the first value we stored on the stack above, \ which is the player's direction, where A is zero if \ the player's car is pointing straight along the track \ segment, or 64 if the car is pointing sideways, \ towards the verge EOR #&FF \ Set A = 64 - A CLC \ ADC #65 \ This works because ~A = -A - 1, so we have: \ \ A = ~A + 65 \ = -A - 1 + 65 \ = 64 - A \ \ So A is now in the range 0 to 64, where 64 means the \ car is pointing forwards along the segment, and 0 \ means it's pointing sideways LDY #136 \ Set A = edgeDistanceLo * (A' / 256) * (136 / 256) JSR GetCarInSegment \ \ where A' is A, scaled by the ScaleCarInSegment routine ASL A \ Set A = A * 2 ASL A BIT playerPastSegment \ If bit 7 of playerPastSegment is set, jump to mseg4 BMI mseg4 EOR #&FF \ Set A = ~A .mseg4 STA carProgress,X \ Set the car's progress to A RTS \ Return from the subroutine
Name: GetCarInSegment [Show more] Type: Subroutine Category: Car geometry Summary: Calculate the player car's position within the current segment
Context: See this subroutine on its own page References: This subroutine is called as follows: * MovePlayerOnTrack calls GetCarInSegment

This routine scales the value in A as follows: edgeDistanceLo * (A' / 256) * (Y / 256) where A' is A, scaled by the ScaleCarInSegment routine as follows: When Calculate Range of result A < 26 A' = 3 * A 0 to 78 26 <= A < 46 A' = 4 * A + 52 156 to 232 A >= 46 A' = A + 190 236 to 253
Arguments: A The number to be scaled Y Called with Y = 136 or 186
Returns: A edgeDistanceLo * A+ * (Y / 256)
.GetCarInSegment JSR ScaleCarInSegment \ Set U = A, scaled up by ScaleCarInSegment (let's call STA U \ it A') TYA \ Set (A T) = A * U JSR Multiply8x8 \ = Y * A' STA U \ Set (U T) = (A T) \ = Y * A' LDA edgeDistanceLo \ Set (A T) = A * U JSR Multiply8x8 \ = edgeDistanceLo * U \ = edgeDistanceLo * (Y * A' / 256) \ = edgeDistanceLo * A' * (Y / 256) \ \ So A = (A T) / 256 \ = edgeDistanceLo * (A' / 256) * (Y / 256) RTS \ Return from the subroutine
Name: ScaleCarInSegment [Show more] Type: Subroutine Category: Car geometry Summary: Work out how far a car is within a segment by scaling the angle in which the car is pointing
Context: See this subroutine on its own page References: This subroutine is called as follows: * GetCarInSegment calls ScaleCarInSegment

This routine takes a car's heading within a segment, as follows: 0 | 26 | / | / _- 46 |/_-´ +--------- 64 and scales it like this: When Calculate Range of result A < 26 A' = 3 * A 0 to 78 26 <= A < 46 A' = 4 * A + 52 156 to 232 A >= 46 A' = A + 190 236 to 253
Arguments: A A is in the range 0 to 63
Returns: A The scaled value of A, shown as A' above
.ScaleCarInSegment CMP #26 \ If A < 26, jump to scar2 BCC scar2 CMP #46 \ If A < 46, jump to scar1 BCC scar1 \ If we get here then A >= 46 CLC \ Set A = A + 190 ADC #190 RTS \ Return from the subroutine .scar1 \ If we get here then 26 <= A < 46 ASL A \ Set A = A << 2 + 52 ASL A \ = 4 * A + 52 CLC ADC #52 RTS \ Return from the subroutine .scar2 \ If we get here then A < 26 STA T \ Set T = A ASL A \ Set A = A << 1 + T CLC \ = A * 2 + A ADC T \ = 3 * A ASL A RTS \ Return from the subroutine
Name: ApplyDrivingModel [Show more] Type: Subroutine Category: Driving model Summary: Apply the driving model to the player's car Deep dive: An overview of the driving model The core driving model
Context: See this subroutine on its own page References: This subroutine is called as follows: * MainDrivingLoop (Part 2 of 5) calls ApplyDrivingModel
.ApplyDrivingModel LDA playerYawAngleHi \ Set (A X) = (playerYawAngleHi playerYawAngleLo) LDX playerYawAngleLo JSR GetRotationMatrix \ Calculate the rotation matrix for rotating the \ player's yaw angle into the global 3D coordinate \ system, as follows: \ \ [ cosYawAngle 0 -sinYawAngle ] \ [ 0 1 0 ] \ [ sinYawAngle 0 cosYawAngle ] JSR RotateCoordToCar \ Rotate the player's delta vector from the 3D world \ coordinate system to the frame of reference of the \ player's car, putting the result into: \ \ [ xVelocity ] \ [ - ] \ [ zVelocity ] \ \ We ignore the y-coordinate as the calculations are \ all done along the ground LDA xVelocityLo \ Set (xPrevVelocityHi xPrevVelocityLo) STA xPrevVelocityLo \ = (xVelocityHi xVelocityLo) LDA xVelocityHi STA xPrevVelocityHi LDA zVelocityLo \ Set (A T) = (zVelocityHi zVelocityLo) STA T LDA zVelocityHi JSR Absolute16Bit \ Set (A T) = |A T| \ = |zVelocity| STA playerSpeedHi \ Set (playerSpeedHi playerSpeedLo) = (A T) LDA T \ = |zVelocity| STA playerSpeedLo LDY playerSpeedHi \ Set Y to the high byte of (playerSpeedHi \ playerSpeedLo) BNE dmod1 \ If the high byte is non-zero, jump to dmod1 to skip \ the following AND #%11110000 \ A contains playerSpeedLo, so this sets Y to the high TAY \ nibble of the low byte of (playerSpeedHi \ playerSpeedLo) .dmod1 STY playerMoving \ Store Y in playerMoving, which is zero if the player \ is not moving, non-zero if they are, so this denotes \ the player as moving if (playerSpeedHi playerSpeedLo) \ is non-zero, ignoring the bottom nibble of the \ low byte JSR ApplySpinYaw \ Calculate the following: \ \ xVelocity = xVelocity - (spinYawAngle * 0.34) \ \ xSpinVelocity = spinYawAngle * 0.52 JSR ApplyGrassOrTrack \ Apply the effects of driving and braking, depending on \ the current driving surface (grass or track) JSR ApplyEngine \ Apply the effects of the engine LDX #1 \ Set X = 1, so the call to ApplyTyresAndSkids processes \ the rear tyres JSR ApplyTyresAndSkids \ Calculate the forces on the rear tyres and apply skid \ forces and sound effects where applicable LDA xPrevVelocityLo \ Set (xVelocityHi xVelocityLo) to the x-coordinate of STA xVelocityLo \ the velocity vector from the previous iteration of the LDA xPrevVelocityHi \ main loop, which we stored in (xPrevVelocityHi STA xVelocityHi \ xPrevVelocityLo) LDA xVelocityLo \ Set (xVelocityHi xVelocityLo) CLC \ += (xSpinVelocityHi xSpinVelocityLo) ADC xSpinVelocityLo \ STA xVelocityLo \ starting with the low bytes LDA xVelocityHi \ And then the high bytes ADC xSpinVelocityHi STA xVelocityHi JSR ApplySteeringSpeed \ Calculate the following in parallel: \ \ zVelocity = zVelocity + xVelocity * steering \ \ xVelocity = xVelocity - zVelocity * steering LDX #0 \ Set X = 0, so the call to ApplyTyresAndSkids processes \ the front tyres JSR ApplyTyresAndSkids \ Calculate the forces on the front tyres and apply skid \ forces and sound effects where applicable JSR ApplySteeringForce \ Calculate the following in parallel: \ \ xTyreForceNose = xTyreForceNose \ + zTyreForceNose * steering \ \ zTyreForceNose = zTyreForceNose \ - xTyreForceNose * steering JSR ScaleTyreForces \ Calculate the following: \ \ spinYawDelta = (xTyreForceNose - xTyreForceRear) \ * 0.3 \ \ xTyreForceNose = xTyreForceNose >> 2 \ \ xTyreForceRear = xTyreForceRear >> 2 \ \ zTyreForceNose = zTyreForceNose >> 2 \ \ zTyreForceRear = zTyreForceRear >> 2 \ \ xPlayerAccel = (xTyreForceRear * 1.5 \ + xTyreForceNose) * 1.6 \ \ zPlayerAccel = (zTyreForceRear * 1.5 \ + zTyreForceNose) * 1.6 \ \ zTyreForceBoth = zPlayerAccelHi LDA heightAboveTrack \ If heightAboveTrack < 2, jump to dmod3 CMP #2 BCC dmod3 \ If we get here then heightAboveTrack >= 2 LDX #2 \ We now zero the three 16-bit bytes at spinYawDelta, \ xPlayerAccel and zPlayerAccel, so set a counter in X \ for the three variables LDA #0 \ Set A = 0 to use as the zero value .dmod2 STA spinYawDeltaLo,X \ Zero the X-th byte of (spinYawDeltaHi spinYawDeltaLo) STA spinYawDeltaHi,X DEX \ Decrement the variable counter BPL dmod2 \ Loop back until we have zeroed all three 16-bit \ variables .dmod3 JSR ApplyWingBalance \ Calculate the following: \ \ xPlayerAccel = xPlayerAccel \ - scaledSpeed * xPrevVelocityHi \ \ zPlayerAccel = zPlayerAccel \ - scaledSpeed \ * (wingBalance * playerSpeedHi + 2048) \ * abs(zVelocity) JSR RotateCarToCoord \ Rotate this vector: \ \ [ xPlayerAccel ] \ [ - ] \ [ zPlayerAccel ] \ \ from the frame of reference of the player's car into \ the 3D world coordinate system, putting the result \ into: \ \ [ xAcceleration ] \ [ - ] \ [ zAcceleration ] \ \ We ignore the y-coordinate as the calculations are \ all done along the ground JSR UpdateVelocity \ Calculate the following: \ \ xPlayerSpeed = xPlayerSpeed + xAcceleration << 5 \ \ zPlayerSpeed = zPlayerSpeed + zAcceleration << 3 \ \ spinYawAngle = spinYawAngle + spinYawDelta << 3 JSR ApplyDeltas \ Calculate the following: \ \ xPlayerCoord = xPlayerCoord + xPlayerSpeed * 2 \ \ zPlayerCoord = zPlayerCoord + zPlayerSpeed * 2 \ \ playerYawAngle = playerYawAngle + spinYawAngle JSR ApplyElevation \ Calculate changes in the car's elevation RTS \ Return from the subroutine
Name: ApplySpinYaw [Show more] Type: Subroutine Category: Driving model Summary: Calculate variables based on the spin yaw angle Deep dive: The core driving model
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ApplySpinYaw

Calculate the following: xVelocity = xVelocity - (spinYawAngle * 0.34) xSpinVelocity = spinYawAngle * 0.52
.ApplySpinYaw LDA spinYawAngleHi \ Set T = spinYawAngleHi to use as the low byte in (A T) STA T LDY #88 \ Set Y = 88 LDA spinYawAngleTop \ Set (A T) = (spinYawAngleTop spinYawAngleHi) JSR Multiply8x16Signed \ Set: \ \ (A T) = (A T) * abs(A) * Y / 256 \ = spinYawAngle * abs(spinYawAngle) * 88 / 256 \ = spinYawAngle * 88 / 256 \ = spinYawAngle * 0.34 STA U \ Set (U T) = (A T) \ = spinYawAngle * 0.34 LDA xVelocityLo \ Set (xVelocityHi xVelocityLo) SEC \ = (xVelocityHi xVelocityLo) - (U T) SBC T \ = xVelocity - (spinYawAngle * 0.34) STA xVelocityLo \ \ starting with the low bytes LDA xVelocityHi \ And then the high bytes SBC U STA xVelocityHi JSR MultiplyBy1Point5 \ Set (A T) = (U T) * 1.5 \ = spinYawAngle * 0.34 * 1.5 \ = spinYawAngle * 0.52 STA xSpinVelocityHi \ Set (xSpinVelocityHi xSpinVelocityLo) = (A T) LDA T \ = spinYawAngle * 0.52 STA xSpinVelocityLo RTS \ Return from the subroutine
Name: Multiply8x16Signed [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Multiply an 8-bit and a 16-bit number and apply a sign to the result
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplySpinYaw calls Multiply8x16Signed * ScaleTyreForces calls Multiply8x16Signed

This routine calculates: (A T) = (A T) * abs(A) * Y / 256 where A is the last variable to be loaded before the subroutine call. So if the call follows an LDA instruction, for example, the following is calculated if A is positive: (A T) = (A T) * Y / 256 and the following is calculated if A is negative: (A T) = -(A T) * Y / 256
Arguments: (A T) A 16-bit signed number Y An unsigned number N flag Controls the sign to be applied: * N flag clear to calculate (A T) * Y / 256 * N flag set to calculate -(A T) * Y / 256
.Multiply8x16Signed PHP \ Store the N flag on the stack JSR Absolute16Bit \ Set the sign of (A T) to that of the N flag argument STA V \ Set (V T) = (A T) STY U \ Set U = Y JSR Multiply8x16 \ Set (U T) = U * (V T) / 256 \ = Y * (A T) / 256 LDA U \ Set (A T) = (U T) \ = Y * (A T) / 256 PLP \ Retrieve the N flag from the stack JSR Absolute16Bit \ Set the sign of (A T) to that of the N flag argument RTS \ Return from the subroutine
Name: MultiplyBy1Point5 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Multiply a 16-bit signed number by 1.5
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplySpinYaw calls MultiplyBy1Point5 * ScaleTyreForces calls MultiplyBy1Point5

Calculate the following: (A T) = (U T) * 1.5
Arguments: (U T) A 16-bit signed number
.MultiplyBy1Point5 LDA U \ Set A = U, the high byte of (U T) CLC \ Clear the C flag, to use when A is positive BPL mulh1 \ If A is positive, jump to mulh1 to skip the following \ instruction SEC \ Set the C flag, to use when A is negative .mulh1 \ By this point, the C flag contains the sign bit of A \ (set if negative, clear if positive) ROR A \ Shift A to the right, inserting the C flag into bit 7 \ to retain the correct sign PHA \ Store the result on the stack, so the stack contains \ the top byte of (U T), shifted right by one place LDA T \ Set A = T, the low byte of (U T) ROR A \ Shift A to the right, so the stack and A contain the \ high and low bytes of (U T) >> 1, so if S is the value \ on top of the stack, we have: \ \ (S A) = (U T) >> 1 CLC \ Set (A T) = (S A) + (U T) ADC T \ = (U T) >> 1 + (U T) STA T \ = (U T) * 1.5 \ \ starting with the low bytes PLA \ And then the high bytes ADC U RTS \ Return from the subroutine
Name: ApplyTyresAndSkids [Show more] Type: Subroutine Category: Driving model Summary: Calculate the forces on the front or rear tyres and apply skid forces and sound effects where applicable Deep dive: The core driving model Skidding
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ApplyTyresAndSkids

Calculate the following: * If heightAboveTrack >= 2, stop the tyres from squealing * If heightAboveTrack < 2: * Call ApplyTyreForces * If either bit 6 or 7 is set in tyreSqueal for tyre X, then: * Call ApplySkidForces and make the tyres squeal otherwise: * On every other main loop iteration, stop the tyres from squealing
Arguments: X The set of tyres to process: * 0 = front tyres * 1 = rear tyres
.ApplyTyresAndSkids LDA heightAboveTrack \ If heightAboveTrack >= 2, jump to gfor1 to stop the CMP #2 \ tyres from squealing BCS gfor1 JSR ApplyTyreForces \ Calculate the tyre forces on the car LDA tyreSqueal,X \ Set A to tyreSqueal for tyre X AND #%11000000 \ If either of bit 6 or 7 is set in A, jump to gfor3 to BNE gfor3 \ make the tyres squeal LDA mainLoopCounterLo \ If bit 1 of mainLoopCounterLo is set, which it is on AND #%00000010 \ two out of every four iterations round the main loop, BNE gfor2 \ then jump to gfor2 to return from the subroutine .gfor1 LDX #3 \ Flush the buffer for sound channel 3 to stop any tyre JSR FlushSoundBuffer \ squeals we might already be making .gfor2 RTS \ Return from the subroutine .gfor3 JSR ApplySkidForces \ Calculate the tyre forces when the car is skidding, \ returning them in xTyreForceNose and zTyreForceNose or \ xTyreForceRear and zTyreForceRear LDA soundBuffer+3 \ If sound buffer 3 is currently being used, then we are BNE gfor4 \ already making the sound of the tyres squealing, so \ jump to gfor4 to return from the subroutine LDY #1 \ Make sound #3 (tyre squeal) using envelope 1 LDA #3 JSR MakeSound .gfor4 RTS \ Return from the subroutine
Name: ApplySteeringSpeed [Show more] Type: Subroutine Category: Driving model Summary: Apply steering to the car's speed in xVelocity and zVelocity Deep dive: The core driving model Matching the code to the driving model
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ApplySteeringSpeed

Calculate the following in parallel: zVelocity = zVelocity + xVelocity * steering xVelocity = xVelocity - zVelocity * steering
.ApplySteeringSpeed LDX #2 \ Set X = 2, so in the call to MultiplyCoords+7 we use \ (steeringHi steeringLo) as the 16-bit sign-magnitude \ value to multiply LDY #9 \ Set Y = 9, so in the call to MultiplyCoords+7 we use \ zVelocity as the 16-bit signed number LDA #%10000000 \ Clear bit 6 and set bit 7 of H, to store the result STA H \ rather than adding, and negate the result in the call \ to MultiplyCoords+7 LDA #14 \ Set A = 14, so in the call to MultiplyCoords+7, we \ store the result in xSteeringForce JSR MultiplyCoords+7 \ Set: \ \ variableA = -variableY * variableX \ \ so: \ \ xSteeringForce = -zVelocity * steering LDX #2 \ Set X = 2, so in the call to MultiplyCoords+7 we use \ (steeringHi steeringLo) as the 16-bit sign-magnitude \ value to multiply LDY #8 \ Set Y = 8, so in the call to MultiplyCoords+7 we use \ xVelocity as the 16-bit signed number LDA #%01000000 \ Set bit 6 and clear bit 7 of H, to add the result STA H \ rather than replacing, and leave the sign of the \ result alone in the call to MultiplyCoords+7 LDA #9 \ Set A = 9, so in the call to MultiplyCoords+7, we \ store the result in zVelocity JSR MultiplyCoords+7 \ Set: \ \ variableA = variableA + variableY * variableX \ \ so: \ \ zVelocity = zVelocity + xVelocity * steering LDX #8 \ Set X = 8, so the call to AddSteeringForce adds \ xSteeringForce to xVelocity JSR AddSteeringForce \ Set xVelocity = xVelocity + xSteeringForce \ = xVelocity - zVelocity * steering RTS \ Return from the subroutine
Name: ApplySteeringForce [Show more] Type: Subroutine Category: Driving model Summary: Apply steering to xTyreForceNose and zTyreForceNose Deep dive: The core driving model Matching the code to the driving model
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ApplySteeringForce

Calculate the following in parallel: xTyreForceNose = xTyreForceNose + zTyreForceNose * steering zTyreForceNose = zTyreForceNose - xTyreForceNose * steering
.ApplySteeringForce LDX #2 \ Set X = 2, so in the call to MultiplyCoords we use \ (steeringHi steeringLo) as the 16-bit sign-magnitude \ value to multiply LDY #12 \ Set Y = 12, so in the call to MultiplyCoords we use \ zTyreForceNose as the 16-bit signed number LDA #%00000000 \ Clear bits 6 and 7 of H, to store the result rather STA H \ than adding, and leave the sign of the result alone in \ the call to MultiplyCoords+7 LDA #14 \ Set A = 14, so in the call to MultiplyCoords+7, we \ store the result in xSteeringForce JSR MultiplyCoords+7 \ Set: \ \ variableA = variableY * variableX \ \ so: \ \ xSteeringForce = zTyreForceNose * steering LDX #2 \ Set X = 2, so in the call to MultiplyCoords we use \ (steeringHi steeringLo) as the 16-bit sign-magnitude \ value to multiply LDY #10 \ Set Y = 10, so in the call to MultiplyCoords we use \ xTyreForceNose as the 16-bit signed number LDA #%11000000 \ Set bits 6 and 7 of H, to add the result rather than STA H \ replacing, and negate the result in the call to \ MultiplyCoords+7 LDA #12 \ Set A = 12, so in the call to MultiplyCoords+7, we \ store the result in zTyreForceNose JSR MultiplyCoords+7 \ Set: \ \ variableA = variableA - variableY * variableX \ \ so: \ \ zTyreForceNose = zTyreForceNose \ - xTyreForceNose * steering LDX #10 \ Set X = 8, so the call to AddSteeringForce adds \ xSteeringForce to xTyreForceNose JSR AddSteeringForce \ Set xTyreForceNose = xTyreForceNose + xSteeringForce \ = xTyreForceNose + zTyreForceNose * steering RTS \ Return from the subroutine
Name: AddSteeringForce [Show more] Type: Subroutine Category: Driving model Summary: Add the steering force to xVelocity or xTyreForceNose
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplySteeringForce calls AddSteeringForce * ApplySteeringSpeed calls AddSteeringForce

Arguments: X Called with either 8 or 10: * 8: set xVelocity += xSteeringForce * 10: set xTyreForceNose += xSteeringForce
.AddSteeringForce LDA xPlayerSpeedHi,X \ Add (xSteeringForceHi xSteeringForceLo) to the CLC \ variable at offset X from (xPlayerSpeedTop ADC xSteeringForceLo \ xPlayerSpeedHi), starting with the low bytes STA xPlayerSpeedHi,X LDA xPlayerSpeedTop,X \ And then the high bytes ADC xSteeringForceHi STA xPlayerSpeedTop,X RTS \ Return from the subroutine
Name: ScaleTyreForces [Show more] Type: Subroutine Category: Driving model Summary: Scale the tyre forces and calculate the combined tyre force on the player Deep dive: The core driving model Matching the code to the driving model
Context: See this subroutine on its own page References: This subroutine is called as follows: * ApplyDrivingModel calls ScaleTyreForces

Calculate the following: spinYawDelta = (xTyreForceNose - xTyreForceRear) * 0.3 xTyreForceNose = xTyreForceNose >> 2 xTyreForceRear = xTyreForceRear >> 2 zTyreForceNose = zTyreForceNose >> 2 zTyreForceRear = zTyreForceRear >> 2 xPlayerAccel = (xTyreForceRear * 1.5 + xTyreForceNose) * 1.6 zPlayerAccel = (zTyreForceRear * 1.5 + zTyreForceNose) * 1.6 zTyreForceBoth = zPlayerAccelHi
.ScaleTyreForces LDY #78 \ Set Y = 78 LDA xTyreForceNoseLo \ Set (A T) = xTyreForceNose - xTyreForceRear SEC \ SBC xTyreForceRearLo \ starting with the low bytes STA T LDA xTyreForceNoseHi \ And then the high bytes SBC xTyreForceRearHi JSR Multiply8x16Signed \ Set: \ \ (A T) = (A T) * abs(A) * Y / 256 \ = (xTyreForceNose - xTyreForceRear) * 78 / 256 \ = (xTyreForceNose - xTyreForceRear) * 0.3 STA spinYawDeltaHi \ Set spinYawDelta = (A T) LDA T \ = (xTyreForceNose - xTyreForceRear) * 0.3 STA spinYawDeltaLo \ We now perform the following shifts, making sure to \ keep the signs intact: \ \ xTyreForceNose = xTyreForceNose >> 2 \ \ xTyreForceRear = xTyreForceRear >> 2 \ \ zTyreForceNose = zTyreForceNose >> 2 \ \ zTyreForceRear = zTyreForceRear >> 2 LDY #1 \ Set Y = 1, to act as a shift counter in the outer loop \ below, so we right-shift Y + 1 times (i.e. twice) .forc1 LDX #3 \ Set X = 3, to act as a variable counter in the inner \ loop to work through zTyreForceRear, zTyreForceNose, \ xTyreForceRear and xTyreForceNose (let's call this \ variableX) .forc2 LDA xTyreForceNoseHi,X \ Set A to the high byte of variableX CLC \ Clear the C flag, to use if variableX is positive BPL forc3 \ If A is positive, jump to forc3 to keep the C flag \ clear SEC \ Otherwise set the C flag, to use if variableX is \ negative \ The C flag now contains the sign bit of A .forc3 ROR xTyreForceNoseHi,X \ Set variableX = variableX >> 1 ROR xTyreForceNoseLo,X \ \ Keeping the sign intact DEX \ Decrement the inner loop counter in X BPL forc2 \ Loop back to forc2 until we have shifted all four \ variables to the right by one place DEY \ Decrement the shift counter in Y BPL forc1 \ Loop back until we have right-shifted Y + 1 times LDX #2 \ Set X = 2, to act as a variable counter in the \ following loop, iterating through values 0 and 2, as \ X is decremented twice at the end of the loop \ \ The loop references xTyreForceRear,X and \ xTyreForceNose,X, so the loop iterates through: \ \ * zTyreForceRear and zTyreForceNose when X = 2 \ \ * xTyreForceRear and xTyreForceNose when X = 0 \ \ The comments below are for when X = 2 LDA #1 \ Set G = 1, to use as the index for storing the STA G \ following calculation .forc4 LDA xTyreForceRearLo,X \ Set (U T) = zTyreForceRear STA T LDA xTyreForceRearHi,X STA U JSR MultiplyBy1Point5 \ Set (A T) = (U T) * 1.5 \ = zTyreForceRear * 1.5 STA U \ Set (U T) = (A T) \ = zTyreForceRear * 1.5 LDA T \ Set (A T) = (U T) + zTyreForceNose CLC \ = zTyreForceRear * 1.5 + zTyreForceNose ADC xTyreForceNoseLo,X \ STA T \ starting with the low bytes LDY #205 \ Set Y = 205 LDA U \ And then the high bytes ADC xTyreForceNoseHi,X JSR Multiply8x16Signed \ Set (A T) = (A T) * abs(A) * Y / 256 \ = (zTyreForceRear * 1.5 + zTyreForceNose) \ * 205 / 256 \ = (zTyreForceRear * 1.5 + zTyreForceNose) * 0.8 ASL T \ Set (A T) = (A T) * 2 ROL A \ = (zTyreForceRear * 1.5 + zTyreForceNose) * 1.6 LDY G \ Set zPlayerAccel = (A T) STA xPlayerAccelHi,Y \ = (zTyreForceRear * 1.5 + zTyreForceNose) * 1.6 LDA T STA xPlayerAccelLo,Y DEC G \ Decrement G so the next iteration stores the result in \ xPlayerAccel DEX \ Decrement X twice so the next iteration calculates DEX \ (xTyreForceRear * 1.5 + xTyreForceNose) * 1.6 BPL forc4 \ Loop back until we have calculated both zPlayerAccel \ and xPlayerAccel LDA zPlayerAccelHi \ Set zTyreForceBoth = zPlayerAccelHi STA zTyreForceBoth RTS \ Return from the subroutine