Author Topic: Bug report: Game Boy timings are off, causing some saves to be undumpable!  (Read 969 times)

Offline obskyr

  • Baby Retrode
  • *
  • Posts: 5
  • Karma: +0/-0
    • My Twitter!
The problem

Lately, I've been researching an as of yet obscure Game Boy cartridge mod, where you switch out the SRAM chip for a non-volatile FRAM chip, effectively making the cartridge's saves immortal. Long story short, to make this mod compatible with some of the more obscure features of the Game Boy, the memory's chip enable pin has to be connected to an OR of the normal signal (the MBC's ¬RAMCS) and the cartridge bus's clock signal (CLK), meaning that the RAM chip (¬CE) is only active when both of those are low. This works A-OK on console – both saving and loading work perfectly! However, the Retrode (firmware version 0.25) can neither read nor write successfully to the saves of these modded cartridges.

The symptoms are awfully strange: writing overwrites every byte with 0xFF, and reading turns all 0x00s (and only 0x00s!) into 0x02! This screamed "timing issue" to me, so I checked out the signals, and indeed – the Retrode pulses the signals completely unlike the Game Boy does!



Analysis

I connected up an oscilloscope up to both a Game Boy Advance and to the Retrode to compare how they pulse the signals when reading and writing SRAM. Check out the results!

Legend:
ColorNameFunction
YellowCLKThe console's generated clock signal. Constantly pulses on console. Pin 2 of the cartridge connector.
Pink¬RAMCSThe MBC's "activate SRAM" signal. Active low. A pin on the MBC (which one depends on the MBC).
Blue¬RDPuts the save memory into read mode (or not, depending on MBC). Active low. Pin 4 of the cartridge connector.
Green¬WRPuts the save memory into write mode (or not, depending on MBC). Active low. Pin 3 of the cartridge connector.

Reading

Console

A one-byte read from an MBC5 game on console running in double-speed mode:



¬WR is constantly inactive, ¬RD is constantly active, and CLK pulses at a constant rate of ~2.1 MHz (as it always does). ¬RAMCS goes active for ~400 ns. Not pictured is that the Game Boy samples the data lines at the midpoint of CLK being low while ¬RAMCS is active.

Retrode

In comparison, two bytes (I think?) read from an MBC5 game on a Retrode:



As you can see, it looks quite different. For starters, ¬WR is constantly asserted active alongside ¬RD, which is a bit befuddling – ¬WR shouldn't be active when reading. ¬RAMCS goes active for ~9 µs, during which CLK pulses 10 times. CLK doesn't pulse outside of ¬RAMCS being active. When the Retrode samples the data lines I don't know, but given that CLK pulses ten times, I wouldn't think it's in the middle of any specific one of those.

Writing

Console

A one-byte write to an MBC5 game on console running in double-speed mode:



CLK pulses at a constant rate of ~2.1 MHz (as usual). ¬RD goes inactive at the same time ¬RAMCS goes active. The next time CLK goes low, ¬WR goes active with it. ¬WR goes inactive a little bit later, after which ¬CLK goes high and brings ¬RAMCS to inactive and ¬RD active with it. Not pictured is that the Game Boy makes data available on the data lines at the same time as ¬WR goes active.

Retrode

A one-byte (I think?) write to an MBC5 game on a Retrode:



I don't quite know what's going on here. At the beginning of the entire length of bytes written, ¬RD briefly goes active (probably just incidentally), after which it goes inactive again and stays that way for the rest of the write. ¬WR is constantly active. ¬RAMCS goes active for ~10 µs, during which CLK pulses 10 times. ¬RAMCS then goes inactive again, and… CLK pulses 10 more times? When the Retrode makes the data available I haven't checked.

For a more formal reference for the console timings, see appendix C of GB-CTR. To note is that GB-CTR uses the name "PHI" for the signal called "CLK" above, and that it documents normal-speed mode – multiply the frequencies by 2 to get the timings for double-speed mode.



The fix

The technically correct fix for this is, of course, to rewrite a bunch of code and make the Retrode's timings more or less match those of the Game Boy. The specific lengths of the pulses aren't immensely important – after all, the Game Boy Color can run at twice the speed of the Game Boy – but the order of rises and falls is. This way, the Retrode is guaranteed to be timing-compatible with anything that works on console. However, the particular case I mentioned can be solved with smaller modifications.

The reason reading doesn't work is probably because the data lines are sampled either while CLK is high or hasn't been low for very long (thus trying to read while the chip is disabled or not ready) – so moving that sampling to a time when CLK is low (and has been low for at least 100 ns) would fix reading.

As for writing, I'm not sure what exactly the issue is! It may be the same thing there – the Retrode could be making data available while CLK is high or similar.

I'd also recommend only one CLK pulse per byte read/write – it's not strictly necessary for this case, but… the current 10× pulse behavior is weird, and might waste memory write cycles when that's wired up to CLK.

