The extra track files in Revs don't only contain track data. They also contain large amounts of code, and a whole smorgasbord of code modifications that are applied to the main game code when the track files are loaded. This article lists all the modifications across all five extra tracks; see the deep dive on secrets of the extra tracks for details of how the code modifications are applied and for an introduction to the extra track hook process.
The following documentation uses a variation of BBC BASIC terminology to describe two types of code modification. Specifically, 16-bit pokes are shown as:
Each of these pokes the value y into address &xxxx, with the first poking a two-byte 16-bit value, and the second poking a one-byte 8-bit value. (In BBC BASIC, the ! operator actually pokes in a word of four bytes, so it's not quite the same; here I'm using it to mean two bytes, but hopefully the meaning is clear.)
This is followed by a description of the modification, along with links to the relevant parts of the source code.
Some of the modifications turn parts of the game code into self-modifying code, which are effectively modifications that are applied while the game is running. These are listed at the end, but let's start with the one-off modifications that are applied when an extra track file is loaded.
Here's a list of all modifications applied by the various extra track files when they are loaded from the extra tracks menu.
Most of these modifications are applied by all five of the extra tracks, but some are specific to particular tracks. For example, not all tracks need to modify the chicane road sign to show a hairpin; only Brands Hatch, the Nürburgring and Oulton Park have hairpin bends, so only these tracks need to modify the sign. The usage of each modification is noted in the first bullet point.
?&1248 = &20 !&1249 = HookSectionFrom
1248 B9 05 59 LDA trackSectionFrom,Y -> JSR HookSectionFrom
|
- This modification is applied by all five tracks
- It modifies routine GetSectionCoords at instruction #10 from the start of the routine
- The modification inserts a call to the HookSectionFrom routine (which is the same in all extra tracks)
- The effect of the modification is to initialise and calculate the current segment vector whenever the game code needs to fetch a section coordinate from the track data (see the deep dive on dynamic track generation in the extra tracks for details)
|
!&128A = HookFirstSegment
1289 20 E0 13 JSR UpdateVectorNumber -> JSR HookFirstSegment
|
- This modification is applied by all five tracks
- It modifies routine GetFirstSegment at instruction #3 from label getf2
- The modification inserts a call to the HookFirstSegment routine (which is the same in all extra tracks)
- The effect of the modification is to calculate the relevant segment vector when the game code needs to fetch the first segment in a new section (see the deep dive on dynamic track generation in the extra tracks for details)
|
?&12FB = &20 !&12FC = HookDataPointers
12FB 18 CLC -> JSR HookDataPointers
12FC 69 03 ADC #3
|
- This modification is applied by all five tracks
- It modifies routine GetTrackSegment part 1 at instructions #3 and #4 from the start of the routine
- The modification inserts a call to the HookDataPointers routine (which is the same in all extra tracks)
- The effect of the modification is to update the data pointers when the game code needs to fetch a segment that is dynamically generated (see the deep dive on dynamic track generation in the extra tracks for details)
|
!&1310 = &A9 &88
1310 29 F8 AND #%11111000 -> LDA #17*8
|
- This modification is applied by Donington Park
- It modifies routine GetTrackSegment part 1 at instruction #7 from label gets1
- The modification replaces the calculation of the halfway segment number with an LDA instruction
- The effect of the modification is to hard-code the halfway point around the Donington Park track to section 17
|
!&1310 = &A9 &10
1310 29 F8 AND #%11111000 -> LDA #2*8
|
- This modification is applied by Snetterton
- It modifies routine GetTrackSegment part 1 at instruction #7 from label gets1
- The modification replaces the calculation of the halfway segment number with an LDA instruction
- The effect of the modification is to hard-code the halfway point around the Snetterton track to section 2
|
!&13CA = HookSegmentVector
13C9 20 DA 13 JSR UpdateCurveVector -> JSR HookSegmentVector
|
- This modification is applied by all five tracks
- It modifies routine GetTrackSegment part 3 at instruction #11 from label gets12
- The modification inserts a call to the HookSegmentVector routine (which is the same in all extra tracks)
- The effect of the modification is to move to the next segment vector, calculate it and store it when the game code needs to fetch a segment that is dynamically generated (see the deep dive on dynamic track generation in the extra tracks for details)
|
!&1427 = HookSegmentVector
1426 20 DA 13 JSR UpdateCurveVector -> JSR HookSegmentVector
|
- This modification is applied by all five tracks
- It modifies routine TurnPlayerAround at instruction #4 from the start of the routine
- The modification inserts a call to the HookSegmentVector routine (which is the same in all extra tracks)
- The effect of the modification is to move to the next segment vector, calculate it and store it when the game code turns the player around and needs to fetch a segment that is dynamically generated (see the deep dive on dynamic track generation in the extra tracks for details)
|
!&1594 = HookJoystick
1593 20 00 0C JSR Multiply8x8 -> JSR HookJoystick
|
- This modification is applied by all five tracks
- It modifies routine ProcessDrivingKeys part 1 at instruction #13 from the start of the routine
- The modification inserts a call to the HookJoystick routine, which is tailored for each individual track: Brands Hatch, Donington Park, the Nürburgring, Oulton Park and Snetterton
- The effect of the modification is to apply enhanced joystick steering and drifting suppression to specific sections of the track, to support hairpins that would be too sharp for the standard joystick code to cope with; each track has a tailored modification routine that depends on the track design
|
!&1947 = HookFlattenHills
1946 20 33 19 JSR CheckVergeOnScreen -> JSR HookFlattenHills
|
- This modification is applied by all five tracks
- It modifies routine MapSegmentsToLines at instruction #5 from the start of the routine
- The modification inserts a call to the HookFlattenHills routine, which differs between tracks: Brands Hatch, Donington Park and Oulton Park have the same routine, while Snetterton and the Nürburgring have their own versions
- The effect of the modification is to flatten any hills in the verge buffer, calculate the hill height and track width, and cut objects off at the hill height
In Snetterton and the Nürburgring, it also sets the verge edge to visible when the car is pointing along the track and the nose is pointing downwards
In the Nürburgring, it also flags the verge coordinate at index X as being visible on-screen, but only when the track section at the front of the segment buffer is one of sections 0 to 5
|
?&1FE9 = &A2
1FE9 A6 1F LDX horizonLine -> LDX #&1F
|
- This modification is applied by all five tracks
- It modifies routine DrawObject at instruction #6 from label dobj3
- The modification changes an LDX instruction from absolute addressing (i.e. loading from an address) to immediate addressing (i.e. loading a constant)
- The effect of the modification is to modify the DrawObject routine so it can be modified at run-time (see the section on self-modifying code below for details)
|
?&231B = 0
231A F0 0F BEQ gsec3 -> BEQ 0 (i.e. NOP)
|
- This modification is applied by Donington Park and Snetterton
- It modifies routine GetSectionAngles part 1 at instruction #2 from label gsec2
- The modification disables a BEQ by setting the branch destination to point to the next instruction
- The effect of the modification is to avoid skipping entry number sectionListPointer when updating the entries in the track section list
|
?&248B = &4C !&248C = HookFieldOfView
248B B0 2B BCS gseg16 -> JMP HookFieldOfView
248D 4C 03 24 JMP gseg4 EQUB &03, &24
|
- This modification is applied by all five tracks
- It modifies routine GetSegmentAngles part 3 at instructions #2 and #3 from label gseg12
- The modification inserts a call to the HookFieldOfView routine (which is the same in all extra tracks)
- The effect of the modification is that when we populate the verge buffer in GetSegmentAngles, we don't give up so easily if we get segments outside the field of view, which prevents segments from disappearing when we're driving around sharp corners
|
!&24DF = HookForward
24DE 20 F3 12 JSR MovePlayerForward -> JSR HookForward
|
- This modification is applied by Donington Park
- It modifies routine MovePlayerSegment at instruction #5 from label mpla3
- The modification inserts a call to the HookForward routine (which is unique to Donington Park)
- The effect of the modification is to move the player forward by an extra segment when edgeSegmentNumber is 10
|
?&24EA = 13
24E9 C9 0E CMP #14 -> CMP #13
|
- This modification is applied by all five tracks
- It modifies routine MovePlayerSegment at instruction #1 from label mpla5
- The modification changes the number to which the segment number is compared
- The effect of the modification is to move the player back a segment if edgeSegmentNumber = 13 (rather than 14), or back by two segments if edgeSegmentNumber > 13 (i.e. 14 or 15, rather than just 15)
|
!&24F3 = HookMoveBack
24F2 20 0B 14 JSR MovePlayerBack -> JSR HookMoveBack
|
- This modification is applied by all five tracks
- It modifies routine MovePlayerSegment at instruction #1 from label mpla6
- The modification inserts a call to the HookMoveBack routine (which is the same in all extra tracks)
- The effect of the modification is to only move the player backwards if the player has not yet driven past the segment
|
!&2539 = HookFixHorizon ?&2538 = &20
2538 99 48 5F STA yVergeLeft,Y -> JSR HookFixHorizon
|
- This modification is applied by all five tracks
- It modifies routine GetTrackAndMarkers at instruction #2 from label gtrm2
- The modification inserts a call to the HookFixHorizon routine, which differs between tracks: Brands Hatch, Oulton Park and Snetterton have the same routine, while Donington Park and the Nürburgring have their own versions
- The effect of the modification is to cut objects off at the track line in A rather than horizonLine
In Brands Hatch, Oulton Park and Snetterton, it also collapses the left verge of the track into the right verge, but only for the track section list and the first three entries in the track segment list
In the Nürburgring, if the track section at the horizon is one of sections 0 to 5, it also hides the verge
|
!&2543 = Hook80Percent ?&2545 = &EA
2542 20 50 34 JSR Absolute8Bit -> JSR Hook80Percent
2545 4A LSR A NOP
|
- This modification is applied by all five tracks
- It modifies routine GetTrackAndMarkers at instructions #6 and #7 from label gtrm2
- The modification inserts a call to the Hook80Percent routine, which differs between tracks: Brands Hatch, the Nürburgring, Oulton Park and Snetterton have the same routine, while Donington Park has its own version
- The effect of the modification is to set the horizonTrackWidth to 80% of the width of the track on the horizon
In Donington Park, it also calls Absolute8Bit at the start of the hook routine
|
?&261A = &4C !&261B = HookUpdateHorizon
261A 85 1F STA horizonLine -> JMP HookUpdateHorizon
261C 84 51 STY horizonListIndex EQUB &51
|
- This modification is applied by all five tracks
- It modifies routine GetVergeAndMarkers part 4 at instructions #9 and #10 from label gmar11
- The modification inserts a call to the HookUpdateHorizon routine (which is the same in all extra tracks)
- The effect of the modification is to only update the horizon if we have found fewer than 12 visible segments
|
?&2772 = 75
2771 C9 3C CMP #60 -> CMP #75
|
- This modification is applied by all five tracks
- It modifies routine ProcessOvertaking part 2 at instruction #5 from label tact14
- The modification changes the comparison value for the distance between cars
- The effect of the modification is to make cars apply the brakes when they're within a distance of 75 of each other, rather than 60
|
?&298E = &FF
298D 29 1F AND #&1F -> AND #&FF
|
- This modification is applied by the Nürburgring
- It modifies routine BuildCarObjects part 1 at instruction #8 from label bcar3
- The modification disables an AND instruction by changing the operand to &FF, so it leaves the value of A untouched
- The effect of the modification is to remove the height restriction on ySegmentCoordIHi, which is implemented with a mod 32 instruction that this modification disables, so segment y-coordinates are no longer capped to a maximum of 8,192
|
?&2F23 = &20 !&2F24 = HookBackground
2F23 B9 60 5F LDA backgroundColour,Y -> JSR HookBackground
|
- This modification is applied by Donington Park and Snetterton
- It modifies routine UpdateBackground at instruction #2 from label upba1
- The modification inserts a call to the HookBackground routine (which is the same in both tracks)
- The effect of the modification is to avoid updating the background colour when the track line above is showing green for the leftTrackStart verge
|
?&3574 = 4 ?&35F4 = 11 = 3 + 8
Object 10, Part 2: Scaffolds: (0, 3, 1, 0) -> (4, -3, 1, 0)
Coordinates: (10, 4, 9, 10) (1, -4, 9, 10)
|
- This modification is applied by Brands Hatch, the Nürburgring and Oulton Park
- It modifies variables objectTop and objectBottom, in part 2 of object 10
- The modification moves part 2 of object 10 (the chicane road sign) so the right-hand vertical bar is below the horizontal line rather than above it
- The effect of the modification is to change the design of the chicane road sign into a hairpin bend, as follows:
->
|
!&44D6 = trackSteering
44D5 B9 D0 59 LDA trackSteering,Y -> LDA trackSteering,Y
|
- This modification is applied by all five tracks
- It modifies routine GetSectionSteering at instruction #1 from label slin1
- The modification changes the address of an LDA instruction so it loads from the new location of trackSteering within the track data file
- The effect of the modification is to read racing line data from the new location of trackSteering
|
?&45CB = &20 !&45CC = HookSlopeJump
45CB 0A ASL A -> JSR HookSlopeJump
45CC 26 77 ROL W
|
- This modification is applied by all five tracks
- It modifies routine ApplyElevation part 5 at instruction #1 from the start of the routine
- The modification inserts a call to the HookSlopeJump routine (which is the same in all extra tracks)
- The effect of the modification is to make the car jump when driving fast over sloping segments
|
!&462C = HookFlipAbsolute
462B 20 50 34 JSR Absolute8Bit -> JSR HookFlipAbsolute
|
- This modification is applied by all five tracks
- It modifies routine MovePlayerOnTrack at instruction #4 from the start of the routine
- The modification inserts a call to the HookFlipAbsolute routine (which is the same in all extra tracks)
- The effect of the modification is to set the sign of A according to the direction we are facing along the track
|
!&4CC1 = zTrackSignVector
4CC0 BD E0 53 LDA zTrackSignVector,X -> LDA zTrackSignVector,X
|
- This modification is applied by all five tracks
- It modifies routine BuildRoadSign at instruction #4 from label sign1
- The modification changes the address of an LDA instruction so it loads from the new location of zTrackSignVector within the track data file
- The effect of the modification is to read sign vector z-coordinates from the new location of zTrackSignVector
|
!&4CC9 = yTrackSignVector
4CC8 BD F0 53 LDA yTrackSignVector,X -> LDA yTrackSignVector,X
|
- This modification is applied by all five tracks
- It modifies routine BuildRoadSign at instruction #7 from label sign1
- The modification changes the address of an LDA instruction so it loads from the new location of yTrackSignVector within the track data file
- The effect of the modification is to read sign vector y-coordinates from the new location of yTrackSignVector
|
!&4CD1 = xTrackSignVector
4CD0 BD D0 53 LDA xTrackSignVector,X -> LDA xTrackSignVector,X
|
- This modification is applied by all five tracks
- It modifies routine BuildRoadSign at instruction #10 from label sign1
- The modification changes the address of an LDA instruction so it loads from the new location of xTrackSignVector within the track data file
- The effect of the modification is to read sign vector x-coordinates from the new location of xTrackSignVector
|
!&4CD7 = trackSignData
4CD6 BD EA 59 LDA trackSignData,X -> LDA trackSignData,X
|
- This modification is applied by all five tracks
- It modifies routine BuildRoadSign at instruction #12 from label sign1
- The modification changes the address of an LDA instruction so it loads from the new location of trackSignData within the track data file
- The effect of the modification is to read sign data from the new location of trackSignData
|
!&4CE1 = trackSignData
4CE0 BD EA 59 LDA trackSignData,X -> LDA trackSignData,X
|
- This modification is applied by all five tracks
- It modifies routine BuildRoadSign at instruction #17 from label sign1
- The modification changes the address of an LDA instruction so it loads from the new location of trackSignData within the track data file
- The effect of the modification is to read sign data from the new location of trackSignData
|
?&4F55 = 22 ?&4F59 = 22
4F54 C9 12 CMP #18 -> CMP #22
4F56 90 03 BCC hori2 BCC hori2
4F58 A9 12 LDA #18 LDA #22
|
- This modification is applied by all five tracks
- It modifies routine MoveHorizon at instructions #1 and #3 from label hori1
- The modification increases the maximum allowed value of A, which is used to calculate the value of screen timer 1
- The effect of the modification is to change the maximum value of screenTimer1 (i.e. when we are on a hill) from &04D8 + 1152 = &0958 to &04D8 + 1408 = &0A58 (&100 more), which allows the palette change to occur lower down the screen, so we can support higher hills
|
The above modifications are only applied once, when the track first loads. However, as part of these modifications, there are three parts of the main game code that are modified into code that is self-modifying at run-time. The first two of these self-modifying modifications require the &1FE9 modification above, which changes an LDX instruction from absolute addressing (i.e. loading from an address) to immediate addressing (i.e. loading a constant), so the LDX can be modified at run-time to load X with a specific value.
These self-modifying modifications are all related to the process of cutting off the bottoms of distant objects so they don't go below the horizon. Here are the details: