Hi all,
You've probably been wondering where my Dexter progress has gone since I haven't posted anything here in a long time.
Unfortunately, having a newborn baby is taking its toll on me and I haven't spent ANY time working on Dexter for several weeks. At CAX, I alluded to this during my presentation when I said that my wife was going to have a baby and it would impact my time to work on Dexter. Our baby just turned 3 months old and is still waking up 2-3 times in the middle of the night to eat and until that stops, I will need to use my spare time to catch up on sleep. I just wanted to let everyone know what is going on. The good news is that this 'wake up in the middle of the night' condition is ALWAYS temporary and goes away. :)
Tuesday, October 28, 2014
Tuesday, October 14, 2014
Dragon's Lair ticks
I haven't posted a blog entry in a while and this is something pretty interesting that I'd like to have saved somewhere so here goes.
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).
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;
Subscribe to:
Posts (Atom)