Showing posts with label daphne. Show all posts
Showing posts with label daphne. Show all posts

Friday, January 8, 2021

Dragon's Lair's software watchdog

I've been rewriting Daphne's Z80 emulator (from scratch) and testing it on Dragon's Lair.  I've got it far enough along so that it loops at the disc routine at 0x231 waiting for an LD-V1000 strobe.  While I had it looping, I was checking out some performance metrics to see how fast my new Z80 emu was running.  To my shock, after about 1 minute, program execution moved away from that endless loop.  After a few hours of tracking down why, I discovered this software watchdog that I never knew existed.


(my disassembly below)

ROM:19E3 DecrementWatchdog:                      ; CODE XREF: IRQ+1BC↑j
ROM:19E3                 ld      hl, A0DE_WatchDogCounter
ROM:19E6                 call    DecrementMem16  ; Decrements a 16-bit memory location
ROM:19E6                                         ; Flags are set according to SBC rules.
ROM:19E6                                         ; HL: pointer to 16-bit memory value
ROM:19E6                                         ; DE and BC are clobbered
ROM:19E9                 jr      nc, CheckSoundThenExitIrq
ROM:19EB                 jp      DoSoftReset     ; watchdog has not been fed. we're stuck in an endless loop.


ROM:1FCF DoSoftReset:                            ; CODE XREF: IRQ+1CB↑j
ROM:1FCF                 ld      sp, 0A3FFh      ; blow away all stack info; start over.
ROM:1FD2                 ld      hl, 0A030h
ROM:1FD5                 ld      de, 0A031h
ROM:1FD8                 ld      bc, 3CFh
ROM:1FDB                 ld      (hl), 0
ROM:1FDD                 ldir                    ; this appears to clear mosts of the game data except the boot-up stuff
ROM:1FDF                 call    ResetWatchdogCounter ; resets watchdog counter back to max value (about 70 seconds)
ROM:1FDF                                         ; (this must be called regularly to avoid a soft system reset)
ROM:1FE2                 ei
ROM:1FE3                 jp      loc_1244        ; this is an alternative to "RETI"; essentially giving up on recovering


ROM:1C2D ; =============== S U B R O U T I N E =======================================
ROM:1C2D
ROM:1C2D ; resets watchdog counter back to max value (about 70 seconds)
ROM:1C2D ; (this must be called regularly to avoid a soft system reset)
ROM:1C2D
ROM:1C2D ResetWatchdogCounter:                   ; CODE XREF: START+10↑p
ROM:1C2D                                         ; sub_15B4+F5↑p ...
ROM:1C2D                 ld      hl, 8AAh
ROM:1C30                 ld      (A0DE_WatchDogCounter), hl
ROM:1C33                 ret
ROM:1C33 ; End of function ResetWatchdogCounter
ROM:1C33
ROM:1C34
ROM:1C34 ; =============== S U B R O U T I N E =======================================

Thursday, January 2, 2020

My new Z80 emulator

So five years ago, I started speculating about how emulators could be improved and came up with several ideas.  One of them was to emulate the clock pin on CPUs (and other devices such as flip-flops or the PIA6821) for maximum accuracy.  I speculated that this would take a major performance hit but perhaps with multi-threading technology, this performance hit could be offset.

Well, I've been working on this secretly for several months now.  I've emulated almost every TTL chip on the Dragon's Lair logic board as a standalone device and tied them all together.  Performance at the moment is barely adequate on modern beefy hardware (75% CPU usage on a single thread on an AMD Ryzen) and not adequate on a Raspberry Pi 4.

I experimenting with using multi-threading solutions to speed this up, but the critical timing and the need to keep the whole system running in lock-step made me conclude that this must be run on a single thread.  I'll keep looking at ways to use other threads to offload things like video and sound processing which I think will work fine.

So the good news is that I believe I'll be able to design the "perfect" Dragon's Lair emulator and it will be able to run at full speed on modern CPUs (such as the aforementioned Ryzen) and possibly even older CPUs like the Intel i5.  When I say perfect, I mean that every single pin on every single IC in the system (including the Z80) will be emulated and have the exact timing of original hardware.  Where a device is digital, there will be no shortcuts.  (analog devices such as the sound chip will not be held to this same standard of perfection).  I'm pretty excited about this because I sometimes study/repair original game board sets and not having the ability to study how the hardware is supposed to work via emulation has been quite inconvenient.

