Skip to navigation

Revs on the BBC Micro

Code hooks in the extra tracks

Documentation on every modification and code hook in the extra tracks

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:

  !&xxxx = y

while 8-bit pokes are shown as:

  ?&xxxx = y

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.)

Each modification is shown in the following format:

?<address 1> = <new value 1>
!<address 2> = <new value 2>

<instructions before modification>                -> <after modification>

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.

Code modifications
------------------

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:
    Object 10 at close quarters -> Object 10 hairpin at close quarters

!&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

Self-modifying code at run-time
-------------------------------

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:

?&1FE9 = &A2
?&1FEA = A

1FE9   A6 1F      LDX horizonLine                 -> LDX #A
  • This modification is applied by HookFlattenHills to DrawObject at instruction #6 from label dobj3
  • The modification modifies the operand of the LDX instruction to the value of A in HookFlattenHills
  • The effect of the modification is to cut off objects that are beyond the horizon down to the horizon line given in A, rather than 7

?&1FE9 = &A2
?&1FEA = A

1FE9   A6 1F      LDX horizonLine                 -> LDX #A
  • This modification is applied by HookFixHorizon to DrawObject at instruction #6 from label dobj3
  • The modification modifies the operand of the LDX instruction to the value of A in HookFixHorizon
  • The effect of the modification is to cut off objects that are beyond the horizon down to the horizon line given in A, rather than 7

?&23B3 = A

23B2   A9 07      LDA #7                          -> LDA #A
  • This modification is applied by HookSectionFrom to GetSectionAngles at instruction #4 from label gsec11
  • The modification modifies the operand of the LDA instruction to the value of A in HookSectionFrom, so the CMP prevHorizonIndex compares with this value rather than 7
  • The effect of the modification is to check the first A entries in the track section list the horizon line, rather than the first 7