| Chase H.Q. | Routines |
| Prev: F95A | Up: Map | Next: FA72 |
|
This is the title screen's synthesised drum sound, dispatched as selector 3 of the three 1-bit sample engines run from titlescr_music (the title music/SFX driver) -- not an in-game engine/tyre sound. Repeatedly reads and updates self-modifying state bytes at $FA72-$FA74 (a running counter/pitch value nudged each call) to derive the wave, toggling port $FE (speaker/border) through busy-wait delay loops (play_drum_noise_burst_1/play_drum_noise_burst_2) whose lengths are driven by that state. Loops D=$32 times per call, and E times overall (FA6C), polling the frame flag (wait_for_frame_flag's $F8A8) to bail out early if a new frame has started.
A (-> E) is the pitch/rate parameter from titlescr_music (the dispatch byte's upper 5 bits). Two nested loops: outer E times (FA6C/$FA6D), inner D = 50 times each (pen_inner_loop_tail/$FA65); each inner iteration advances the 3-byte self-modified state at $FA72-$FA74 (a phase counter, a wrapping accumulator subtracting a fixed constant, and a rotating byte mixed back into the accumulator) and tests bit 4 of the result to decide whether to emit a click this iteration -- a bit-4 test on a steadily-advancing counter behaves like a variable duty-cycle gate, the source of the drum's buzz/rattle. When a click fires, the ON delay is $18-E cycles and the OFF delay is E cycles (play_drum_noise_burst_1/play_drum_noise_burst_2): a larger E (higher dispatch parameter) shortens the ON wait but lengthens the OFF wait, lowering the effective pitch. After each inner-loop pass, the frame flag ($F8A8, set by frame_interrupt_handler) is checked; note that the "AND A" immediately before "RET C" always clears the carry flag, so that RET C can never actually fire -- an apparent dead check preserved as found in the original code, not a translation artifact. Once the outer loop completes, control falls into wait_for_frame_flag to wait for the next frame.
Used by the routine at titlescr_music.
|
||||||||
| play_drum_noise_burst | FA3A | LD E,A | E = pitch/rate parameter; outer loop counter | |||||
| play_drum_noise_burst_0 | FA3B | LD D,$32 | D = 50; inner loop counter | |||||
| pen_inner_loop | FA3D | LD HL,$FA72 | Advance the 3-byte phase/accumulator state | |||||
| FA40 | INC (HL) | |||||||
| FA41 | INC (HL) | |||||||
| FA42 | INC (HL) | |||||||
| FA43 | LD B,(HL) | |||||||
| FA44 | INC HL | |||||||
| FA45 | LD A,(HL) | |||||||
| FA46 | SUB $8D | |||||||
| FA48 | LD (HL),A | |||||||
| FA49 | ADD A,B | |||||||
| FA4A | INC HL | |||||||
| FA4B | RLCA | |||||||
| FA4C | RRC (HL) | |||||||
| FA4E | ADD A,(HL) | |||||||
| FA4F | LD (HL),A | |||||||
| FA50 | AND $10 | Bit 4 of the result gates this iteration's click | ||||||
| FA52 | JR Z,pen_inner_loop_tail | |||||||
| FA54 | LD A,$18 | ON delay = $18 - E | ||||||
| FA56 | SUB E | |||||||
| FA57 | LD B,A | |||||||
| play_drum_noise_burst_1 | FA58 | DJNZ play_drum_noise_burst_1 | ||||||
| FA5A | LD A,$18 | |||||||
| FA5C | OUT ($FE),A | Speaker/border on | ||||||
| FA5E | LD B,E | OFF delay = E | ||||||
| play_drum_noise_burst_2 | FA5F | DJNZ play_drum_noise_burst_2 | ||||||
| FA61 | XOR A | |||||||
| FA62 | OUT ($FE),A | Speaker/border off | ||||||
| pen_inner_loop_tail | FA64 | DEC D | ||||||
| FA65 | JR NZ,pen_inner_loop | |||||||
| FA67 | LD A,($F8A8) | Frame flag check (see comment above: RET C here | ||||||
| FA6A | AND A | never fires, AND A always clears carry) | ||||||
| FA6B | RET C | |||||||
| FA6C | DEC E | Outer loop | ||||||
| FA6D | JR NZ,play_drum_noise_burst_0 | |||||||
| FA6F | JP wait_for_frame_flag | Done: wait for the next frame | ||||||
| Prev: F95A | Up: Map | Next: FA72 |