The bad news is that Dragon's Lair is the simplest of laserdisc games, so a game like Star Rider (which has three 6809E CPUs and a ton of TTL chips) probably won't able to run at full speed even on the newest of hardware using this approach.  I'll be looking at thoughtful optimizations/compromises to make for these scenarios since obviously with enough compromises, any of these old games could be made to run at full speed.

Here's a brief description of how the Z80's pins work from an arbitrary instruction:


Here's every single pin of a Z80 captured via my logic analyzer.  When I embarked on the journey to emulate this beast, I thought "this is insane.  it will take so long to figure this stuff out."  but.. this is the kind of emulator I want Daphne to be, so I did it anyway.  now it's starting to make sense.

I had to do two separate captures because my logic analyzer can capture 32 pins at a time (which is a ton!) and the z80 has 40 pins.  that alone almost made it give up.

I've put colored lines to show how this instruction (LD (HL), 00h) works.

First section ("M1") is loading the instruction's opcode from the ROM program.  It sets the address to 1153h, lowers RD and MREQ lines, then the EPROM puts 36h on the data bus.  Z80 then raises RD/MREQ, then lowers MREQ/REFRESH, putting the number 0005h on the address bus which is used for dynamic ram to refresh itself (I think dragon's lair ignores it because it uses static ram).  That takes 4 clock cycles and is known in Z80 speak as "M1".  Next section ("M2") it needs to read the rest of the instruction, so it sets address to 1154h, lowers RD/MREQ again and the EPROM puts 00h on the data bus.  M2 is complete.  Now it has the full instruction.  It goes to the next section ("M3").  HL happens to have a value of A000h, so it sets the address bus to A000h, lowers MREQ but instead of lowering RD, it puts 00h on the data bus and lowers WR which tells the rest of the system that "Hey, I want to write a 00h to address A000h".  The rest of the system's hardware maps that address to the RAM and the RAM wakes up and grabs the data from the data bus and stores it.

The amazing thing is... how did they design the Z80 in 1976 or whenever?

Wednesday, March 9, 2016

Emulation vs FPGA

I was thinking about this on my drive to work this morning and decided to write my thoughts down while they are still fresh in my head.

I've been hearing people talk about "one to one" (ie 1:1) or "not emulation" when talking about FPGAs.  What do they mean when they says this?

I've talked about this in previous posts, but it's been a while and now may be a good time for a refresher.

The way that CPU's in classic 80's arcade games work is that they all require a clock which alternates between a high and low state at a frequency usually in the low megahertz range (ie 1 MHz).  They also usually have at least 16 address lines and at least 8 data lines.  A few CPUs had more than this, but I will focus on the CPUs that have 16 address lines, 8 data lines, and a single clock input.

This is your typical 8-bit CPU that can address 64k of memory.  How did I conclude that it can access 64k of memory?  Because it has 16 address lines, and 2 to the 16th power is 65536 which is 64k (65536 / 1024 is 64, and 1k is 1024 bytes).  How did I conclude that it's an 8-bit CPU?  Because it has 8 data lines which is usually what people refer to when they talk about an "8-bit", "16-bit", "32-bit", or "64-bit" CPU.

The CPU's 16 address lines are always outputs, meaning that the CPU is the only device in the computer that is allowed to change its address.  The CPU's 8 data lines can be either inputs or outputs.  The CPU has other lines on it which indicate whether it is trying to read from its data lines or write to its data lines.

The CPU has a very small number of internal memory buffers called registers, but almost all of the memory that the CPU needs comes from either external ROM or external RAM.  When the CPU wants to execute an instruction (where a complete program consists of many instructions), it has to fetch the instruction from an external dependency (usually ROM or RAM).  It does this by setting its address lines to the address that it wants to fetch the memory from, and then setting its read/write lines to let the rest of the computer know that it wants the byte stored at the address on its address lines.  On power-up, the CPU will set its initial address to a fixed, known location and either start executing instructions from that location or read an address from that location, jump to that address, and start executing instructions.

Each CPU instruction takes a specific number of clock cycles to complete.

So what does all of this have to do with emulation vs FPGA ?

If I were trying to implement a CPU inside of an FPGA, I could (if I do it correctly) have the FPGA's input/output lines behave exactly like the original CPU.  And if the FPGA's I/O behaves exactly like the original CPU's I/O, then the FPGA could be considered to be 100% compatible with the original CPU and one could use it as a drop-in replacement for the CPU without sacrificing any sort of accuracy.  This is what people mean by "one to one."  I should note that from what I have seen of efforts to implements CPU's inside of FPGA's, I am not convinced that the people who put forth the effort were terribly concerned about accuracy, and may have been more concerned about making the FPGA version CPU perform faster than the original.  So just because an FPGA could in theory be a 100% accurate replacement for the CPU does not mean that this is what is happening.

Now, what might people mean when they say "not emulation" ?

CPU emulators, such as what one may find in MAME, take a few shortcuts in order to achieve decent performance.  They do not emulate the clock of the CPU, but instead are designed to execute a variable number of cycles in one shot as quickly as the host machine (ie a modern x64 computer) can execute.  The code that is driving this execution is then responsible to regulate the overall speed of the system so that it does not run too quickly.

For example, let's say we are emulating a 1 MHz Z80 cpu.  The cpu management code may tell a z80 emulator to execute 1000 cycles which would take 1 ms on original hardware.  The z80 emulator would then go execute these 1000 cycles as fast as possible and report back how many cycles were actually emulated (it might be more than 1000 because some instructions take more than 1 cycle).  The cpu management code would then have to stall until the 1 ms period has completed before executing the next chunk of cycles.

This creates an unauthentic experience because it means that instead of instructions being executed at a steady slower cadence, they are executed in quick bursts with delays in between.  This is usually not noticeable by a human playing the emulated game because it's just 1 ms, but several problems can arise depending on the other architecture of the original hardware.

On a game like Dragon's Lair where there is just one CPU and a steady clock that never varies, the above method of emulation is "good enough."  A human is not really going to notice any meaningful difference in accuracy.

But what of the game has multiple CPUs such as a dedicated sound CPU?  Now the emulator has to execute a smaller slice of cycles on the first CPU, then switch to the second CPU and execute another smaller slice of cycles.  If there are interactions between these two CPUs (and there usually will be), the slice of cycles that gets executed needs to be small enough so that there is no unnatural lag in the interactions which hurts performance of the overall system.  And even if each CPU takes turns executing just 1 cycle, the potential for an inaccurate interaction between the two emulated CPUs still exists since the emulator does not take into account the clock.

Now, what if the original hardware fiddles with the CPUs clock, or the clock is not constant for some reason?  The Williams games are notorious for doing this.  On a lot of the Williams games, like Joust, their custom DMA chip will actually halt the CPU while the DMA operation is running.  On Star Rider, the CPU's clock gets halted every field for unknown reasons (that's on my TODO list to figure out why).  Last time I checked, this behavior was not emulated very well in MAME (it may have improved since I last checked) and certainly Daphne is not equipped to handle this type of scenario.  However, an FPGA would be able to handle it just fine.

Now, does this mean that emulators like MAME and Daphne can't be improved to take into account a variable clock?  Not at all.  As modern computers get faster, it will become more feasible for emulators to become more accurate without hurting performance.  I believe that aside from the problems associated with running on a modern operating system (with many processes and threads all competing for the CPU's time), there is no reason why software-based emulators cannot achieve 100% accuracy as their architectures are improved.  However, I do not believe that that day is here... yet.

I hope that gives people a better idea of what I consider the difference to be between FPGA solutions and emulation solutions.

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.

Monday, February 8, 2016

Software Development Insights: Coupling and why it is bad

Ever heard the term 'coupling' before?  I am not fond of it and I go into ridiculous amounts of detail ranting about why it's bad :)  I also talk about what enums are, touch on if/else "nesting", and unit testing.  All related to work on Daphne/Dexter.


Sunday, February 7, 2016

Software Development Insights: Why hard-coding numbers is a Bad Idea

Ever written something like this?

int count = 2;
int count2 = 4;
int count3 = 6;

Watch this stunningly insightful video (sarcasm) to find out why this is a Bad Idea.


Saturday, February 6, 2016

Dexter disc auto-swapping is getting closer!

Dexter disc auto-swapping is becoming closer to being a reality.  Here is a video showing me demo'ing the feature in Daphne.


Software Development Insights: Debugging multiple threads on a raspberry pi (Dexter/Daphne)

Wanna see how to debug threads on a raspberry pi?  Or do you like anything geeky related to Daphne and Dexter?  This video may be of interest to you.


Friday, February 5, 2016

Software development insights: Debugging OpenMax problem related to Dexter

If you are an aspiring software developer, here is a 30 minute video I just recorded of me troubleshooting a software problem with Dexter.  It could be interest or boring depending on what you are looking for, haha.


