Revs on the BBC Micro

# Driving model: ApplyGrassOrTrack

```       Name: ApplyGrassOrTrack                                       [Show more]
Type: Subroutine
Category: Driving model
Summary: Apply the effects of driving or braking on grass
Deep dive: The core driving model
Driving on grass
Matching the code to the driving model
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* ApplyDrivingModel calls ApplyGrassOrTrack

* If all the following are true:

* There is grass under at least one side of the car
* bumpyGrassHeight = 0
* heightAboveTrack = 0
* Bit 7 of playerDrift is set

then calculate the following to make the car jump when it hits the verge:

* yGravityDelta = playerSpeedHi / 2
* yJumpHeight = playerSpeedHi / 4
* heightAboveTrack = heightAboveTrack + 1
* spinYawAngleHi = spinYawAngleHi >> 1 with bit 7 set

and make the crash/contact sound

* If there is grass under at least one side of the car, then:

* bumpyGrassHeight = random number in the range 1 to 7 that is higher with
higher speeds

otherwise:

* bumpyGrassHeight = 0

* For each wing, calculate the following:

* If the brakes are not being applied, set brakeForce = 0, otherwise set:

* brakeForce = -zTyreForceBoth / 8    (front wing)
* brakeForce =  zTyreForceBoth / 8    (rear wing)

* If we are driving on grass on both sides of the car, then set:

wingForce = wingForceGrass + brakeForce

otherwise calculate:

wingForce = wingSetting * min(53, playerSpeedHi) * abs(zVelocity)
+ wingForceTrack + brakeForce

* Set:

wingForce95 = wingForce * 243 / 256

.ApplyGrassOrTrack

LDA #0                 \ Set A = 0

LDY throttleBrakeState \ If throttleBrakeState is non-zero, then the brakes are
BNE gras1              \ not being applied, so jump to gras1 to set H = 0 and
\ G = 0

\ If we get here then the brakes are being applied

LDA zTyreForceBoth     \ Set A = zTyreForceBoth

PHP                    \ Store the N flag on the stack, so the PLP below sets
\ the N flag according to the value of zTyreForceBoth

LSR A                  \ Set A = A >> 3
LSR A                  \       = zTyreForceBoth / 8
LSR A

BPL gras1              \ the following instruction

ORA #%11100000         \ Set bits 5-7 of A to ensure that the value of A
\ retains the correct sign after being shifted

\ By this point, A = zTyreForceBoth / 8 and has the
\ correct sign

.gras1

STA H                  \ Set H = A

EOR #&FF               \ Set G = -A
CLC
STA G

\ So if the brakes are not being applied we have:
\
\   * G = 0
\   * H = 0
\
\ and if the brakes are being applied, we have:
\
\   * G = -zTyreForceBoth / 8
\   * H = zTyreForceBoth / 8
\
\ G is used in the front wing calculation below, while
\ H is used in the rear wing calculation

LDA playerSpeedHi      \ Set U = playerSpeedHi
STA U

LDX #0                 \ Set X = 0, to store as the value of bumpyGrassHeight
\ if we are not driving on grass

\ The leftSurface and rightSurface locations are in
\ screen memory, and are just to the left or right of
\ the dashboard, so:
\
\   * If leftSurface = &FF then there is grass under the
\     left edge of the dashboard
\
\   * If rightSurface = &FF then there is grass under
\     the right edge of the dashboard

LDA leftSurface        \ Set W so it is &FF if there is grass under both edges
AND rightSurface       \ of the dashboard
STA W

LDA leftSurface        \ If leftSurface = &FF, then there is grass under the
CMP #&FF               \ left side of the car, so jump to gras2
BEQ gras2

LDA rightSurface       \ If rightSurface <> &FF, then there isn't grass under
CMP #&FF               \ the right side of the car, so jump to gras4 to skip
BNE gras4              \ the following and set bumpyGrassHeight to 0

.gras2

\ If we get here then there is grass under at least one
\ side of the car, and W = &FF if there is grass under
\ both sides

LDA VIA+&68            \ Read 6522 User VIA T1C-L timer 2 low-order counter
\ (SHEILA &68), which decrements one million times a
\ second and will therefore be pretty random

JSR Multiply8x8        \ Set (A T) = A * U
\           = random * playerSpeedHi
\
\ So A is a random number that is higher with higher
\ speeds

AND #7                 \ Set X = A mod 8
TAX

BNE gras3              \ If X = 0, set X = 1
INX

\ So X is now a random number in the range 1 to 7 that
\ is higher with higher speeds

.gras3

BNE gras4

ORA heightAboveTrack   \ LDA is not required here, so perhaps it's left over
BNE gras4              \ from development)

BIT playerDrift        \ If bit 7 of playerDrift is clear, then the player's
BPL gras4              \ car is not moving significantly sideways, so jump to
\ gras4

\ If we get here then the player's car is drifting, and
\ the car is not jumping on bumpy grass (as
\ bumpyGrassHeight = 0) and is glued to the track (as
\ heightAboveTrack = 0)

JSR ApplyVergeJump     \ Calculate the following variables to apply a jump to
\ the car when we hit the track verge:
\
\   yGravityDelta = playerSpeedHi / 2
\
\   yJumpHeight = playerSpeedHi / 4
\
\   heightAboveTrack = heightAboveTrack + 1
\
\   spinYawAngleHi = spinYawAngleHi >> 1 with bit 7 set
\
\ and make the crash/contact sound

.gras4

STX bumpyGrassHeight   \ Set bumpyGrassHeight = X, so:
\
\   * If we are not driving on grass, then
\     bumpyGrassHeight = 0
\
\   * If we are driving on grass, then bumpyGrassHeight
\     is set to a random number in the range 1 to 7 that
\     is higher with higher speeds

LDX #1                 \ We now run the following loop, once for each wing
\ setting, so set X to use as a loop counter, from 1
\ (for the front wing) to 0 (for the rear wing)

.gras5

LDA playerSpeedHi      \ Set A = playerSpeedHi

BCC gras6

LDA #53                \ Set A = 53, so A has a maximum value of 53

.gras6

STA U                  \ Store A in U, so we now have the following:
\
\   U = min(53, playerSpeedHi)

LDA wingSetting,X      \ Set A to the scaled wing setting for wing X

JSR Multiply8x8        \ Set (A T) = A * U
\           = wingSetting * min(53, playerSpeedHi)

BIT zVelocityHi        \ Set the N flag according to the sign in bit 7 of
\ zVelocityHi, so the call to Absolute8Bit sets the
\ sign of A to the same sign as zVelocity

JSR Absolute8Bit       \ Set A = A * abs(zVelocity)
\       = wingSetting * min(53, playerSpeedHi)
\                     * abs(zVelocity)

CLC                    \ Set A = A + wingForceTrack for this wing
ADC wingForceTrack,X   \       = wingSetting * min(53, playerSpeedHi)
\                     * abs(zVelocity)
\         + wingForceTrack

LDY #243               \ Set U = 243 to use in the 95% calculation below
STY U

LDY W                  \ If W <> &FF, then there is not grass under both sides
CPY #&FF               \ of the car, so jump to gras7 to skip the following
BNE gras7

LDA wingForceGrass,X   \ There is grass under both sides of the car, so set A
\ to the wingForceGrass value for this wing

LDY #&FF               \ Set Y = &FF (this has no effect as Y is already &FF,
\ so this instruction is a bit of a mystery)

.gras7

CLC                    \ Set A = A + G (front wing) or H (rear wing)
\ This adds the effect of the brakes being applied

STA wingForce,X        \ Store A in wingForce for this wing

JSR Multiply8x8        \ Set (A T) = A * U
\           = A * 243
\
\ so A = A * 243 / 256
\      = A * 0.95

STA wingForce95,X      \ Store A in wingForce95 for this wing

DEX                    \ Decrement the loop counter in X to point to the next
\ wing

BPL gras5              \ Loop back until we have processed both wings

RTS                    \ Return from the subroutine
```