Tuesday, October 28, 2014

Dexter update

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 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).

extraTicks = ticks / 30; /* (no remainder, for example, 31/30 would be 1, not 1.0333) */
adjustedTicks = ticks + extraTicks;
seconds = (adjustedTicks) * 0.032768;

Wednesday, September 17, 2014

Monday, September 1, 2014

Java annoyances

So I've wanted to learn java for years and am kinda forcing myself to do the desktop portion of the Williams IC Tester in java.

Quick take: C# is a better language in pretty much every way (which makes sense since MSFT shamelessly copied java and improved upon it).  And in some ways, C++ is better than java, which is why I am writing this blog post.

I discovered (re-discovered) that java does not have unsigned data types.  Everything is signed.  If you google for information about this, you will find java apologists demanding people to justify why unsigned data types are needed.  I found this attitude somewhat absurd.

Being an emulation author and a machine language enthusiast, unsigned data types are a no-brainer.  The benefit of unsigned types is that the maximum size of the type in question is doubled from the signed version (in the positive direction).  This benefit is something I take advantage of so often that I take it for granted.  Here is a quick example.

An unsigned byte has a range of 0-255, while a signed byte is -128 to 127.  I work with bytes a lot, by default, I expect to have a max upper range of 255.  I've expected this for years (many many years).  In fact, I can't remember the last time where I willingly chose to use a signed byte for any meaningful work.  If I am going to use a signed number for any purpose, I will almost always use whatever the native CPU is optimized for which means on a 32-bit CPU, I would use a 32-bit signed integer, on a 64-bit CPU, I would use a 64-bit signed integer, etc.  I can't think of any practical use for a signed byte when it's so common/easy to use an 'int'.  So the fact that java only supports signed bytes is a major blunder in the design, IMO.

Now, you may be saying that I can just ignore the sign for many operations and I will still get the same result.  This is somewhat true, or rather, would be somewhat true, if java didn't get in my way.

In C++, I am used to doing something like this:

uint8_t u8 = 0xFF; // C++ assigns this to have a value of 255
int i = u8;

^ - I would _always_ expect i to have a value of 0xFF after performing this operation.  It is a no-brainer.

However, to my dismay, I discovered that java does the wrong thing here:

byte u8 = 0xFF; // java assigns this to have a -1 value, even though it has a datatype called 'byte' hur hur
int i = u8; // now i is set to -1 also (0xFFFFFFFF) instead of 255 (0xFF).

To workaround this poor language design, one has to do this:

byte u8 = 0xFF;
int i = u8 & 0xFF; // hurrr hurrrrrr

Now it works correctly because I manually hacked the value to have the proper sign.  Nevermind that performing an extra AND for no practical reason wastes CPU cycles (admittedly not that many) that wouldn't have to be wasted if I could simply tell the language that u8 was supposed to be unsigned and to treat it accordingly.

What I've got from reading about java is that if you want to deal with unsigned types, you need to use a signed type that is bigger to spoof the unsigned type.  So if you want to represent an unsigned byte, you'd use a short, if you wanted to represent an unsigned short, you'd use an int, if you wanted to represent an unsigned int, then you'd have to go crazy and use a long.  It's really pathetic for any "serious" language to have to require this kind of hackery.

This doesn't mean that I've given up on java.  After all, C# is proprietary/MSFT so it's not really available on other platforms (don't get me started on mono).  So java still has its place, but I am not going to be fleeing C++ any time soon since java hasn't given me a great reason to do so.

Friday, August 29, 2014

Thursday, August 28, 2014

First Dexter rev 3b prototype finished

I've finished soldering the first rev 3b prototype and so far, I can't find any problems with it.  I have 3 more of these boards to assemble before ordering more from the fabrication place.

Friday, August 22, 2014

Soldering the next round of Dexter prototypes

The new board without any soldering done.

The new board with all of the surface mount ICs soldered in.  A few solder bridges, but no big deal.  All in all, the soldering took me about 2 hours.  I can't wait to have a machine assemble these :)