Dragon's Lair's scene data has a concept of 'ticks' which roughly map to clock ticks generated by the hardware. They are used to determine how long scene segments should last.
One issue with Dragon's Lair hardware is that the clock tick frequency differs slightly across different revisions of the hardware. Dragon's Lair's ROM program does a speed test upon boot-up to determine whether the hardware clock is faster, normal, or slower. The result of this speed test is displayed in the scoreboard as a 1 (faster), 2 (normal), or 3 (slower).
Here is the disassembled code that documents this speed test:
ROM:11BA SpeedTestLoop: ; CODE XREF: START+7B j
ROM:11BA ; START+88 j ...
ROM:11BA ld hl, SpeedTest_A001 ; when this loop starts for the first time, HL is FFFF, DE is FFFF, and BC is 0000
ROM:11BD bit 1, (hl)
ROM:11BF jr nz, SpeedTestGotIrq ; if this is non-zero, it means IRQ has been triggered
ROM:11C1 inc bc
ROM:11C2 jr SpeedTestLoop ; when this loop starts for the first time, HL is FFFF, DE is FFFF, and BC is 0000
ROM:11C4 ; ---------------------------------------------------------------------------
ROM:11C4
ROM:11C4 SpeedTestGotIrq: ; CODE XREF: START+78 j
ROM:11C4 res 1, (hl) ; clear IRQ flag
ROM:11C6 inc d
ROM:11C7 ld a, 48 ; don't start tracking IRQs until we've got 48 of them
ROM:11C9 cp d
ROM:11CA jr c, loc_11D1
ROM:11CC ld bc, 0
ROM:11CF jr SpeedTestLoop ; when this loop starts for the first time, HL is FFFF, DE is FFFF, and BC is 0000
ROM:11D1 ; ---------------------------------------------------------------------------
ROM:11D1
ROM:11D1 loc_11D1: ; CODE XREF: START+83 j
ROM:11D1 ld a, 50
ROM:11D3 cp d
ROM:11D4 jr nz, SpeedTestLoop ; when this loop starts for the first time, HL is FFFF, DE is FFFF, and BC is 0000
ROM:11D6 ld a, c
ROM:11D7 ld (unk_E035), a ; least significant digit from BC
ROM:11DA srl a
ROM:11DC srl a
ROM:11DE srl a
ROM:11E0 srl a
ROM:11E2 ld (unk_E034), a
ROM:11E5 ld a, b
ROM:11E6 ld (unk_E033), a
ROM:11E9 srl a
ROM:11EB srl a
ROM:11ED srl a
ROM:11EF srl a
ROM:11F1 ld (unk_E032), a ; most significant digit from BC
ROM:11F4 ld a, 0Ah
ROM:11F6 cp b
ROM:11F7 jr c, BcGreaterThanOrEq0A00 ; if BC >= 0x0A00 then branch
ROM:11F9 set 3, (hl) ; set bit 3 of A001 to indicate that speed test's result is 'faster'
ROM:11FB ld a, 1 ; speed test got a '1' result
ROM:11FD jr SpeedTest1Or2Result ; set bit 0 of A001 to indicate the speed test's result is a 1 or 3
ROM:11FF ; ---------------------------------------------------------------------------
ROM:11FF
ROM:11FF BcGreaterThanOrEq0A00: ; CODE XREF: START+B0 j
ROM:11FF ld a, 15h ; check to see if BC >= 0x1500
ROM:1201 cp b
ROM:1202 jr c, SpeedTest3Result ; branch if BC >= 0x1500
ROM:1204 ld a, 2 ; speed test got a '2' result
ROM:1206
ROM:1206 SpeedTest1Or2Result: ; CODE XREF: START+B6 j
ROM:1206 set 0, (hl) ; set bit 0 of A001 to indicate the speed test's result is a 1 or 3
ROM:1208
ROM:1208 ExitSpeedTest: ; CODE XREF: START+CC j
ROM:1208 ld (unk_E038), a ; set speed test result to scoreboard
ROM:120B set 2, (hl) ; set bit 2 of A001 to indicate that the speed test is finished
ROM:120D jr BootPhase2 ; play 'move' beep
ROM:120F ; ---------------------------------------------------------------------------
ROM:120F
ROM:120F SpeedTest3Result: ; CODE XREF: START+BB j
ROM:120F res 0, (hl) ; clear bit 0 to indicate that speed test result is slower
ROM:1211 ld a, 3 ; display a 3 on scoreboard
ROM:1213 jr ExitSpeedTest ; set speed test result to scoreboard
ROM:1215 ; ---------------------------------------------------------------------------
So what does the result of the speed test mean for the ticks?
If the speed test got a 1 (faster) or a 2 (normal), then every 31st tick is dropped. If the speed test got a 3 (slower) then every 255th tick is dropped. Here is the code which handles this:
ROM:1922 ld hl, SpeedTest_A001 ; bit 0: 1=drop every 31st tick (speedtest result was 1 of 2), 0=drop every 255th tick (speedtest result was 3)
ROM:1922 ; bit 1: (during speed test) 1=IRQ has been triggered, 0=IRQ has not been triggered yet
ROM:1922 ; bit 2: 1=normal operation, 0=speedtest active
ROM:1922 ; bit 3: 1=speedtest got a '1' (faster), 0=speedtest got a '2' or '3' (just right or slower respectively), see 11F9
ROM:1925 bit 0, (hl)
ROM:1927 jr z, DropEvery255thTick ; drop every 255th tick
ROM:1929 ld b, 31 ; drop every 31st tick
ROM:192B jr UpdateGlobalTickCounter
ROM:192D ; ---------------------------------------------------------------------------
ROM:192D
ROM:192D DropEvery255thTick: ; CODE XREF: IRQ+107 j
ROM:192D ld b, 255 ; drop every 255th tick
ROM:192F
ROM:192F UpdateGlobalTickCounter: ; CODE XREF: IRQ+10B j
ROM:192F ld hl, GlobalTickCounter_A000 ; keeps a count of how many ticks we've processed so that we can drop ticks from time to time
ROM:1932 ld a, (hl)
ROM:1933 inc a ; increment tick counter
ROM:1934 ld (hl), a
ROM:1935 cp b ; do we need to drop this tick?
ROM:1936 jr nz, UpdateAllTickCounters
ROM:1938 xor a
ROM:1939 ld (hl), a
ROM:193A jp PostCountersUpdate ; we go here after the tick counters have been updated
ROM:193D ; ---------------------------------------------------------------------------
ROM:193D
ROM:193D UpdateAllTickCounters: ; CODE XREF: IRQ+116 j
Additionally, the IRQ service routine will periodically skip certain work if the speed test was a '1' in order to 'slow down' the timer in software.
Therefore, to reliably understand how many seconds a certain number of 'ticks' mean in the Dragon's Lair move data, one can apply this algorithm:
Assume that the hardware clock 'ticks' every 32.768 milliseconds and that the CPU is 4 MHz. This will yield a speed test result of 2 (normal).
extraTicks = ticks / 30; /* (no remainder, for example, 31/30 would be 1, not 1.0333) */
adjustedTicks = ticks + extraTicks;
seconds = (adjustedTicks) * 0.032768;
Hi Matt,
ReplyDeleteGreat stuff!
I would like to add that '3' is a Rev-A board,
a '2' is a normal Rev-C board,
and a '1' is a Rev-C board that has had it's "Real Time Clock" (tick maker) multiplied by 8.
If the board is a '1', the ISR will process the coin drop every time, but process all of timers only one out of 8 times.
Here is the code to skip most of the ISR 7 out of 8 times...
------------------------------------------------------------------------------
18e1: ld a,(0a001h)
18e4: bit 3,a // test for modified Rev-C
18e6: jr z,18f4h
// fast board - do ISR only one of 8 times
18e8: ld a,(0a05ch)
18eb: inc a
18ec: ld (0a05ch),a
18ef: and 07h
18f1: jp nz,19f1h // exit
--------------------------------------------------------------------------------
We just don't know why they needed to check for a coin drop so frequently.
-Shaun
Excellent! I didn't get into the ISR enough to see exactly what the 'fastest' behavior was. I did notice the "and 07h" and thought "What on earth are they doing there??" I'm glad you shed some light on it. So when TMV said that the clock was 8x as fast, he was being literal. I didn't realize that.
DeleteYou ever see how fast a kid can cram tokens into a coin mech Shaun? LOL
Delete