.ApplyTyreForces LDA xVelocityLo \ Set T = xVelocityLo STA T ORA xVelocityHi \ Set A = xVelocityLo OR xVelocityHi and store the flags PHP \ on the stack, which will be zero if both the high and \ low bytes of xVelocity are zero, i.e. if xVelocity = 0 LDA xVelocityHi \ Set (A T) = (xVelocityHi xVelocityLo) JSR Negate16Bit \ Set (A T) = -(A T) \ = -xVelocity LDY #5 \ Set Y = 5 to act as a shift counter in the following \ loop .tfor1 ASL T \ Set (A T) = (A T) << 1 ROL A DEY \ Decrement the shift counter BNE tfor1 \ Loop back until we have shifted left by five places, \ so we now have: \ \ (A T) = (A T) * 2^5 \ = -xVelocity * 2^5 STA xTyreForceNoseHi,X \ Set xTyreForceNoseHi for tyre X to the high byte of \ the result PLP \ Retrieve the flags that we stored on the stack, which \ will be zero if xVelocity = 0 BEQ tfor2 \ If xVelocity = 0, jump to tfor2 to set \ xTyreForceNoseLo to the low byte of the result \ If we get here then xVelocity is non-zero EOR xVelocityHi \ If xVelocityHi and xTyreForceNoseHi for tyre X have \ the same sign, this will clear bit 7 of A SEC \ Set the C flag BPL tfor7 \ If bit 7 of A is clear, then xVelocityHi and \ xTyreForceNoseHi for tyre X have the same sign, so \ jump to tfor7 with the C flag set to rotate a 1 into \ bit 7 of tyreSqueal for tyre X .tfor2 \ If we get here then either xVelocity = 0, or xVelocity \ is non-zero and xVelocity and xTyreForceNose have \ different signs LDA T \ Set xTyreForceNoseLo = T, so we now have: STA xTyreForceNoseLo,X \ \ xTyreForceNose = (A T) \ = -xVelocity * 2^5 JSR GetTyreForces \ Calculate the tyre forces due to the throttle or \ brakes: \ \ * (A T) = the force \ \ * H = the sign of the force \ \ * G = X + 2, so the call to ApplyLimitThrottle sets \ zTyreForceNose or zTyreForceRear accordingly \ \ * If the throttle is not being applied, (NN MM) is \ a maximum value for the force \ \ If the throttle is being applied and we are processing \ the front tyres, only G is calculated and the C flag \ is set BCC tfor3 \ If the C flag is clear then GetTyreForces successfully \ returned a set of calculated values, so jump to tfor3 \ to keep going LDA #0 \ Set zTyreForceNose = 0 STA zTyreForceNoseLo STA zTyreForceNoseHi LDA xTyreForceNoseHi \ Set A = xTyreForceNoseHi JSR Absolute8Bit \ Set A = |A| \ = |xTyreForceNoseHi| JMP tfor6 \ Jump to tfor6 .tfor3 JSR ApplyLimitThrottle \ If the throttle is being applied, then set \ zTyreForceNose or zTyreForceRear to: \ \ (A T) * abs(H) \ \ otherwise set it to: \ \ max((A T), (NN MM)) * abs(H) LDA zTyreForceNoseHi,X \ Set A = zTyreForceNoseHi for tyre X JSR Absolute8Bit \ Set A = |A| \ = |zTyreForceNoseHi| STA T \ Set T = A \ = |zTyreForceNoseHi| LDA xTyreForceNoseHi,X \ Set A = xTyreForceNoseHi for tyre X JSR Absolute8Bit \ Set A = |A| \ = |xTyreForceNoseHi| CMP T \ If A < T then |xTyreForceNoseHi| < |zTyreForceNoseHi|, BCC tfor4 \ so jump to tfor4 to halve A \ If we get here then |xTyreForceNoseHi| >= \ |zTyreForceNoseHi|, so we halve T LSR T \ Set T = T >> 1 \ = |zTyreForceNoseHi| / 2 JMP tfor5 \ Jump to tfor5 .tfor4 LSR A \ Set A = A >> 1 \ = |xTyreForceNoseHi| / 2 .tfor5 CLC \ Set A = A + T ADC T \ \ So if |zTyreForceNoseHi| > |xTyreForceNoseHi|: \ \ A = |zTyreForceNoseHi| + |xTyreForceNoseHi| / 2 \ \ or if |xTyreForceNoseHi| >= |zTyreForceNoseHi|: \ \ A = |xTyreForceNoseHi| + |zTyreForceNoseHi| / 2 \ \ In other words: \ \ A = max(|xTyreForceNoseHi|, |zTyreForceNoseHi|) \ + min(|xTyreForceNoseHi|, |zTyreForceNoseHi|) / 2 .tfor6 CMP wingForce,X \ If A <> wingForce for tyre X, jump to tfor7 with the C BNE tfor7 \ flag set as follows: \ \ * Clear if A < wingForce \ \ * Set if A > wingForce CLC \ Clear the C flag for when A = wingForce, so in all we \ have the following for the C flag: \ \ * Clear if A <= wingForce \ \ * Set if A > wingForce .tfor7 ROR tyreSqueal,X \ Rotate the C flag into bit 7 of tyreSqueal for tyre X RTS \ Return from the subroutineName: ApplyTyreForces [Show more] Type: Subroutine Category: Driving model Summary: Calculate the tyre forces on the car Deep dive: The core driving model Skidding Matching the code to the driving modelContext: See this subroutine in context in the source code References: This subroutine is called as follows: * ApplyTyresAndSkids calls ApplyTyreForces
Calculate the following: * If xVelocity is non-zero and xVelocity and xTyreForceNose for tyre X have the same sign: * Set the high byte of xTyreForceNose = -xVelocity * 2^5 * Rotate a 1 into bit 7 of tyreSqueal for tyre X and finish * xTyreForceNose = -xVelocity * 2^5 * Call GetTyreForces to set (A T), H and (NN MM) * If the throttle is being applied and we are processing the front tyres, set: zTyreForceNose = 0 A = |xTyreForceNoseHi| otherwise: * If the throttle is being applied, then set: zTyreForceNose or zTyreForceRear = (A T) * abs(H) otherwise set: zTyreForceNose or zTyreForceRear = max((A T), (NN MM)) * abs(H) * Set the following (as appropriate for tyre X): A = max(|xTyreForceNoseHi|, |zTyreForceNoseHi|) + min(|xTyreForceNoseHi|, |zTyreForceNoseHi|) / 2 * Rotate a new bit 7 into tyreSqueal for tyre X as follows: * Clear if A <= wingForce for tyre X * Set if A > wingForce for tyre X
Arguments: X The set of tyres to process: * 0 = front tyres * 1 = rear tyres
[X]
Subroutine Absolute8Bit (category: Maths (Arithmetic))
Calculate the absolute value (modulus) of an 8-bit number
[X]
Subroutine ApplyLimitThrottle (category: Driving model)
Apply a maximum limit to a 16-bit number, unless the throttle is being applied
[X]
Subroutine GetTyreForces (category: Driving model)
Calculate the tyre forces due to the throttle or brakes
[X]
Subroutine Negate16Bit (category: Maths (Arithmetic))
Negate a 16-bit number
[X]
Label tfor1 is local to this routine
[X]
Label tfor2 is local to this routine
[X]
Label tfor3 is local to this routine
[X]
Label tfor4 is local to this routine
[X]
Label tfor5 is local to this routine
[X]
Label tfor6 is local to this routine
[X]
Label tfor7 is local to this routine
[X]
Variable tyreSqueal (category: Driving model)
A flag to determine whether the front or rear tyres are squealing
[X]
Variable wingForce (category: Driving model)
The downward force from the front and rear wings
[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 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 zTyreForceNoseHi (category: Driving model)
High byte of the z-coordinate of the force vector produced by the front tyres in the nose of the car
[X]
Variable zTyreForceNoseLo (category: Driving model)
Low byte of the z-coordinate of the force vector produced by the front tyres in the nose of the car