Friday, February 26, 2016

Fixing Dexter so that it works with Time Traveler

Some of you may know that Dexter's Time Traveler support has been "almost working" for a year or two.  Now that Dexter is finally shipping, I've gotten serious about fixing problems like this.

Time Traveler presents a unique challenge.  It uses an 80188 Intel CPU which I am not as familiar with as I am with CPUs like the z80, 6502, or 6809E.  I've made a few half-hearted attempts to disassemble the Time Traveler ROM in the past but always gave up due to the Intel 'segments' paradigm which is basically a hack that Intel added to the 8086 to allow it to address up to 1 megabyte of RAM.  This hack makes disassembly more difficult.

Things working in my favor are that Mark Broadhead had mostly got Time Traveler emulated in Daphne (how he managed to do this, I have no idea!).  I had made a few attempts to run Time Traveler in Daphne in the past, but gave up due to problems that I can't quite recall.

Things working against me are that I do not currently have a Time Traveler PCB and no schematics seem to exist (other than a few unhelpful ones).  Again, somehow Mark B was able to get the game mostly figured out without using schematics.  I am pretty impressed.

Here is what I have done:

I had only tested Time Traveler with Dexter a few times.  The most recent time was at CAX a few years ago.  I had done some logging on Dexter while it was plugged into Time Traveler and concluded that the game was trying to send an 0xE8 command to the Sony LDP-1450.  Unfortunately, 0xE8 is not a documented command.  Also unfortunate is that I did not bother to have anyone re-test this and have been operating under this assumption for years.  This assumption has proved to be incorrect.

I had a choice.  Do I try to send an 0xE8 command to my real Sony LDP-1450 and observe results? (this might have been a wise thing to do as it would've likely confirmed to me that this is indeed an invalid command)  Do I try to finish the Time Traveler emulation in Daphne so that I can get the game to send this bad command?  Or do I try (again) to disassemble the ROM and find where the bad command gets sent?

I decided that since I was dealing with an undocumented command (or so I thought), I would eventually have to disassemble the ROM.  I finally broke down and learned about 'segments' for 8086 era CPU's (such as Time Traveler's 80188).  And then I loaded the Time Traveler ROM into IDA and went to work.  It was a slow process, but I was able to disassemble all of the segments in the ROM and get it into a sane state.  *phew*

I noticed as I was going that the code seems to be pretty poorly optimized.  I deduced that this code was written in C and compiled by a pretty crappy C compiler.  Most likely it was built on an MS-DOS based system.  The compiler could've been anything from Microsoft QuickC, Turbo C, or any other MS-DOS 16-bit 8086 compiler of that area.  I really don't know.  But compilers have come a long way since this time, I can assure you :)

I speculate that the development version of this game ran on an MS-DOS PC plugged into a serial port.  And when they were ready to ship, they compiled a special embedded version.

I decided that first piece of code I wanted to find was the code that sends commands to the serial port.  Daphne made this easy.  I simply had to put a breakpoint on the function that sends a command to the Sony LDP-1450 interpreter, and then examine the x86 emulator to see where the program counter was located.  I did so, and found this little method:

It is nothing special, just a helper method to send an arbitrary byte to an arbitrary port.

I entered the x86 CPU debugger in Daphne and stepped out of this method.  I came out into this method:

After studying this method a bit both in IDA and the debugger, I concluded that this method takes a null-terminated byte array and sends it to the LDP-1450, then checks to make sure that the LDP-1450 has returned a 0x0A byte (which indicates success).  In other words, this method is probably a general purpose "send these bytes to the LDP" method.  I was getting closer.  Just had to step out one of the method one more time.

I was initially pretty confused about what this method did, but I realized later that it prints the words "SERIAL PORT" on the Time Traveler LEDs, then sends a 0x41 ("clear") command to the LDP-1450 (at d37e:0078), then sends "TESTING" to the LEDs, and loops until it gets a response from the LDP-1450.  Apparently this is where the game would get stuck if the LDP-1450 was not available or not communicating properly.

Coincidentally, around this time, someone who already had a Dexter decided to test Dexter with Time Traveler and informed me that the "undocumented command" problem occurs after successfully completing the first stage.

I fired Daphne up and played Time Traveler through to the end of the first stage and set a break point on any bad LDP-1450 commands.  Sure enough, after I completed the stage my breakpoint was hit.  But instead of seeing the bad command of 0xE8, the 'bad' command was 0x2B!  A quick check of the LDP-1450 manual shows this command to be "still step forward".  And sure enough, I had failed to implement this command in my LDP-1450 interpreter!

The good news is that this will be super easy to implement and I have hopes that Time Traveler will be fully functional after I am done.

1 comment: