Staacks/gbinterceptor

Tracking state of DIV register

Opened this issue · 3 comments

Hi, after your amazing presentation on Friday, I asked you about the issues with tracking the value of the DIV register. If I recall correctly, the possible issues are:

  • You don't know which value DIV has when the first instruction of the game gets executed, because this is different for each Game Boy model and/or boot ROM version.
  • You might not be able to start tracking the DIV register from the very first clock tick after reset, where it will always start at zero, because you might not be quick enough in setting things up.
  • When the CPU is in HALT mode, the output of the 1 MiHz phi clock is disabled and you can't keep track of the time anymore.

I'm pretty sure the first two points can be solved. If you're really too slow, then this would require a hardware change. The HALT mode issue will be tricky.

When the Game Boy comes out of reset, it uses the DIV register (the full 16 bit, not only the 8 bit you get access to) to count to 0x8000. It counts with 1 MiHz, so it takes ~32 milliseconds to reach that value. When this value is reached, then the 1 MiHz phi clock that you see on the cartridge port starts ticking. Before that, the phi clock is held high, so you can't actually see the very first rising edge, because it is already "risen". The CPU will fetch the first instruction of the boot ROM two phi ticks after the 0x8000 was reached. Here are two GTKWave screenshots from a simulation of the DMG-CPU B chip, showing this scenario:

start_div16_after_reset
(DIV starts counting from 0, when low active reset signal is released. "clk" is the 4 MiHz crystal clock. "phi" is the 1 MiHz clock you see on the cartridge.)

start_cpu_after_div8000
(1 MiHz phi clock starts ticking after DIV reaches 0x8000 for the first time after reset. CPU fetches first instruction shortly after that. "afer" is the high active synchronous reset signal of the SM83 CPU core. "a" and "d" are the chip internal address and data buses, not visible on the cartridge port.)

So, when you start counting from the very first phi clock tick, you should know the exact value of the 16 bit DIV register as long as there is no HALT instruction used by the game. Register FF04 shows you bits 6-13 of those 16 bits (start counting from 0, not 1). When the game resets DIV by writing to 0xFF04, the whole 16 bits get reset to 0, not only bits 6-13. You should also be able to determine the Game Boy model / boot ROM by counting the number of ticks it takes until it reaches the first instruction of the game.

If you are really to slow after detecting that the Game Boy was switched on, and you are missing the first tick of the phi clock, you could address this in a new hardware revision. I saw that you used up all GPIOs already, so you would need to switch to a bigger microcontroller. You can buy yourself as much time as you need, by just driving the reset pin low. You can't do that with those somthing245 level shifters though. It needs to be done with an open drain output. It would also be beneficial to be able to read the reset line, because cartridges like the EverDrive or the Nintendo GB-memory cartridges that were only available in Japan are also pulling at the reset line to boot into the actual game after it was selected in the menu. I don't know if and how you are handling this right now. There is a transistor that is also used for bidirectionally level shifting I2C buses. It's the BSS138. I use it for the Game Boy reset line like this and it works good:
using_bss138

It's too bad that the HALT mode disables the phi clock on the cartridge port. The only solution I see there is to start a timer and measure the time from the HALT instruction until the phi clock is enabled again. The mechanism responsible for generating the 1 MiHz clock from the 4 MiHz is always running internally, only the output is disabled. This means, the timing grid for the 1 MiHz phi clock will be preserved. For example, if phi stops at the 4 MiHz clock tick number 12, it can only resume at clock ticks like 20 or 24 or 28, but not on ticks like 21 or 22, because they are not dividable by four. You could use this information to counter inaccuracies of your locally tracked time while the phi clock is disabled. I don't know how good this would work, but before I heard from you, I wasn't expecting that something like this GB interceptor would work at all. So what do I know...

I was wrong about the boot ROM not using DMA btw. The Game Boy Color boot ROM is using the HDMA. But I didn't understand why this is relevant or if you already talked about something else there.

If you are actually trying to implement accurate DIV tracking in the future, I could help you figuring out the details you need for that.

Thanks for all the details. I am not entirely sure why I gave up on this. Maybe I made a mistake or maybe I am forgetting about a problem. Now I am really feeling the itch to give it another shot, but unfortunately after spending a lot of time on the presentation I will have to invest my little free time in a few other projects first (mostly, but not only work and family related). But in a few months, this will be at the top of the list :)

Having a timer during HALT is not an unsolvable problem. In fact, the GB Interceptor already starts an internal timer as a substitute for the external clock to drive the PPU emulation, which does not HALT. However, since it is not so precise to begin with, being off by a few cycles after a long HALT is not much of a problem - especially as it is the less demanding games that tend to pause extensively. I have no idea which precision is possible, but I currently train the internal clock on the external clock that I see during the boot ROM and it should mostly be a matter of throwing enough digits at the problem. Among my worries about the unknown initial state and not knowing the influence of different Game Boy models I always thought that this was rather an inconvenience of restructuring the code...

About the reset line: In the current implementation I do not care about the reset line because I dismiss everything before the game starts and the PIO is observing the clock which is not running before the reset state is lifted. As soon as the GB Interceptor is not following the game anymore (either because the Game Boy was turned off, the clock signal was lost too long or an error was detected) it simply observes the address part of the incoming bus events and waits for 0x0100. This might need some improvement to reset if the clock is lost without ever reaching 0x0100 (for example corrupted Nintendo logo, clean and try again successfully), but I think this should still work reliably.

I have to admit that I am not entirely sure how HDMA behaves. So far I have almost entirely focused on the monochrome stuff. But isn't the GBC's DMA different than the old OAM DMA in that it actually halts the CPU? How does this affect the DIV register?

Unfortunately, I'm no expert on the GBC, because there aren't any useful die shots of the CGB chips. Yes, according to the pan docs, the HDMA halts the CPU. Now I understand why you are worried about that. I also read that it is not allowed to issue a HALT instruction between the short DMA bursts, when it is used in H-blank mode. I think that the HDMA is halting the CPU in a different way. It wouldn't make any sense to stop the phi clock on the cartridge port while the DMA is issuing read requests on that port. Even if it were stopping the phi clock, the DIV register would keep counting. It never stops, because it is used for generating the clocks for other peripherals, like the timer and APU. (Well, that's not entirely true. If the CPU executes a STOP instruction, then the crystal clock stops, and with it everything else. But I'm sure no game ever uses this useless instruction anyway.)

I will run some tests and inform you about the results.

Everything is fine, don't worry. There are no gaps in the phi clock during the boot ROM execution on the GBC. I also noticed that directly after power on, the clock is high for ~41 milliseconds. I guess, these are around 9 milliseconds for the power ramping up while the system is under reset, plus the 32 milliseconds that the DIV register needs to count to 0x8000, until the synchronous CPU reset is released as I described above. I measured this some time in the past for the DMG, where the phi clock was high for ~51 milliseconds before it started ticking. So it just takes a bit longer. But doesn't matter. When you see the first rising edge, then the 16 bit DIV register should be at 0x8001 in both cases.