.ApplyEngine LDA engineStatus \ If the engine is not on, jump to ProcessEngineStart to BEQ ProcessEngineStart \ process the key press for starting the engine, \ returning from the subroutine using a tail call LDA heightAboveTrack \ If heightAboveTrack <> 0, jump to CalcRevsNoTorque to BNE CalcRevsNoTorque \ calculate the revs and zero the engine torque, \ returning from the subroutine using a tail call LDA gearChangeKey \ If bit 7 of gearChangeKey is set then a gear change BMI CalcRevsNoTorque-2 \ key is being pressed, so jump to CalcRevsNoTorque-2 to \ set bit 7 of clutchEngaged to indicate that the clutch \ is not engaged, calculate the revs and zero the engine \ torque, returning from the subroutine using a tail \ call LDY gearNumber \ If gearNumber = 1, then we are in neutral, so jump to DEY \ CalcRevsNoTorque, returning from the subroutine using BEQ CalcRevsNoTorque \ a tail call \ If we get here then the engine is engaged and the car \ is on the track, so we need to calculate the engine \ torque and revs LDA playerSpeedLo \ Set (A T) = (playerSpeedHi playerSpeedLo) STA T LDA playerSpeedHi ASL T \ Set (A T) = (A T) << 1 ROL A PHP \ Store the flags on the stack, so we can retrieve it \ below BMI engi1 \ If bit 7 of (A T) is set, jump to engi1 to skip the \ following ASL T \ Set (A T) = (A T) << 1 ROL A \ \ so we only do this shift if we won't lose a bit off \ the left end of A .engi1 STA U \ Set U to the high byte of (A T), i.e. to the high byte \ of either speed * 2 or speed * 4 \ \ As the high byte in playerSpeedHi contains the speed \ in mph and the low byte contains the fraction, this \ just discards the fractional part of the calculation LDX gearNumber \ Set A to the gear ratio for the current gear, which is LDA trackGearRatio,X \ defined in the track data file at trackGearRatio JSR Multiply8x8 \ Set (A T) = A * U \ = trackGearRatio * high byte of (A T) \ = trackGearRatio * speed * 2 \ or trackGearRatio * speed * 4 ASL T \ Set (A T) = (A T) << 1 ROL A \ = trackGearRatio * speed * 4 \ trackGearRatio * speed * 8 PLP \ Retrieve the flags we stored on the stack above, and BPL engi2 \ if bit 7 is clear, skip the following, as we already \ doubled the result above ASL T \ Set (A T) = (A T) << 1 ROL A \ \ So we only do this shift if we didn't do it above, \ which means we have the following result, calculated \ in one of two ways to preserve accuracy: \ \ (A T) = trackGearRatio * playerSpeed * 8 .engi2 BIT clutchEngaged \ If bit 7 of clutchEngaged is clear then the clutch is BPL engi6 \ engaged, so jump to engi6 LDY throttleBrakeState \ If throttleBrakeState <> 1 then the throttle is not DEY \ being applied, so jump to engi4 to engage the clutch BNE engi4 LDY playerSpeedHi \ If playerSpeedHi >= 22, jump to engi4 to engage the CPY #22 \ clutch BCS engi4 LDY raceStarting \ Set Y = raceStarting BPL engi3 \ If bit 7 of raceStarting is clear, then we are not on \ the grid at the start of a race, so jump to engi3 to \ check the revs to decide if the clutch has engaged CPY #160 \ If raceStarting <> 160, then we are not showing the BNE engi4 \ blue lights at the start of the race, so jump to engi4 \ to engage the clutch \ If we get here then we are showing the blue lights at \ the start of the race, which we keep showing until the \ main loop counter is a multiple of 64 (at which point \ we show the green lights) PHA \ Store A on the stack to we can retrieve it below LDA mainLoopCounterLo \ If mainLoopCounterLo mod 64 < 53, clear the C flag AND #63 CMP #53 PLA \ Retrieve the value of A we stored on the stack above BCC engi4 \ If mainLoopCounterLo mod 64 < 53, then we have at \ least 63 - 53 = 10 main loop iterations (including \ this one) to go until the green lights appear, so jump \ to engi4 to engage the clutch .engi3 \ If we get here then we are either not on the starting \ grid, or we are on the starting grid, the blue lights \ are showing and we have fewer than 10 main loop \ iterations to go until the lights turn green \ Above, we set (A T) = trackGearRatio * speed * 8 CMP revsOnGearChange \ If A < revsOnGearChange, jump to engi5 to process the BCC engi5 \ clutch being disengaged \ Otherwise A >= revsOnGearChange, so the revs are \ higher than at the last gear change, so fall through \ into engi4 to engage the clutch .engi4 \ If we get here then we engage the clutch LDY #0 \ Set clutchEngaged = 0 to indicate that the clutch is STY clutchEngaged \ engaged BEQ engi6 \ Jump to engi6 (this BEQ is effectively a JMP as Y is \ always zero) .engi5 \ If we get here then the clutch is not engaged LDA revsOnGearChange \ Set A = revsOnGearChange CMP #108 \ If A < 108, i.e. revsOnGearChange < 108, jump to engi6 BCC engi6 \ to set the revs to this amount \ If we get here then revsOnGearChange >= 108 SEC \ Set revsOnGearChange = revsOnGearChange - 2 SBC #2 \ STA revsOnGearChange \ And fall through into engi6 to set revCount to the new \ value of revsOnGearChange .engi6 STA revCount \ Set revCount = A CMP #170 \ If A < 170, jump to engi7 to skip the following BCC engi7 \ instruction LDA #170 \ Set A = 170, so A has a maximum value of 170: \ \ A = max(170, revCount) .engi7 CMP #3 \ If A >= 3, jump to engi8 BCS engi8 INC engineStatus \ The rev count has fallen below 3, so increment \ engineStatus from &FF to 0, which turns the engine off JMP ZeroEngineTorque \ Jump to ZeroEngineTorque to set the engine torque to \ zero, returning from the subroutine using a tail call .engi8 SEC \ Set A = A - 66 SBC #66 \ = revCount - 66 BMI engi9 \ If A is negative (i.e. revCount < 66), jump to engi9 CMP #17 \ If A >= 17, jump to engi10 BCS engi10 .engi9 \ If we get here then either A < 0 or A < 17, so \ 3 <= revCount < 83 ASL A \ Set A = A * 2 + 152 CLC ADC #152 JMP engi13 \ Jump to engi13 .engi10 \ If we get here then A >= 0 and A >= 17, so \ revCount >= 83 SEC \ Set A = A - 17 SBC #17 \ = revCount - 66 - 17 \ = revCount - 83 CMP #4 \ If A >= 4 (i.e. revCount >= 87), jump to engi11 BCS engi11 \ If we get here then A is in the range 0 to 3 and \ 83 <= revCount < 87 EOR #&FF \ Set A = ~A, so A is in the range 255 to 252 CLC \ Set A = A + 187 ADC #187 \ = ~A + 187 \ = -A - 1 + 187 \ = 186 - A \ = 186 - (revCount - 83) BCS engi13 \ Jump to engi13 (the BCS is effectively a JMP as the \ above addition will always overflow) .engi11 \ If we get here then A >= 4, so revCount >= 87 SEC \ Set A = A - 4 SBC #4 \ = revCount - 83 - 4 \ = revCount - 87 CMP #5 \ If A >= 5, (i.e. revCount >= 92), jump to engi12 BCS engi12 \ If we get here then A is in the range 0 to 5 ASL A \ A = ~(A * 4) ASL A EOR #&FF CLC \ Set A = A + 183 ADC #183 \ = ~(A * 4) + 183 \ = -(A * 4) - 1 + 183 \ = 182 - (A * 4) \ = 182 - ((revCount - 87) * 4) BCS engi13 \ Jump to engi13 (the BCS is effectively a JMP as the \ above addition will always overflow) .engi12 \ If we get here then A >= 5, so revCount >= 92 SEC \ Set A = ~((A - 5) * 2) SBC #5 ASL A EOR #&FF CLC \ Set A = A + 163 ADC #163 \ = ~((A - 5) * 2) + 163 \ = -((A - 5) * 2) - 1 + 163 \ = 162 - (A - 5) * 2 \ = 162 - ((revCount - 87) - 5) * 2 \ = 162 - (revCount - 92) * 2 .engi13 STA U \ Set U to A LDA trackGearPower,X \ Set A to the gear power for the current gear, which is \ defined in the track data file at trackGearPower JSR Multiply8x8 \ Set (A T) = A * U \ = trackGearPower * U \ Fall through into SetEngineTorque to set the engine \ torque to the high byte in AName: ApplyEngine [Show more] Type: Subroutine Category: Driving model Summary: Apply the effects of the engine Deep dive: The core driving model Modelling the engine Matching the code to the driving modelContext: See this subroutine in context in the source code References: This subroutine is called as follows: * ApplyDrivingModel calls ApplyEngine
Calculate the following: * If the engine is not on, jump to ProcessEngineStart * If heightAboveTrack <> 0, jump to CalcRevsNoTorque to calculate the rev count and zero the engine torque * If a gear change key is being pressed, jump to CalcRevsNoTorque-2 to set bit 7 of clutchEngaged to indicate that the clutch is not engaged, calculate the rev count and zero the engine torque * If we are in neutral, jump to CalcRevsNoTorque to calculate the rev count and zero the engine torque * Otherwise, calculate the engine torque based on gear ratio, power and revs, setting the engineTorque, revCount and soundRevTarget variables as follows: * Set (A T) = trackGearRatio * playerSpeed * 8 * If clutch is not engaged: * If any of these are true: * Throttle is not being applied * playerSpeedHi >= 22 * This is a race and we are not showing the blue lights * This is a race, we are showing the blue lights and still have 10 iterations until the green * A >= revsOnGearChange, so the revs are higher than at the last gear change then engage the clutch, otherwise clutch stays disengaged and we do: A = revsOnGearChange If A >= 108, A = A - 2 and revsOnGearChange = revsOnGearChange - 2 * Set revCount = A * We now calculate the power being generated by the engine at this rev count. First, we cap the rev count to a maximum of 170: max(170, revCount) so in the following revCount is this capped number. * If revCount < 3, turn the engine off, zero the torque and quit * If 3 <= revCount < 83, set A = A * 2 + 152 * If 83 <= revCount < 87, set A = 186 - (revCount - 83) * If 87 <= revCount < 92, set A = 182 - ((revCount - 87) * 4) * If 92 <= revCount, set A = 162 - (revCount - 92) * 2 * Set engineTorque = trackGearPower * A * Set soundRevTarget = revCount + 25
[X]
Subroutine CalcRevsNoTorque (category: Driving model)
Calculate the value of the rev counter according to the throttle being applied and zero the engine torque
[X]
Entry point CalcRevsNoTorque-2 in subroutine CalcRevsNoTorque (category: Driving model)
Set clutchEngaged to A before running the routine
[X]
Subroutine Multiply8x8 (category: Maths (Arithmetic))
Calculate (A T) = T * U
[X]
Subroutine ProcessEngineStart (category: Driving model)
Process the key press for starting the engine
[X]
Subroutine ZeroEngineTorque (category: Driving model)
Zero engineTorque
[X]
Variable clutchEngaged in workspace Zero page
Determines whether the clutch is engaged
[X]
Label engi1 is local to this routine
[X]
Label engi10 is local to this routine
[X]
Label engi11 is local to this routine
[X]
Label engi12 is local to this routine
[X]
Label engi13 is local to this routine
[X]
Label engi2 is local to this routine
[X]
Label engi3 is local to this routine
[X]
Label engi4 is local to this routine
[X]
Label engi5 is local to this routine
[X]
Label engi6 is local to this routine
[X]
Label engi7 is local to this routine
[X]
Label engi8 is local to this routine
[X]
Label engi9 is local to this routine
[X]
Variable engineStatus in workspace Zero page
Whether or not the engine is on
[X]
Variable gearChangeKey in workspace Zero page
Determines whether or not a gear change key has been pressed
[X]
Variable gearNumber in workspace Zero page
The current gear number
[X]
Variable heightAboveTrack in workspace Zero page
The 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 mainLoopCounterLo in workspace Zero page
Low byte of the main loop counter, which increments on each iteration of the main driving loop
[X]
Variable playerSpeedHi in workspace Zero page
High byte of the speed of the player's car along the track
[X]
Variable playerSpeedLo in workspace Zero page
Low byte of the speed of the player's car along the track
[X]
Variable raceStarting in workspace Zero page
The current stage of the starting lights at the start of the race
[X]
Variable revsOnGearChange in workspace Zero page
The rev count when the gear was last changed
[X]
Variable throttleBrakeState in workspace Zero page
Denotes whether the throttle or brake are being applied
[X]
Variable trackGearPower in workspace trackData
The power for each gear
[X]
Variable trackGearRatio in workspace trackData
The gear ratio for each gear