Monday, October 26, 2015

Dragon's Lair Movie Kickstarter!

If you like laserdisc games (And I am assuming you do since you are reading this), go here and help support this project!

Thursday, July 9, 2015

How to reverse engineer arcade game hardware

I'll be giving a presentation at CAX again this year.  This time it will be about reverse engineering arcade game hardware.  I'll share some of the tricks I've learned over the years including how to read schematics, disassemble ROM images, use logic analyzers, etc.  I hope to see everyone there ;)

http://caextreme.org/forum/index.php?topic=731.0

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;

Friday, August 8, 2014

The next generation of emulation design?

So as I sat here at 1 AM feeding my newborn infant (that makes 4 kids!), my mind drifted to Star Rider emulation once again and the inherent inaccuracies with the way that Daphne (and also MAME last time I checked) handle emulating the 6809E CPU, the PIA6821, and the Williams Special Chips.

Daphne emulates a multi-CPU game by having each CPU take turns and execute a small chunk of cycles before switching to next CPU and doing the same.  In the case of Star Rider, where each CPU runs at the same speed (1 MHz), Daphne actually executes a single instruction before switching to the next CPU which gives a pretty dang accurate experience.  But even this is not really good enough to achieve "perfect emulation" because Star Rider (and other Williams games) utilize a design which requires the CPU to be halted on a regular basis or to not run on a steady clock.  Daphne assumes that the clock will always be steady and that the CPU will never be halted, so this presents a bit of a problem.  To get perfect emulation, one needs to look to FPGA solutions and forget about desktop PC solutions.  Or does one?

I realized a few things tonight.  If I am going to take Daphne to the "next level" and emulate a game like Star Rider "perfectly" so that real hardware and Daphne and run side by side and retain identical state over a period of days (with allowance for clock frequency drift between the two), I am going to have to ditch the traditional model of "execute N cycles as fast as possible in isolation".

Instead, a more ideal way to emulate Star Rider and other Williams games is to emulate the actual clock that all of the CPUs use and sync each CPU to this clock.  Star Rider has a single 24 MHz clock on the VGG board which is split up (via PROM) into a 12 MHz, 6 MHz, 4 MHz, 2 MHz, and 1 MHz clock.  But all of these clocks are in sync because they all derive from the parent 24 MHz clock.  The PIF board has its own 4 MHz clock (which creates a 1 MHz clock for its CPU), and I haven't looked at the sound board that much to know where its clock is coming from, but it probably is also running at 1 MHz.  It is quite convenient that the three 6809E CPUs for this game are all running at the same clock speed because that means they can all be clocked as if from a single source.  Not all games will have this benefit, so this approach can't be necessarily applied to every scenario out there.  But I think for Star Rider (and probably other Williams games) it would work to emulate the game on a clock level!

One reason that I didn't even consider doing this in the past is because of performance.  Emulating the clock of a CPU is a lot slower than just running a bunch of instructions and keeping track of how many cycles it is supposed to take.  But new modern CPUs keep adding greater capacity to execute multiple tasks in parallel (by adding CPU cores) and it has been a problem for me to figure out how to utilize this parallelism.  The cool thing about emulating a CPU on a clock level is that multiple emulated CPUs can somewhat execute independently of each other which is perfect for a multi-threaded architecture!

It basically would work something like this:

When the clock is about to do a Low->High transition, the state of all inputs would be sent to every thread (emulated CPU) and then each thread would process this clock transition in parallel and ignore propagation delay (which I think is okay to do since this is a digital clocked system).  Each thread would report any change to the CPU's output (for example, the R/W pin changing or data being put on the bus, etc) to the master thread.  Then all of the output changes would be consumed by the master thread, triggering any emulation-related callbacks (such as updating video output), and the master thread would figure out the new input state.  This new input state would be sent to every thread (emulated CPU) and they'd all perform the High->Low transition in parallel and this process would repeat infinitely.

The idea is that as long as a thread has an input state, it can execute its own clock transition and figure out its own output state without having to wait for another thread to accomplish the same task.

This assumes that the hardware design is such that propagation delay can be completely ignored due to the clock speed(s) of the system being chosen conservatively enough.

How would this type of system perform?  I frankly have no idea!  I don't even know if it would be worth using multiple threads to do this BUT my experience over the years tells me that there is a good chance that performance would be good enough that an emulated system like this would run at full speed on a modern PC.