I hope that this can be fixed soon! As the batteries of MBC5 games run out in greater and greater numbers, and word of this mod gets out, I expect mods like these to become more pervasive. It'd be nice if the Retrode could handle it correctly!
« Last Edit: 05/Apr/2019 05:36:26 PM by obskyr »

Offline MasterOfPuppets

  • Sgt. Retrode
  • ***
  • Posts: 110
  • Karma: +2/-0
You may want to reach out to Matthias to see if you can get the Retrode's source code to implement the fix. That is, of course, if you have the time/desire to do so.

Offline obskyr

  • Baby Retrode
  • *
  • Posts: 5
  • Karma: +0/-0
    • My Twitter!
I suppose… In a way, I don't want to implement the fix myself, since I'd be tempted to make it properly correct and not just put a band-aid on it – which might take a lot of time and work. But… on the other hand, I guess I wouldn't be opposed to making sure it gets done right, and I do know the ins and outs of the issue.

Where can I get in contact with Matthias?
« Last Edit: 06/Apr/2019 12:40:02 AM by obskyr »

Offline skaman

  • Global Moderator
  • Sgt. Retrode
  • *****
  • Posts: 206
  • Karma: +41/-0
Nice work documenting the issues.  I haven't worked on any of the GB/GBC/GBA code.  There are a lot of things that could be improved.  If you're up to fixing the code, then definitely contact Matthias.

If you start working on those consoles, then you'll see the missing support for the various mappers and save types.  The small project can become a large one very quickly.

Offline Wannado

  • Sgt. Retrode
  • ***
  • Posts: 106
  • Karma: +13/-0
Since skaman says he didn't, I think I've been the last one to work on the GB SRAM code. I took a quick look into the repo and the code still seems familiar. Though I don't know which branch exactly v0.25 was built from, and whether the repo is up to date with it.

Back then, I had fixed a few issues and tried to make the Retrode behave more like the real GB, according to the information I could find on the internet (see below). However, CLK behavior stayed quite different, and now likely has to be fixed. The ten CLK pulses are a bit of cargo cult that I couldn't prove useless. My best guess was that it might be required for the GB camera, which does use CLK.

Unfortunately, I didn't have any diagnostic tools except my games working or not.

Note that even when reading, some writing will occur: To MBC registers. Other than that, I don't know why the current firmware would activate /WR when reading.

Reply #12 to "Cannot grab sram from Game Boy Camera" may be of interest.

Below is my description of the GB behavior as I understood it and the Retrode behavior that I tried to implement, quoted from the code file. Note that the Retrode cycle counts given are minimum values and might be exceeded.
Code: [Select]
GB write sequence for SRAM (pulling /CS low)
t1   t2   dt2  c  CLK  /RD  /WR  /CS  Addr  Data
000  000  140  2  H    L    H    H    old   idle
150  140  100  2  .    H    .    .    new   .
250  240  240  3  .    .    .    L    .     .
450  480  360  4  L    .    L    .    .     active
800  840  120  2  .    .    H    .    .     .
950  960  030  1  H    .    .    H    .     idle
950  990  ---  -  .    L    .    .    .     .

GB write sequence for MBC registers (keeping /CS high to prevent SRAM corruption)
t1   t2   dt2  c  CLK  /RD  /WR  /CS  Addr  Data
000  000  140  2  H    L    H    H    old   idle
150  140  340  4  .    H    .    .    new   .
450  480  360  4  L    .    L    .    .     active
800  840  120  2  .    .    H    .    .     .
950  960  030  1  H    .    .    .    .     idle
950  990  ---  -  .    L    .    .    .     .

GB read sequence for SRAM (pulling /CS low)
t1   t2   dt2  c  CLK  /RD  /WR  /CS  Addr  Data
000  000  140  2  H    L    H    H    old   idle
150  140  100  2  .    .    .    .    new   .
250  240  240  3  .    .    .    L    .     .
450  480  480  5  L    .    .    .    .     active
950  960  ---  -  H    .    .    H    .     idle

When reading ROM, keep /CS high to reduce the risk of a bus conflict with SRAM.

Note that the Retrode does not adhere to the CLK behavior depicted above, see gbFlashClk.

Legend
t1 = Time in ns measured with 20 MHz sampling (50 ns resolution).
t2 = Time in ns measured with unknown sampling frequency, "rounded to the nearest 10 ns".
dt2 = Difference in t2 to the subsequent step.
c = ((dt2 + 50 ns) / 125 ns), rounded up. (125 ns = 1 Retrode clock cycle.)

Much of the above information, including t1 and t2, was taken from
https://dhole.github.io/post/gameboy_cartridge_emu_1/
("Emulating a GameBoy Cartridge with an STM32F4. Part 1" in "Dhole's blog").
That blog post itself cites an "unofficial Game Boy CPU Manual".

Sorry that this is all I can contribute right now. Hope it's helpful.