.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 subroutineName: GetWingSettings [Show more] Type: Subroutine Category: Keyboard Summary: Get the front and rear wing settings from the playerContext: See this subroutine on its own page References: This subroutine is called as follows: * HeadToTrack calls GetWingSettings.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 subroutineName: PrintRaceClass [Show more] Type: Subroutine Category: Text Summary: Print the race classContext: See this subroutine on its own page References: This subroutine is called as follows: * MainLoop (Part 5 of 6) calls PrintRaceClass * PrintDriverTable calls PrintRaceClass.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 tokenName: token50 [Show more] Type: Variable Category: Text Summary: Text for recursive token 50 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * PrintDriverTable uses token50 * tokenHi uses token50 * tokenLo uses token50.token6 EQUB 160 + 2 \ Print 2 spaces EQUS "BEST LAP TIMES" \ Print "BEST LAP TIMES" EQUB 160 + 2 \ Print 2 spaces EQUB 255 \ End tokenName: token6 [Show more] Type: Variable Category: Text Summary: Text for recursive token 6 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token6 * tokenLo uses token6.token13 EQUS " mins" \ Print " mins" EQUB 255 \ End tokenName: token13 [Show more] Type: Variable Category: Text Summary: Text for recursive token 13 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token13 * tokenLo uses token13.token14 EQUS " laps" \ Print " laps" EQUB 255 \ End tokenName: token14 [Show more] Type: Variable Category: Text Summary: Text for recursive token 14 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token14 * tokenLo uses token14.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, &81Name: token15 [Show more] Type: Variable Category: Text Summary: Text for recursive token 15 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token15 * tokenLo uses token15.dashData25 SKIP 10 \ Populated with part of the dashboard image SKIP 26 \ Populated with code from &7B00 to &7B19Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData25.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 40Name: objectIndex [Show more] Type: Variable Category: 3D objects Summary: Index range of an object's data in the object data tables Deep dive: Object definitionsContext: 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..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 70Name: scaffoldIndex [Show more] Type: Variable Category: 3D objects Summary: Index of an object's scaffold in the objectScaffold table Deep dive: Scaling objects with scaffoldsContext: 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..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 subroutineName: GetDriverAddress [Show more] Type: Subroutine Category: Text Summary: Get the address of the specified driver's nameContext: 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.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 tokenName: token27 [Show more] Type: Variable Category: Text Summary: Text for recursive token 27 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token27 * tokenLo uses token27.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, &81Name: token34 [Show more] Type: Variable Category: Text Summary: Text for recursive token 34Context: 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.dashData26 SKIP 41Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData26 * fillDataOffset uses dashData26.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 subroutineName: PrintSpaces [Show more] Type: Subroutine Category: Text Summary: Print the specified number of spacesContext: 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).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 stripName: DrawFence (Part 1 of 2) [Show more] Type: Subroutine Category: Screen buffer Summary: Draw the fence that we crash into when running off the trackContext: 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.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, greenName: fencePixelsGrass [Show more] Type: Variable Category: Screen buffer Summary: Pixel bytes for the fence with green grass behind itContext: See this variable on its own page References: This variable is used as follows: * DrawFence (Part 2 of 2) uses fencePixelsGrass.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, blueName: fencePixelsSky [Show more] Type: Variable Category: Screen buffer Summary: Pixel bytes for the fence with blue sky behind itContext: See this variable on its own page References: This variable is used as follows: * DrawFence (Part 2 of 2) uses fencePixelsSky.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, &81Name: token21 [Show more] Type: Variable Category: Text Summary: Text for recursive token 21 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token21 * tokenLo uses token21.dashData27 SKIP 52Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData27 * fillDataOffset uses dashData27.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 listName: 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 GetSegmentAnglesContext: See this variable on its own page References: This variable is used as follows: * GetSegmentAngles (Part 3 of 3) uses segmentStep.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 ENDIFName: shiftedKeys [Show more] Type: Variable Category: Keyboard Summary: Negative inkey values for the configuration keys that are pressed in combination with SHIFTContext: See this variable on its own page References: This variable is used as follows: * ProcessShiftedKeys uses shiftedKeysIF _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 ENDIFName: menuKeys [Show more] Type: Variable Category: Keyboard Summary: Negative inkey values for the menu keys (SPACE, "1", "2" and "3") for the Acornsoft releaseContext: See this variable on its own page References: This variable is used as follows: * GetMenuOption uses menuKeys.timeFromOption EQUB 4, 9, 25 EQUB &00 \ This byte appears to be unusedName: 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 minutesContext: 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..lapsFromOption EQUB 5, 10, 20Name: lapsFromOption [Show more] Type: Variable Category: Keyboard Summary: Table to convert from the option numbers in the laps menu to the actual number of lapsContext: See this variable on its own page References: This variable is used as follows: * MainLoop (Part 5 of 6) uses lapsFromOption.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 unusedName: pointsForPlace [Show more] Type: Variable Category: Drivers Summary: The points awarded for the top six places, plus the fastest lapContext: See this variable on its own page References: This variable is used as follows: * AwardRacePoints uses pointsForPlace.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 tokenName: token29 [Show more] Type: Variable Category: Text Summary: Text for recursive token 29 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token29 * tokenLo uses token29.token9 EQUS "Professional" \ Print "Professional" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81Name: token9 [Show more] Type: Variable Category: Text Summary: Text for recursive token 9 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token9 * tokenLo uses token9.dashData28 SKIP 56Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData28 * fillDataOffset uses dashData28.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 - shlr2Name: 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 vergesContext: See this variable on its own page References: This variable is used as follows: * DrawShallowToRight uses jumpShallowRight.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 subroutineName: SetRowColours [Show more] Type: Subroutine Category: Text Summary: Set the foreground and background colours for a table rowContext: 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.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: rowColours [Show more] Type: Variable Category: Text Summary: Three different palettes for displaying even-odd rows in tablesContext: See this variable on its own page References: This variable is used as follows: * SetRowColours uses rowColours.token10 EQUS "SELECT " \ Print "SELECT " EQUB 255 \ End tokenName: token10 [Show more] Type: Variable Category: Text Summary: Text for recursive token 10 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token10 * tokenLo uses token10.token12 EQUS " DRIVER" \ Print " DRIVER" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81Name: token12 [Show more] Type: Variable Category: Text Summary: Text for recursive token 12 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token12 * tokenLo uses token12.dashData29 SKIP 60Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData29 * fillDataOffset uses dashData29.jumpSteepRight EQUB stlr2 - stlr2 EQUB stlr3 - stlr2 EQUB stlr4 - stlr2 EQUB stlr5 - stlr2 EQUB stlr6 - stlr2 EQUB stlr7 - stlr2 EQUB stlr8 - stlr2 EQUB stlr9 - stlr2Name: 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 vergesContext: See this variable on its own page References: This variable is used as follows: * DrawSteepToRight uses jumpSteepRight.jumpSteepLeft EQUB strl9 - strl2 EQUB strl8 - strl2 EQUB strl7 - strl2 EQUB strl6 - strl2 EQUB strl5 - strl2 EQUB strl4 - strl2 EQUB strl3 - strl2 EQUB strl2 - strl2Name: 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 vergesContext: See this variable on its own page References: This variable is used as follows: * DrawSteepToLeft uses jumpSteepLeft.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 subroutineName: GetNumberInput [Show more] Type: Subroutine Category: Keyboard Summary: Fetch a number between 0 and 40 from the keyboardContext: 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).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: startMirror [Show more] Type: Variable Category: Dashboard Summary: The offset from mirrorAddress for the start of each mirror segmentContext: See this variable on its own page References: This variable is used as follows: * DrawCarInMirror uses startMirror.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, &81Name: token37 [Show more] Type: Variable Category: Text Summary: Text for recursive token 37 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token37 * tokenLo uses token37.dashData30 SKIP 64Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData30 * fillDataOffset uses dashData30.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 &69Name: 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 edgesContext: 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..token7 EQUS "Novice" \ Print "Novice" EQUB 255 \ End token EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81 EQUB &81Name: token7 [Show more] Type: Variable Category: Text Summary: Text for recursive token 7 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token7 * tokenLo uses token7.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, &EAName: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData31 * fillDataOffset uses dashData31.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 &81Name: yLookupLo [Show more] Type: Variable Category: Drawing pixels Summary: Lookup table for converting pixel y-coordinate to low byte of screen addressContext: 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..dashData32 SKIP 73Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData32 * fillDataOffset uses dashData32.driverNames1 EQUS "Max Throttle" EQUS "Johnny Turbo" EQUS "Davey Rocket" EQUS "Gloria Slap " EQUB &81, &81 \ These bytes appear to be unused EQUB &81Name: 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.dashData33 SKIP 77Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData33 * fillDataOffset uses dashData33.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 - shrl2Name: 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 vergesContext: See this variable on its own page References: This variable is used as follows: * DrawShallowToLeft uses jumpShallowLeft.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 tokenName: token33 [Show more] Type: Variable Category: Text Summary: Text for recursive token 33Context: 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.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 unusedName: ResetBestLapTime [Show more] Type: Subroutine Category: Drivers Summary: Reset the best lap time to 10:00.0 for a specific driverContext: 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).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 &81Name: endMirror [Show more] Type: Variable Category: Dashboard Summary: The offset from mirrorAddress for the end of each mirror segmentContext: See this variable on its own page References: This variable is used as follows: * DrawCarInMirror uses endMirror.dashData34 SKIP 77Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData34 * fillDataOffset uses dashData34.driverNames2 EQUS "Hugh Jengine" EQUS "Desmond Dash" EQUS "Percy Veer " EQUS "Gary Clipper" EQUB &81, &81 \ These bytes appear to be unused EQUB &81, &81Name: 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.dashData35 SKIP 76Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData35 * fillDataOffset uses dashData35.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 &7EName: PrintHeader [Show more] Type: Subroutine Category: Text Summary: Configure and print a double-height header in screen mode 7 Deep dive: Text tokensContext: 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.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, &81Name: token1 [Show more] Type: Variable Category: Text Summary: Text for recursive token 1 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token1 * tokenLo uses token1.dashData36 SKIP 58Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData36 * fillDataOffset uses dashData36.driverNames3 EQUS "Willy Swerve" EQUS "Sid Spoiler " EQUS "Billy Bumper" EQUS "Slim Chance "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.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, &81Name: token40 [Show more] Type: Variable Category: Text Summary: Text for recursive token 40 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token40 * tokenLo uses token40.dashData37 SKIP 52Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData37 * ShowStartingLights uses dashData37.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 subroutineName: PrintGearNumber [Show more] Type: Subroutine Category: Text Summary: Print the number of the current gear in double-width characters on the gear stickContext: 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.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 subroutineName: ResetBestLapTimes [Show more] Type: Subroutine Category: Drivers Summary: Reset the best lap times to 10:00.0 for all driversContext: 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.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 unusedName: token52 [Show more] Type: Variable Category: Text Summary: Text for recursive token 52 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token52 * tokenLo uses token52.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 tokenName: token28 [Show more] Type: Variable Category: Text Summary: Text for recursive token 28 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token28 * tokenLo uses token28.dashData38 SKIP 52Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData38 * fillDataOffset uses dashData38.driverNames4 EQUS "Harry Fume " EQUS "Dan Dipstick" EQUS "Wilma Cargo " EQUS "Miles Behind"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.token5 EQUS "THE PITS" \ Print "THE PITS" EQUB 255 \ End tokenName: token5 [Show more] Type: Variable Category: Text Summary: Text for recursive token 5 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token5 * tokenLo uses token5.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 tokenName: token36 [Show more] Type: Variable Category: Text Summary: Text for recursive token 36 Deep dive: Text tokensContext: See this variable on its own page References: This variable is used as follows: * tokenHi uses token36 * tokenLo uses token36.dashData39 SKIP 52Name: 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 binaryContext: 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.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: Print234DigitBCD [Show more] Type: Subroutine Category: Text Summary: Print a specific driver's accumulated points as a padded two-, three- or four-digit numberContext: 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..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 subroutineName: Print4DigitBCD [Show more] Type: Subroutine Category: Text Summary: Print a specific driver's accumulated points as a four-digit numberContext: 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).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 unusedName: FlushSoundBuffers [Show more] Type: Subroutine Category: Sound Summary: Flush all four specified sound buffers Deep dive: The engine soundsContext: 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.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, &81Name: dashRightEdge [Show more] Type: Variable Category: Dashboard Summary: Storage for the first track pixel byte along the right edge of the dashboardContext: 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..dashData40 SKIP 52Name: 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 binaryContext: See this variable on its own page References: This variable is used as follows: * dashDataOffset uses dashData40 * fillDataOffset uses dashData40.driverNames5 EQUS "Roland Slide" EQUS "Rick Shaw " EQUS "Peter Out " EQUS "Dummy Driver"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..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/32Name: 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 scaffoldsContext: 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..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 subroutineName: GetSectionSteering [Show more] Type: Subroutine Category: Tactics Summary: Calculate the optimum steering for each track section Deep dive: Tactics of the non-player driversContext: 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.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 = YName: 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 dropsContext: 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).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 trackName: 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 dropsContext: 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 trackEOR #%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 carName: 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 dropsContext: 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 playerPitchAngleLDA #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 heightAboveTrackName: 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 dropsContext: 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 127ASL 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 subroutineName: 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 dropsContext: 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.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 subroutineName: MultiplyHeight [Show more] Type: Subroutine Category: Track geometry Summary: Multiply the height above ground of a specified track segment by AContext: 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).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 subroutineName: 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 trackContext: 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..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 subroutineName: GetCarInSegment [Show more] Type: Subroutine Category: Car geometry Summary: Calculate the player car's position within the current segmentContext: 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).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 subroutineName: 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 pointingContext: 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.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 subroutineName: 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 modelContext: See this subroutine on its own page References: This subroutine is called as follows: * MainDrivingLoop (Part 2 of 5) calls ApplyDrivingModel.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 subroutineName: ApplySpinYaw [Show more] Type: Subroutine Category: Driving model Summary: Calculate variables based on the spin yaw angle Deep dive: The core driving modelContext: 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.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 subroutineName: Multiply8x16Signed [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Multiply an 8-bit and a 16-bit number and apply a sign to the resultContext: 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.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 subroutineName: MultiplyBy1Point5 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Multiply a 16-bit signed number by 1.5Context: 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.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 subroutineName: 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 SkiddingContext: 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.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 subroutineName: 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 modelContext: 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.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 subroutineName: 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 modelContext: 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.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 subroutineName: AddSteeringForce [Show more] Type: Subroutine Category: Driving model Summary: Add the steering force to xVelocity or xTyreForceNoseContext: 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.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 subroutineName: 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 modelContext: 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[X]Subroutine Absolute16Bit (category: Maths (Arithmetic))Calculate the absolute value (modulus) of a 16-bit number[X]Subroutine Absolute8Bit (category: Maths (Arithmetic))Calculate the absolute value (modulus) of an 8-bit number[X]Subroutine AddSteeringForce (category: Driving model)Add the steering force to xVelocity or xTyreForceNose[X]Subroutine ApplyBounce (category: Driving model)Apply a bounce to the player's car when it hits the ground[X]Subroutine ApplyDeltas (category: Driving model)Apply the deltas in the x-axis and z-axis to the player's coordinates[X]Subroutine ApplyElevation (Part 1 of 5) (category: Driving model)Calculate changes in the car's elevation[X]Subroutine ApplyEngine (category: Driving model)Apply the effects of the engine[X]Subroutine ApplyGrassOrTrack (category: Driving model)Apply the effects of driving or braking on grass[X]Subroutine ApplySkidForces (category: Driving model)Calculate the tyre forces when the car is skidding[X]Subroutine ApplySpinYaw (category: Driving model)Calculate variables based on the spin yaw angle[X]Subroutine ApplySteeringForce (category: Driving model)Apply steering to xTyreForceNose and zTyreForceNose[X]Subroutine ApplySteeringSpeed (category: Driving model)Apply steering to the car's speed in xVelocity and zVelocity[X]Subroutine ApplyTyreForces (category: Driving model)Calculate the tyre forces on the car[X]Subroutine ApplyTyresAndSkids (category: Driving model)Calculate the forces on the front or rear tyres and apply skid forces and sound effects where applicable[X]Subroutine ApplyWingBalance (category: Driving model)Apply the effect of the wing settings[X]Entry point DrawFence-1 in subroutine DrawFence (Part 1 of 2) (category: Screen buffer)Contains an RTS[X]Subroutine FlushSoundBuffer (category: Sound)Flush the specified sound buffer[X]Subroutine GetCarInSegment (category: Car geometry)Calculate the player car's position within the current segment[X]Subroutine GetNumberFromText (category: Text)Convert a two-digit string into a number[X]Subroutine GetNumberInput (category: Keyboard)Fetch a number between 0 and 40 from the keyboard[X]Subroutine GetRotationMatrix (Part 1 of 5) (category: Maths (Geometry))Calculate the rotation matrix for rotating the player's yaw angle into the global 3D coordinate system[X]Subroutine GetTextInput (category: Keyboard)Fetch a string from the keyboard, padded with spaces if required[X]Subroutine MakeSound (category: Sound)Make a sound[X]Subroutine Multiply8x16 (category: Maths (Arithmetic))Multiply an 8-bit and a 16-bit number[X]Subroutine Multiply8x16Signed (category: Maths (Arithmetic))Multiply an 8-bit and a 16-bit number and apply a sign to the result[X]Subroutine Multiply8x8 (category: Maths (Arithmetic))Calculate (A T) = T * U[X]Subroutine MultiplyBy1Point5 (category: Maths (Arithmetic))Multiply a 16-bit signed number by 1.5[X]Entry point MultiplyCoords+7 in subroutine MultiplyCoords (category: Maths (Arithmetic))Use the following variables instead of the above: * Y = Offset of the 16-bit signed number to multiply (instead of N)[X]Subroutine MultiplyHeight (category: Track geometry)Multiply the height above ground of a specified track segment by A[X]Configuration variable OSWRCHThe address for the OSWRCH routine[X]Subroutine Print2DigitBCD (category: Text)Print a binary coded decimal (BCD) number in the specified format[X]Subroutine Print4DigitBCD (category: Text)Print a specific driver's accumulated points as a four-digit number[X]Entry point Print4DigitBCD+3 in subroutine Print4DigitBCD (category: Text)Do not print the first two digits (i.e. omit printing A)[X]Subroutine PrintCharacter (category: Text)Print a character on-screen[X]Entry point PrintCharacter-6 in subroutine PrintCharacter (category: Text)Print double-width character (this is used to print the double-width number on the gear stick)[X]Subroutine PrintHeader (category: Text)Configure and print a double-height header in screen mode 7[X]Subroutine PrintSpaces (category: Text)Print the specified number of spaces[X]Subroutine PrintToken (category: Text)Print a recursive token[X]Subroutine ResetBestLapTime (category: Drivers)Reset the best lap time to 10:00.0 for a specific driver[X]Subroutine RotateCarToCoord (category: Maths (Geometry))Rotate a vector from the frame of reference of the player's car into the 3D world coordinate system[X]Subroutine RotateCoordToCar (category: Maths (Geometry))Rotate a vector from the 3D world coordinate system into the frame of reference of the player's car[X]Subroutine ScaleCarInSegment (category: Car geometry)Work out how far a car is within a segment by scaling the angle in which the car is pointing[X]Subroutine ScaleTyreForces (category: Driving model)Scale the tyre forces and calculate the combined tyre force on the player[X]Subroutine SetPlayerDriftSup (category: Car geometry)Record player drift, but only if the player is not in the first three segments of a track section[X]Subroutine UpdateVelocity (category: Driving model)Update the player's velocity and spin yaw angle[X]Subroutine WaitForSpace (category: Keyboard)Print a prompt, wait for the SPACE key to be released, and wait for SPACE to be pressed[X]Variable baseSpeed (category: Drivers)The base speed for each car, copied from the track data[X]Variable bestLapMinutes in workspace Main variable workspaceMinutes of each driver's best lap time, stored in BCD[X]Variable bestLapSeconds in workspace Main variable workspaceSeconds of each driver's best lap time, stored in BCD[X]Variable bestLapTenths in workspace Main variable workspaceTenths of seconds of each driver's best lap time, stored in BCD[X]Variable bumpyGrassHeight in workspace Zero pageThe height of the bumps when driving over grass, which fluctuates randomly in the range 1 to 7[X]Variable carProgress in workspace Stack variablesLowest byte of each car's progress through the segment it's in[X]Variable carRacingLine in workspace Stack variablesEach car's position on the track in the x-axis[X]Variable carSpeedHi in workspace Stack variablesHigh byte of each car's forward speed[X]Variable colourScheme in workspace Zero pageThe number of the table colour scheme passed to the SetRowColours routine[X]Variable currentPlayer in workspace Zero pageThe number of the current player[X]Configuration variable dashDataThe address of the first code block that gets swapped in and out of screen memory, along with parts of the dashboard image[X]Variable dashData0 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData1 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData10 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData11 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData12 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData13 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData14 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData15 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData16 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData17 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData18 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData19 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData2 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData20 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData21 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData22 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData23 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData24 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData26 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData27 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData28 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData29 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData3 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData30 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData31 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData32 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData33 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData34 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData35 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData36 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData38 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData39 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData4 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData40 (category: Screen buffer)Contains part of the dashboard image that gets moved into screen memory[X]Variable dashData5 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData6 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData7 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData8 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashData9 (category: Screen buffer)Contains code that gets moved into screen memory[X]Variable dashDataOffset (category: Screen buffer)Offset of the dash data within each dash data block[X]Variable directionFacing in workspace Zero pageThe direction that our car is facing[X]Label dmod1 in subroutine ApplyDrivingModel[X]Label dmod2 in subroutine ApplyDrivingModel[X]Label dmod3 in subroutine ApplyDrivingModel[X]Variable driverNames1 (category: Text)The first batch of driver names (1 of 5)[X]Variable edgeDistanceLo in workspace Zero pageLow byte of the distance between the player's car and the nearest track edge[X]Variable edgeSegmentPointer in workspace Zero pageThe index of the segment within the track verge buffer that is closest to the player's car[X]Variable edgeYawAngle in workspace Zero pageThe yaw angle of the track segment that is closest to the player's car, from the point of view of the car[X]Label elev1 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev10 in subroutine ApplyElevation (Part 2 of 5)[X]Label elev11 in subroutine ApplyElevation (Part 2 of 5)[X]Label elev12 in subroutine ApplyElevation (Part 2 of 5)[X]Label elev13 in subroutine ApplyElevation (Part 3 of 5)[X]Label elev14 in subroutine ApplyElevation (Part 4 of 5)[X]Label elev15 in subroutine ApplyElevation (Part 4 of 5)[X]Label elev16 in subroutine ApplyElevation (Part 4 of 5)[X]Label elev17 in subroutine ApplyElevation (Part 4 of 5)[X]Label elev18 in subroutine ApplyElevation (Part 4 of 5)[X]Label elev19 in subroutine ApplyElevation (Part 5 of 5)[X]Label elev2 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev3 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev4 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev5 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev6 in subroutine ApplyElevation (Part 1 of 5)[X]Label elev7 in subroutine ApplyElevation (Part 2 of 5)[X]Label elev8 in subroutine ApplyElevation (Part 2 of 5)[X]Label elev9 in subroutine ApplyElevation (Part 2 of 5)[X]Variable engineTorque in workspace Zero pageThe power being generated by the engine[X]Label fenc2 in subroutine DrawFence (Part 2 of 2)[X]Label flub1 in subroutine FlushSoundBuffers[X]Label forc1 in subroutine ScaleTyreForces[X]Label forc2 in subroutine ScaleTyreForces[X]Label forc3 in subroutine ScaleTyreForces[X]Label forc4 in subroutine ScaleTyreForces[X]Variable frontWingSetting (category: Driving model)The front wing setting, as entered by the player[X]Variable gearNumber in workspace Zero pageThe current gear number[X]Variable gearNumberText (category: Text)The character to print on the gear stick for each gear[X]Label gfor1 in subroutine ApplyTyresAndSkids[X]Label gfor2 in subroutine ApplyTyresAndSkids[X]Label gfor3 in subroutine ApplyTyresAndSkids[X]Label gfor4 in subroutine ApplyTyresAndSkids[X]Variable headerBackground (category: Text)Background colour for printing mode 7 headers[X]Variable headerForeground (category: Text)Foreground colour for printing mode 7 headers[X]Variable headerSpaces (category: Text)Number of spaces for printing mode 7 headers[X]Variable heightAboveTrack in workspace Zero pageThe car's height above the track, for when it is being dropped from the crane, or jumping from hitting the verge too fast[X]Variable liftFromTorque (category: Driving model)The lift of the front of the car caused by acceleration or braking[X]Variable mainLoopCounterLo in workspace Zero pageLow byte of the main loop counter, which increments on each iteration of the main driving loop[X]Label mseg1 in subroutine MovePlayerOnTrack[X]Label mseg2 in subroutine MovePlayerOnTrack[X]Label mseg3 in subroutine MovePlayerOnTrack[X]Label mseg4 in subroutine MovePlayerOnTrack[X]Label mulh1 in subroutine MultiplyBy1Point5[X]Label numb1 in subroutine GetNumberInput[X]Label numb2 in subroutine GetNumberInput[X]Variable playerDrift (category: Driving model)Records whether the player's car is moving sideways by a significant amount[X]Variable playerHeading in workspace Zero pageThe 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[X]Variable playerMoving in workspace Zero pageFlag to denote whether the player's car is moving[X]Variable playerPastSegment in workspace Zero pageDetermines whether the player has gone past the closest segment to the player (i.e. whether the relative yaw angle of the segment is greater than 90 degrees)[X]Variable playerPitchAngle in workspace Zero pageThe player's pitch angle[X]Variable playerSegmentIndex in workspace Zero pageUsed to store the index * 3 of the track segment containing the player's car in the track segment buffer[X]Variable playerSideways (category: Car geometry)Contains the player's heading, for testing whether the car is pointing sideways[X]Variable playerSpeedHi in workspace Zero pageHigh byte of the speed of the player's car along the track[X]Variable playerSpeedLo in workspace Zero pageLow byte of the speed of the player's car along the track[X]Variable playerYawAngleHi in workspace Zero pageHigh byte of the player's yaw angle[X]Variable playerYawAngleLo in workspace Zero pageLow byte of the player's yaw angle[X]Variable raceClass (category: Drivers)The class of the current race[X]Label rall1 in subroutine ResetBestLapTimes[X]Variable rearWingSetting (category: Driving model)The rear wing setting, as entered by the player[X]Variable rowColours (category: Text)Three different palettes for displaying even-odd rows in tables[X]Label scar1 in subroutine ScaleCarInSegment[X]Label scar2 in subroutine ScaleCarInSegment[X]Variable sectionSteering (category: Tactics)The optimum steering for each section[X]Variable segmentVector in workspace Main variable workspaceThe segment vector number for a track segment in the track segment buffer[X]Label shlr10 in subroutine DrawShallowToRight[X]Label shlr12 in subroutine DrawShallowToRight[X]Label shlr13 in subroutine DrawShallowToRight[X]Label shlr14 in subroutine DrawShallowToRight[X]Label shlr15 in subroutine DrawShallowToRight[X]Label shlr16 in subroutine DrawShallowToRight[X]Label shlr17 in subroutine DrawShallowToRight[X]Label shlr18 in subroutine DrawShallowToRight[X]Label shlr19 in subroutine DrawShallowToRight[X]Label shlr2 in subroutine DrawShallowToRight[X]Label shlr3 in subroutine DrawShallowToRight[X]Label shlr4 in subroutine DrawShallowToRight[X]Label shlr5 in subroutine DrawShallowToRight[X]Label shlr6 in subroutine DrawShallowToRight[X]Label shlr7 in subroutine DrawShallowToRight[X]Label shlr8 in subroutine DrawShallowToRight[X]Label shlr9 in subroutine DrawShallowToRight[X]Label shrl10 in subroutine DrawShallowToLeft[X]Label shrl12 in subroutine DrawShallowToLeft[X]Label shrl13 in subroutine DrawShallowToLeft[X]Label shrl14 in subroutine DrawShallowToLeft[X]Label shrl15 in subroutine DrawShallowToLeft[X]Label shrl16 in subroutine DrawShallowToLeft[X]Label shrl17 in subroutine DrawShallowToLeft[X]Label shrl18 in subroutine DrawShallowToLeft[X]Label shrl19 in subroutine DrawShallowToLeft[X]Label shrl2 in subroutine DrawShallowToLeft[X]Label shrl3 in subroutine DrawShallowToLeft[X]Label shrl4 in subroutine DrawShallowToLeft[X]Label shrl5 in subroutine DrawShallowToLeft[X]Label shrl6 in subroutine DrawShallowToLeft[X]Label shrl7 in subroutine DrawShallowToLeft[X]Label shrl8 in subroutine DrawShallowToLeft[X]Label shrl9 in subroutine DrawShallowToLeft[X]Label slin1 in subroutine GetSectionSteering[X]Label slin2 in subroutine GetSectionSteering[X]Variable soundBuffer (category: Sound)Details of each sound channel's buffer status[X]Label spac1 in subroutine PrintSpaces[X]Variable spinPitchAngle in workspace Zero pageThe amount of pitch angle spin that is being applied to the player's car[X]Variable spinYawAngleHi (category: Car geometry)High byte of the amount of yaw angle spin that is being applied to the player's car[X]Variable spinYawAngleTop (category: Car geometry)Top byte of the amount of yaw angle spin that is being applied to the player's car[X]Variable spinYawDeltaHi (category: Driving model)High byte of the change in the spin yaw angle for the player's car[X]Variable spinYawDeltaLo (category: Driving model)Low byte of the change in the spin yaw angle for the player's car[X]Label stlr2 in subroutine DrawSteepToRight[X]Label stlr3 in subroutine DrawSteepToRight[X]Label stlr4 in subroutine DrawSteepToRight[X]Label stlr5 in subroutine DrawSteepToRight[X]Label stlr6 in subroutine DrawSteepToRight[X]Label stlr7 in subroutine DrawSteepToRight[X]Label stlr8 in subroutine DrawSteepToRight[X]Label stlr9 in subroutine DrawSteepToRight[X]Label strl2 in subroutine DrawSteepToLeft[X]Label strl3 in subroutine DrawSteepToLeft[X]Label strl4 in subroutine DrawSteepToLeft[X]Label strl5 in subroutine DrawSteepToLeft[X]Label strl6 in subroutine DrawSteepToLeft[X]Label strl7 in subroutine DrawSteepToLeft[X]Label strl8 in subroutine DrawSteepToLeft[X]Label strl9 in subroutine DrawSteepToLeft[X]Variable throttleBrakeState in workspace Zero pageDenotes whether the throttle or brake are being applied[X]Variable token31 (category: Text)Text for recursive token 31[X]Variable token33 (category: Text)Text for recursive token 33[X]Variable token34 (category: Text)Text for recursive token 34[X]Variable totalPointsHi (category: Drivers)High byte of total accumulated points for each driver[X]Variable totalPointsLo (category: Drivers)Low byte of total accumulated points for each driver[X]Variable trackBaseSpeed in workspace trackDataThe base speed for each race class, used when generating the best racing lines and non-player driver speeds[X]Variable trackSectionCount in workspace trackDataThe total number of track sections * 8[X]Variable trackSteering in workspace trackDataThe optimum steering for non-player drivers to apply on each track section[X]Variable tyreSqueal (category: Driving model)A flag to determine whether the front or rear tyres are squealing[X]Variable xCursor (category: Text)The cursor's x-coordinate, which can either be a pixel coordinate or a character row[X]Variable xHeader (category: Text)Column number for printing mode 7 headers[X]Variable xPlayerAccelHi (category: Driving model)High byte of the x-coordinate of the change in velocity of the player's car in the player's frame of reference[X]Variable xPlayerAccelLo (category: Driving model)Low byte of the x-coordinate of the change in velocity of the player's car in the player's frame of reference[X]Variable xPlayerSpeedHi (category: Driving model)High byte of the x-coordinate of the velocity vector (i.e. x-axis speed) for the player's car during this main loop iteration[X]Variable xPlayerSpeedTop (category: Driving model)Top byte of the x-coordinate of the velocity vector (i.e. x-axis speed) for the player's car during this main loop iteration[X]Variable xPrevVelocityHi in workspace Zero pageUsed to store the high byte of the x-coordinate of the player's previous velocity vector during the driving model calculations (i.e. the velocity from the last iteration of the main loop)[X]Variable xPrevVelocityLo in workspace Zero pageUsed to store the low byte of the x-coordinate of the player's previous velocity vector during the driving model calculations (i.e. the velocity from the last iteration of the main loop)[X]Variable xSpinVelocityHi in workspace Zero pageUsed to store the high byte of the scaled spin yaw angle during the driving model calculations[X]Variable xSpinVelocityLo in workspace Zero pageUsed to store the low byte of the scaled spin yaw angle during the driving model calculations[X]Variable xSteeringForceHi (category: Driving model)High byte of the sideways force applied by steering[X]Variable xSteeringForceLo (category: Driving model)Low byte of the sideways force applied by steering[X]Variable xTrackSegmentI in workspace trackDataVector x-coordinates between two consecutive segments on the inside of the track[X]Variable xTyreForceNoseHi (category: Driving model)High byte of the x-coordinate of the force vector produced by the front tyres in the nose of the car[X]Variable xTyreForceNoseLo (category: Driving model)Low byte of the x-coordinate of the force vector produced by the front tyres in the nose of the car[X]Variable xTyreForceRearHi (category: Driving model)High byte of the x-coordinate of the force vector produced by the rear tyres of the car[X]Variable xTyreForceRearLo (category: Driving model)Low byte of the x-coordinate of the force vector produced by the rear tyres of the car[X]Variable xVelocityHi (category: Driving model)High byte of the x-coordinate of the player's velocity vector, from the player's frame of reference[X]Variable xVelocityLo (category: Driving model)Low byte of the x-coordinate of the player's velocity vector, from the player's frame of reference[X]Variable yCursor (category: Text)The cursor's pixel y-coordinate[X]Variable yGravityDelta in workspace Zero pageThe distance in the y-axis that the car would fall if the track wasn't there, for use when calculating the outcome of jumps and being dropped from the crane[X]Variable yHeader (category: Text)Row number for printing mode 7 headers[X]Variable yJumpHeight in workspace Zero pageThe height of the car's jump (or crane lift), in terms of track lines, which we use to alter the position of the horizon[X]Variable yPlayerCoordHi (category: Car geometry)The high byte of the y-coordinate of the player's 3D coordinates[X]Variable yPlayerCoordTop (category: Car geometry)The top byte of the y-coordinate of the player's 3D coordinates[X]Variable ySegmentCoordIHi in workspace Main variable workspaceThe high byte of the 3D y-coordinate for an inner track segment in the track segment buffer[X]Variable ySegmentCoordILo in workspace Main variable workspaceThe low byte of the 3D y-coordinate for an inner track segment in the track segment buffer[X]Variable yTrackSegmentI in workspace trackDataVector y-coordinates between two consecutive segments on the inside of the track[X]Variable zPlayerAccelHi (category: Driving model)High byte of the z-coordinate of the change in velocity of the player's car in the player's frame of reference[X]Variable zTrackSegmentI in workspace trackDataVector z-coordinates between two consecutive segments on the inside of the track[X]Variable zTyreForceBoth (category: Driving model)High byte of the z-coordinate of the force vector produced by the front and tyres combined[X]Variable zVelocityHi (category: Driving model)High byte of the z-coordinate of the player's velocity vector, from the player's frame of reference[X]Variable zVelocityLo (category: Driving model)Low byte of the z-coordinate of the player's velocity vector, from the player's frame of reference