Heck, at this level, even the video drawing hardware could be emulated!  The possibilities!  THE POSSIBILITIES!!! :)

As I alluded to in a previous blog post, I am working with Sean Riddle on an FPGA replacement design for the Williams Special Chip at the moment and once we have this thing perfected (hehe), someone could make an emulated version of it (FPGA isn't the same kind of emulation in my opinion) using the technique that I described above.

Wednesday, July 16, 2014

CAX 2014 Dexter presentation

For those of you who wanted to attend my presentation but could not for whatever reason, here is how it turned out.

Dexter Facebook Page (presentation is near the top)

Sunday, July 13, 2014

I got to meet Eugene Jarvis and Sam Dicker!

I've become increasingly interested in Williams' early arcade games ever since digging deep into Star Rider's interesting/complicated design so I was quite interested to hear what Eugene Jarvis and Sam Dicker (who worked on the 1980 game, Defender) had to say at California Extreme 2014.  After their remarks, I had the opportunity to chat briefly with both of them.

I asked Sam about Rich Witt (who put an Easter Egg in the Star Rider PIF board ROM program) and Chuck Bleich (whose name appears on the schematics). Sam said that he did indeed know Rich but had no idea what had happened to him and was interested if I knew his contact information.  I reported that I did not, but I was hoping to one day tell Rich that I found his hidden easter egg inside the PIF board ROM.  Sam told me that Chuck Bleich was the guy who designed the hardware.


I also asked Eugene Jarvis about Rich Witt and Chuck Bleich.  Eugene told me that he didn't know Rich very well but did know Chuck and also confirmed that Chuck was the guy who designed the hardware.  I've sent a few messages to Chuck but so far I have not received any response.  I'd love to just chat about the hardware for no other reason than I've spent so much time reverse engineering it.  I also told Eugene that I had written an emulator for Star Rider and I asked him about the origins of the special Williams blitter chips.  Eugene told me that he thought that Greg Wepner was the guy behind the design of the chips, and I have seen Greg's name on the Star Rider expander board schematic.  I think that is pretty amazing to get more insight into the special chips.


Monday, June 30, 2014

New facebook pages

I made a few facebook pages that may be a more convenient way for some people to get news than this blog (although I certainly intend to keep writing here!)

RuleCity LLC (the official company that sells Dexter) : Rulecity Facebook

Daphne Emulator : Daphne Facebook

Dexter Laserdisc Replacement : Dexter (Facebook)

Tuesday, June 24, 2014

CAX: It's official!

Not that anyone was doubting, but I am officially on the list :)

Just got this email:

Speakers

We are working on finalizing the speakers for the show.  Below is a teaser list of who’s coming (subject to change):

Eugene Jarvis will return to CAX to discuss his legendary work in creating some of the greatest arcade games ever made.  Learn about Defender and his many other contributions to gaming.

Larry Rosenthal, creator of the Vectorbeam system, will be on hand to discuss his technical achievement and shed some light on the realities of his experience bringing it to the arcade.

Skit-B Pinball will discuss their creations and future plans.  You won't want to miss this seminar - they plan to raffle one of their creations to someone attending the presentation!

Matt Ownby will discuss Dexter, a project keeping classic laser games alive.

Roy "Mr. AWESOME" Shildt will discuss the game Missile Command, as will Dwayne Richard, who has created a documentary on proper play settings.

A panel of Sente employees will convene to discuss the history of the company.

PPM will be making their case for a pinball museum at the Alameda Carnegie Library.

Sunday, the Film Festival returns with Richie Knucklez, King of Arcades, a piece on Wade Krause's contributions to the FAILE BAST Deluxx Fluxx Arcade, and a number of shorts.

Also, I didn't know that Eugene Jarvis was gonna be there.  I definitely want to see what he has to say!

Wednesday, June 4, 2014

Looks like I will be presenting at CAX

I just got official word that I will be presenting at CAX 2014.  If anyone has anything they specifically want me to talk about that they would find interesting, I am inviting feedback starting immediately.  Commenting on this blog post is one way you can get word to me :)

Friday, May 30, 2014

I will be at California Extreme 2014!

Barring unpreventable emergencies (such as my unborn son coming very prematurely), I will be at California Extreme 2014 on July 12th.  I've contacted the CAX organizers and proposed that I give another presentation like I did 3 years ago (wow, can't believe it's been that long!) and now they are (presumably) considering it.  So this may or may not happen.  I am keeping my fingers crossed because I had a lot of fun last time and have some new cool stuff to talk about this time regarding Daphne, Dexter, and more :)

