Prev: B059 Up: Map Next: B318
B063: Hero car jumps; gear changing; turbos; off road checks; speed adjustment; turning
Called every frame to advance the hero car's physical state. While the car is airborne the jump counter feeds the pitch and the vertical position from the jump table. The boost timer, the smoke counter and the gear change lockout are counted down here too.
The speed limit is computed from the gear, the boost and whether the car is off road, corrected by the pitch when the car is inclined, then the speed is clamped to 0..511. Left and right turning forces accumulate from the user's input and are capped against the current speed. Curvature scroll ticks become a road_pos delta. The last two things written are turn_speed (0/1/2) and flip_car, which the sprite renderer reads.
Used by the routines at main_loop and drive_attract_demo.
move_hero_car B063 LD A,$00 Load jump counter. Self modified by 8827 and B965. Highest is 8.
B065 AND A Jump to mhc_set_jump_offset if the jump counter is zero
B066 JR Z,mhc_set_jump_offset
Hero car is jumping.
mhc_jumping B068 DEC A Decrement the jump counter in (self modified) move_hero_car
B069 LD ($B064),A
B06C JR NZ,mhc_midair Jump to mhc_midair if we're still jumping
Hero car has landed.
mhc_landed B06E LD A,$03 smoke = 3
B070 LD ($A24F),A
B073 LD BC,$0203 Effect 2 (car landing), Priority 3
B076 CALL start_sfx Call start_sfx
Hero car is in mid-air, or has just landed.
mhc_midair B079 LD HL,$0000 Point HL at entry in jump(ing) table. Self modified by B96C and B092. Default is $B055.
B07C XOR A off_road = 0
B07D LD ($A23E),A
B080 LD A,($A0D5) Clear up/down/left/right bits of user input (stop the player from turning when in mid-air)
B083 AND $F0
B085 LD ($A0D5),A
The low bytes of the hero_car_jump_table entries are the car's pitch (0/3/6).
B088 LD A,(HL) Self modify the 'ADD A,x' at #$B5AF to load the car's pitch
B089 LD ($B5B0),A
B08C INC HL
The high bytes of the entries are the car's jump offset (-13..13).
B08D LD A,($B5AB) A = *$B5AB + *HL++ -- Self modified value in draw_hero_car
B090 ADD A,(HL)
B091 INC HL
B092 LD ($B07A),HL Update HL jump table entry address above @ mhc_midair
mhc_set_jump_offset B095 LD ($B5AB),A Self modify 'SUB x' @ B5AA to be the new car jump offset
B098 LD HL,$A24E Load address of boost timer
B09B LD A,(HL) Jump to mhc_smoke if a turbo boost is not in effect
B09C AND A
B09D JR Z,mhc_smoke
B09F DEC (HL) Decrement boost timer
B0A0 JR NZ,mhc_smoke Jump to mhc_smoke if boost timer is non-zero
B0A2 LD A,($A170) Decrement turbo boost count [POKE $B0A5,0 for Infinite turbos]
B0A5 DEC A
B0A6 LD ($A170),A
Handle smoke effect.
mhc_smoke B0A9 INC HL HL now points at smoke
B0AA LD A,(HL) Jump if smoke is zero
B0AB AND A
B0AC JR Z,mhc_gear_change
B0AE DEC (HL) Decrement smoke
Handle gear changes.
mhc_gear_change B0AF LD A,($A0D5) Read user input
B0B2 LD C,A Preserve it in C
B0B3 LD A,($B326) Read from the 'LD A,x' @ ahc_check_crashed (crashed flag)
B0B6 AND A Jump if zero
B0B7 JR Z,mhc_b0bd
Otherwise the crashed flag is set.
B0B9 LD A,C Change user input to be ONLY the fire/gear flag (if set)
B0BA AND $10
B0BC LD C,A
mhc_b0bd B0BD LD A,C A = C
B0BE PUSH AF Preserve the modified user input for later
B0BF BIT 4,A Test fire/gear flag
B0C1 LD HL,$A253 Load address of gear flag
B0C4 LD A,($A252) Load gear_lockout
B0C7 JR Z,mhc_b0d7 Jump if fire/gear is unset
Fire/gear was set.
B0C9 AND A Jump if gear_lockout is set -- lock out gear changes while set
B0CA JR NZ,mhc_b0d7
B0CC LD A,(HL) Toggle gear flag
B0CD XOR $01
B0CF LD (HL),A
B0D0 LD A,$04 Smoke time remaining
B0D2 JR Z,mhc_b0d7 Jump if low gear
B0D4 LD ($A24F),A Set smoke counter
mhc_b0d7 B0D7 SUB $01 Decrement A -- SUB, not DEC A, because JR C below tests the carry flag DEC A wouldn't set
B0D9 JR C,mhc_b0de Jump if negative result
B0DB LD ($A252),A gear_lockout = A
mhc_b0de B0DE LD A,(HL) Read current gear flag
B0DF EX AF,AF' Bank it
B0E0 LD HL,($A24A) Read current speed
B0E3 PUSH HL Stack current speed
B0E4 LD DE,$0078 Jump if speed >= 120
B0E7 SBC HL,DE
B0E9 JR NC,mhc_done_idle
Going slowly here (<120).
mhc_going_slow B0EB LD A,($A230) Jump if perp_caught_phase > 0 -- Don't do idle handling if we're slowing while catching the perp
B0EE AND A
B0EF JR NZ,mhc_done_idle
Handle idle timer.
mhc_idle_timer B0F1 LD HL,$A16E Load address of idle timer
B0F4 DEC (HL) Decrement idle timer
B0F5 JR NZ,mhc_done_idle Jump if non-zero
mhc_reset_idle_timer B0F7 LD (HL),$64 Reset idle timer to 100
B0F9 LD A,$0A Set priority to 10
B0FB LD HL,$98F8 Point HL at get_moving_chatter (Raymond: "LET'S GET MOVIN' MAN!")
B0FE CALL start_chatter Call start_chatter (priority 10)
mhc_done_idle B101 POP HL Pop current speed
B102 LD D,H Save current speed in DE for later
B103 LD E,L
B104 LD A,($A23E) Load the off-road flag
B107 AND A Jump if zero (fully on-road)
B108 JR Z,mhc_done_offroad
Handle off-road (A can be 1 or 2 here).
mhc_offroad B10A LD BC,$0078 Set max speed for when one wheel off-road (120 => ~100?)
B10D DEC A Jump if off-road was one
B10E JR Z,mhc_chose_offroad_speed
B110 LD BC,$006E Set max speed for when both wheels off-road (110 => ~90?)
mhc_chose_offroad_speed B113 AND A Clear carry
B114 SBC HL,BC 16-bit subtract only for result in flags
B116 LD H,D Restore current speed saved earlier
B117 LD L,E
B118 JR C,mhc_done_offroad Jump if current speed (HL) < max speed (DE) -- already within the off-road cap, so nothing to shed
Decelerate in proportion to speed while off-road: the delta is speed / 32, kept to four bits and forced to at least 1, then negated. If it's NOPped out then going offroad will cause max speed.
mhc_offroad_reduce_speed B11A LD A,L Current speed low byte
B11B RR H Bottom bit of H moves to carry
B11D RLA A <<= 4 -- 9 bit rotate left through carry
B11E RLA
B11F RLA
B120 RLA
B121 AND $0F A = -((A & $0F) | 1) -- top four bits of speed... at least one
B123 OR $01
B125 NEG
B127 LD C,A BC = $FF00 | A -- speed delta (slowing)
B128 LD B,$FF
B12A JP mhc_check_brake Jump to mhc_check_brake
mhc_done_offroad B12D LD A,($A24E) Load turbo boost time remaining (60..0)
B130 AND A Test
B131 EX AF,AF' Bank with flags
B132 AND A Test previously banked copy of gear flag
B133 JR NZ,mhc_high_gear Jump if in high gear
mhc_low_gear B135 LD BC,$01D6 BC = 470 -- max speed?
B138 EX AF,AF' Unbank turbo boost with flags
B139 JR NZ,mhc_low_gear_slowing Jump if turbo boost in effect -- Uses banked flags
No turbo boost.
B13B LD BC,$00E6 BC = 230 -- max speed?
mhc_low_gear_slowing B13E SBC HL,BC HL -= BC
B140 JP NC,mhc_low_gear_accelerating Jump if HL was >= BC -- speed > limit value
B143 LD A,L BC = ~HL + 1 = -HL
B144 CPL
B145 LD C,A
B146 LD A,H
B147 CPL
B148 LD B,A
B149 INC BC
B14A LD A,C A = C
B14B SRL B Shift a bit out of B into A?
B14D RRA
B14E SRL B and again -- B must now be zero
B150 RRA
B151 RRA Then divide by 4?
B152 RRA
B153 AND $3F C = (A & $3F) | 1
B155 OR $01
B157 LD C,A
B158 JR mhc_check_brake Jump to mhc_check_brake
mhc_low_gear_accelerating B15A LD A,L A = L
B15B SRL H A = HL >> 4
B15D RRA
B15E SRL H
B160 RRA
B161 RRA
B162 RRA
B163 AND $1F A = -((A & $1F) | 1)
B165 OR $01
B167 NEG
B169 LD C,A BC = $FF00 | A -- speed delta (slowing)
B16A LD B,$FF
B16C JR mhc_check_brake Jump to mhc_check_brake
mhc_high_gear B16E LD BC,$00DC BC = 220 -- max speed?
B171 SBC HL,BC 16-bit subtract only for result in flags
B173 LD H,D Restore current speed saved earlier
B174 LD L,E
B175 JR NC,mhc_high_gear_accelerating Jump if current speed (HL) < max speed (DE)
mhc_high_gear_slowing B177 LD BC,$01D6 BC = 470 -- max speed?
B17A EX AF,AF' Unbank turbo boost with flags
B17B JR NZ,mhc_low_gear_slowing Jump if turbo boost in effect -- Uses banked flags
B17D LD A,E A = DE >> 4
B17E LD B,D
B17F SRL B
B181 RRA
B182 SRL B
B184 RRA
B185 RRA
B186 RRA
B187 OR $01 C = (A | 1) & $1F
B189 AND $1F
B18B LD C,A
B18C JR mhc_check_brake Jump to mhc_check_brake
mhc_high_gear_accelerating B18E LD BC,$02B7 BC = 695 -- max speed?
B191 EX AF,AF' Unbank turbo boost with flags
B192 JR NZ,mhc_low_gear_slowing Jump if turbo boost in effect -- Uses banked flags
B194 LD BC,$0168 BC = 360 -- max speed?
B197 JP mhc_low_gear_slowing Use mhc_low_gear_slowing
mhc_check_brake B19A POP AF Get modified user input (stored by B0BE)
B19B PUSH AF
B19C RRA Is BRAKE pressed? (bit 2 / down)
B19D RRA
B19E RRA
B19F JR NC,mhc_not_braking Jump to mhc_not_braking if not pressed
Braking
B1A1 LD BC,$FFEC Speed delta is -20 to slow down
B1A4 JR mhc_calc_speed Jump to mhc_calc_speed
mhc_not_braking B1A6 RRA Is ACCELERATE pressed? (bit 3 / up)
B1A7 JR C,mhc_calc_speed Jump if so {what's in BC? must be the speed delta from earlier}
Not accelerating
B1A9 LD BC,$FFF6 Speed delta is -10 to gradually slow
mhc_calc_speed B1AC LD H,D Copy speed from DE
B1AD LD L,E
B1AE AND A Clear carry
B1AF ADC HL,BC Change speed
B1B1 JP P,mhc_speed_set Jump if still positive or zero
Cope with speed going negative
B1B4 LD HL,$0000 HL = $0000
HL = new speed
mhc_speed_set B1B7 LD A,($A24C) A = inclined_counter - 1 (SUB, not DEC A, because the JR NC below tests the carry flag DEC A wouldn't set)
B1BA SUB $01
B1BC JR NC,mhc_set_inclined Jump if A is now >= 0
"inclined" counter went -ve
B1BE LD A,($B5B0) A = $B5B0 -- read self modified value in draw_hero_car that sets the car's pitch (0/3/6)
B1C1 AND A Jump if zero (pitch is level)
B1C2 JR Z,mhc_set_inclined
B1C4 LD C,A C = A -- temp save A
B1C5 LD A,H Jump if HL (new speed) is zero
B1C6 OR L
B1C7 JR Z,mhc_set_inclined
new speed is non-zero
B1C9 LD A,C BC = (C - 5) | 1 -- 0/3/6 in C => -5/-1/1 in BC
B1CA SUB $05
B1CC OR $01
B1CE LD C,A
B1CF LD B,$00
B1D1 JP P,mhc_positive Jump if result is positive
B1D4 DEC B BC = $FF00 | A -- otherwise widen as negative
mhc_positive B1D5 LD D,H Copy speed from HL to DE
B1D6 LD E,L
B1D7 ADD HL,BC Increment speed ... by a value computed from car's pitch ... accelerating slowly when going upwards?
B1D8 LD BC,$02B7 Speed threshold 695
B1DB PUSH HL Save it
B1DC SBC HL,BC Reduce speed by threshold
B1DE POP HL Restore it
B1DF JR C,mhc_b1e2 Jump if speed < 695, which it always is. B1EC caps the stored speed at 511, the largest acceleration any path above adds is 63 and the pitch term here adds at most 1, so the speed cannot exceed 575 by this point
B1E1 EX DE,HL Unreachable: restore the pre-increment speed
mhc_b1e2 B1E2 LD A,$03 Set inclined to 3
mhc_set_inclined B1E4 LD ($A24C),A inclined_counter = A
B1E7 LD A,H A = H
B1E8 CP $02 CP 2
B1EA JR C,mhc_b1ef
B1EC LD HL,$01FF Cap speed to $1FF
mhc_b1ef B1EF LD ($A24A),HL Set speed to HL
B1F2 POP HL Restore HL -- the user input flags, popped earlier
B1F3 LD A,($A263) B = right_turn
B1F6 LD B,A
B1F7 LD A,($A264) C = left_turn
B1FA LD C,A
B1FB LD A,($B064) A = *$B064 -- Read jump counter [self modified]
B1FE AND A Jump to mhc_b253 if non-zero
B1FF JP NZ,mhc_b253
Testing the user input flags in H: steering is ignored while the car is airborne (jump counter non-zero, checked above)
B202 RR H Shift LSB out of H
B204 JR C,mhc_b21a Jump to mhc_b21a if set
B206 RR H Shift new LSB out of H
B208 JR C,mhc_b22c Jump to mhc_b22c if set
Reduce left turning force A = ((C >= 9) ? C - 9 : 0) = MAX(C - 9, 0) This instr is hit continuously
B20A LD A,C A = C - 9 -- left turning force from earlier
B20B SUB $09
B20D JR NC,mhc_store_left_turning_force Jump if C >= 9
B20F XOR A A = 0 -- clamp
mhc_store_left_turning_force B210 LD C,A C = A
Reduce right turning force A = ((B >= 9) ? B - 9 : 0) = MAX(B - 9, 0)
B211 LD A,B A = B - 9 -- right turning force from earlier
B212 SUB $09
B214 JR NC,mhc_store_right_turning_force Jump to mhc_b217 if B >= 9
B216 XOR A A = 0 -- clamp
mhc_store_right_turning_force B217 LD B,A B = A
B218 JR mhc_handle_speed Jump to mhc_handle_speed
B = ((B + 4 >= 36) ? 36 : B + 4) = MIN(36, B + 4) -- 36 is the max turning force
mhc_b21a B21A LD A,B A = B + 4
B21B ADD A,$04
B21D LD B,$24 Jump if A >= 36
B21F CP B
B220 JR NC,mhc_b223
B222 LD B,A B = A
C = ((C >= B) ? C - B : 0) = MAX(C - B, 0)
mhc_b223 B223 LD A,C C -= B
B224 SUB B
B225 LD C,A
B226 JR NC,mhc_handle_speed Jump to mhc_handle_speed if C >= B
B228 LD C,$00 C = 0
B22A JR mhc_handle_speed Jump to mhc_handle_speed
C = ((C + 4 >= 36) ? 36 : C + 4) = MIN(36, C + 4)
mhc_b22c B22C LD A,C A = C + 4
B22D ADD A,$04
B22F LD C,$24 C = 36
B231 CP C Jump to mhc_b235 if A >= 36
B232 JR NC,mhc_b235
B234 LD C,A C = A
B = ((B >= C) ? B - C : 0) = MAX(B - C, 0)
mhc_b235 B235 LD A,B B -= C
B236 SUB C
B237 LD B,A
B238 JR NC,mhc_handle_speed Jump to mhc_handle_speed if B >= C
B23A LD B,$00 B = 0
mhc_handle_speed B23C LD HL,($A24A) Load speed into HL
B23F LD A,L Speed low byte
B240 RR H Bottom bit of H moves to carry (H now unused)
B242 RRA Halve speed, shifting carry in as MSB
Divide by 2.666 but not quite right? Or is it twice that?
B243 SRL A A >>= 2
B245 SRL A
B247 LD L,A L = A
B248 SRL L L >>= 1
B24A ADD A,L A += L
B24B CP B Jump if A >= B
B24C JR NC,mhc_b24f
B24E LD B,A B = A
mhc_b24f B24F CP C Jump if A >= C
B250 JR NC,mhc_b253
B252 LD C,A C = A
mhc_b253 B253 PUSH BC Store turning forces for later mhc_reset_turning_forces
B254 LD BC,$0000 BC = $0000
B257 LD E,B E = B, i.e. zero
B258 LD A,($A25C) Load current_curvature
B25B OR A Set flags
B25C JP Z,mhc_straight_road Jump to mhc_straight_road if zero
B25F JP P,mhc_scroll_horizon Jump to mhc_scroll_horizon if positive
Negative scroll => scroll horizon right.
mhc_scroll_right B262 INC E E++ -- i.e. 1
B263 NEG A = -A
Positive scroll => scroll horizon left.
mhc_scroll_horizon B265 LD HL,$B827 horizon_table - 1: the curvature magnitude added next is 1-based, so curvature 1 selects the first byte
B268 LD C,A BC = A -- B is zero at this point
B269 ADD HL,BC HL += BC
B26A LD A,($A261) A = horizon_scroll_sub
B26D LD C,A C = A
B26E EX AF,AF'
B26F LD A,($A23F) A = fast_counter - C
B272 SUB C
B273 AND A Set flags
B274 JR Z,mhc_straight_road Jump if zero
B276 LD C,(HL) C = *HL
mhc_b277_loop B277 SUB C A -= C
B278 JR C,mhc_b280 Jump if carry
B27A INC B B++
B27B EX AF,AF'
B27C ADD A,C A += C
B27D EX AF,AF'
B27E JR mhc_b277_loop Jump to mhc_b277_loop (above)
mhc_b280 B280 LD A,B A = B
B281 AND A Jump to mhc_b295 if A is zero
B282 JR Z,mhc_b295
B284 LD HL,$A262 HL = curvature_ticks
B287 ADD A,(HL) A += *HL
B288 LD (HL),A *HL = A
B289 LD A,B A = B * 3
B28A ADD A,A
B28B ADD A,B
B28C LD B,$00 B = 0
B28E RR E Bottom bit of E moves to carry
B290 JR NC,mhc_b295 Jump if clear
B292 DEC B B--
B293 NEG A = -A
mhc_b295 B295 LD C,A C = A
B296 EX AF,AF' Bank the scroll amount just copied to C into A'. sh_curved_road in scroll_horizon reads it back; nothing between here and there executes EX AF,AF', so it survives. Only written when this path is reached, i.e. curvature is non-zero and ticks have elapsed
B297 LD ($A261),A horizon_scroll_sub = A [the value unbanked by the EX above]
No curvature - No scroll required?
mhc_straight_road B29A LD HL,($A25F) HL = horizontal_adjust + BC
B29D ADD HL,BC
B29E LD DE,$0000 horizontal_adjust = 0
B2A1 LD ($A25F),DE
B2A5 LD A,($B326) A = *$B326 -- Read self modified op in animate_hero_car -- crashed flag
B2A8 AND A Set flags
B2A9 JR Z,mhc_reset_turning_forces Jump to mhc_b2ad if zero
B2AB EX DE,HL DE <> HL
B2AC LD D,H D = H [must be a delta]
mhc_reset_turning_forces B2AD POP BC Restore turning forces
B2AE LD A,B right_turn = B
B2AF LD ($A263),A
B2B2 LD A,C left_turn = C
B2B3 LD ($A264),A
B2B6 SUB B A -= B
B2B7 JP P,mhc_b2bb Jump to mhc_b2bb if positive
B2BA DEC D Otherwise decrement D
mhc_b2bb B2BB LD E,A E = A >> 1
B2BC SRA E
B2BE ADD HL,DE HL += DE
B2BF OR A Set flags
B2C0 JR Z,mhc_set_cornering Jump to mhc_set_cornering if zero
B2C2 JP P,mhc_check_cornering Jump if positive
B2C5 NEG A = -A
mhc_check_cornering B2C7 CP $11 Compare A to 17
B2C9 LD A,$00 A = 0 -- cornering = 0
B2CB JR C,mhc_set_cornering Jump to mhc_set_cornering if A < 17
B2CD BIT 7,H Jump if H bit 7 clear
B2CF JR Z,mhc_b2db
B2D1 BIT 7,D Jump to mhc_set_cornering if D bit 7 set
B2D3 JR NZ,mhc_set_cornering
B2D5 BIT 7,H Jump to mhc_set_cornering if H bit 7 clear -- bug? already tested above
B2D7 JR Z,mhc_set_cornering
B2D9 JR mhc_is_cornering Jump to mhc_is_cornering
mhc_b2db B2DB BIT 7,D Jump to mhc_set_cornering if D bit 7 clear
B2DD JR Z,mhc_set_cornering
B2DF BIT 7,H Jump to mhc_set_cornering if H bit 7 set
B2E1 JR NZ,mhc_set_cornering
mhc_is_cornering B2E3 LD A,$01 A = 1
mhc_set_cornering B2E5 LD ($A24D),A cornering = A
B2E8 LD BC,($A26C) road_pos += HL [must be a delta]
B2EC ADD HL,BC
B2ED LD ($A26C),HL
B2F0 LD D,$01 D = 1 -- flip flag
B2F2 LD A,E A = E -- E holds half the (right_turn - left_turn) delta set at mhc_b2bb
B2F3 OR A Set flags -- OR A and AND A are equivalent here (both test sign/zero and clear carry); the choice is stylistic
B2F4 JP P,mhc_decide_turn_speed Jump if positive
B2F7 DEC D D-- -- flip flag -> 0
B2F8 NEG A = -A
mhc_decide_turn_speed B2FA CP $0C Compare A to 12
B2FC LD B,$02 2 => Turn hard
B2FE JR NC,mhc_exit Jump to mhc_exit if A >= 12
B300 DEC B 1 => Turn
B301 CP $06 Compare A to 6
B303 JR NC,mhc_exit Jump to mhc_exit if A >= 6
B305 DEC B 0 => Straight
mhc_exit B306 LD A,B turn_speed = B -- should be 0/1/2
B307 LD ($A250),A
B30A LD A,D flip_car = D -- should be 0/1
B30B LD ($A251),A
B30E LD A,($B064) A = *$B064 -- Read jump counter [self modified]
B311 AND A Return if zero
B312 RET Z
B313 XOR A cornering = 0 -- reset cornering if jumping
B314 LD ($A24D),A
B317 RET Return
Prev: B059 Up: Map Next: B318