I will really try to have a Dexter rev3 prototype on hand to show off at the show, and to install in available laserdisc game cabinets at the show for those who are interested.  That means I have about a month to get the design finished, get the PCBs fabricated, and get the board assembled.  It's crunch time! :)

Saturday, May 24, 2014

Fixed Daphne 64-bit overflow bug

So, not too long ago, I modified the way vertical blanking timing is calculated to be very accurate.  I was working on Star Rider emulation, so I didn't spend that much time thinking about overflow issues when designing this.  Well, it turns out that my initial design was pretty prone to overflow, plus it was computationally expensive.  Here is how I calculated CPU cycle counts relative to vertical blanking timing:

#define DIVIDER 63000000

void VblankTimer::OnTopFieldVblankStart(unsigned int uFrameIdx)
{
// this operation is essentially [lines finished] * [microsecond / line] * [ cycles / microsecond]
// (uFrameIdx * 525) * (4004/63) * (cpuHz / 1000000)

// TODO : would floating point math be faster here?

// determine the base cycle count using a VERY accurate, self-correcting, and expensive method
m_u64Line1StartCycle = (uFrameIdx * 525);
m_u64Line1StartCycle *= 4004;
m_u64Line1StartCycle *= m_u32CpuHz;
m_u64Line1StartCycle /= DIVIDER;

// set up next event
this->m_pCallback->RegisterCpuEvent(m_u64Line1StartCycle + m_u32TopVblankEndCycleOffset, OnTopFieldVblankEndCallback, this);
}

As I show in my comments, the basic algorithm to compute the CPU cycle count for when vblank of the top field should begin is:

currentFrame * 525 * 4004 / 63 * CpuHz / 1000000

Now, one can plug this into a calculator and get a pretty accurate floating point result.  For example, if the cpu frequency is 1 MHz (1,000,000 Hz) the answer would be 0 if the currentFrame is 0, or 33,366.666666 or 33,367 if one rounds it up.

However, doing math the way a typical calculator does it in this program is expensive, and on CPUs such as ARM that handle floating point operations (ie fractional numbers) poorly, doing floating-point math brings the CPU to its knees.

So it is much faster to do integer math.

However, when doing integer math, the result of any math operation discards any fraction.  So 3/2 is not 1.5 but instead 1.  That means in order to get the best accuracy, division should be saved until the last possible moment.

This means that the order that one would perform the math would be:

currentFrame * 525 * 4004 * CpuHz

and then divide the whole thing by 63 * 1000000

This makes for a very large number before the division takes place!

I was noticing that Dragon's Lair was locking up after running for a while (meaning almost 24 hours) and I tracked it down to when the currentFrame value is greater or equal to 2,193,848.  At 29.97 frames per second, this is 20.33 hours.

2,194,847 is still manageable.  2194847 * 525 * 4004 * 4000000 (Dragon's Lair CPU speed) is 0xFFFFFF20BC896C00 in hex (64-bit number).

However, 2194848 * 525 * 4004 * 4000000 when performed on calc.exe is 0x1DE62BA75C8000 in hex (64-bit number) which is less than the previous result.  This means that overflow has occurred.  So the solution here is to either try to do 128-bit math which is expensive no matter what or try to find another way to solve this problem :)

I decided that the best solution to avoid overflow issues like this is to get rid of the multiplication entirely and just use addition (adding onto previous result each new frame).  This is dangerous to do with integer math because it can lead to greater and greater inaccuracy over time.

However, I did some experimentation and found a pattern with part of the calculation.  currentFrame * 525 * 4004 / 63 has a repeating pattern which can be mimicked with integer math:

Frame: 0, time is 0.000000
Frame: 1, time is 33366.666667
Frame: 2, time is 66733.333333
Frame: 3, time is 100100.000000
Frame: 4, time is 133466.666667
Frame: 5, time is 166833.333333
Frame: 6, time is 200200.000000

Every 3rd value is a whole number.

I observed that this pattern can be approximated (without loss of accuracy over time) by adding 33367, 33366, and 33367 over and over again.

I've now implemented a new system that does just this: adds 33367,33366,33367 after a one-time calculation of multiplying these numbers by cpuhz/1000000.  This now is much faster, doesn't lose accuracy, and will still work naturally when the total cycle count overflows (which, since I am using a 64-bit number, will take quite a while to do).