Question about MoonEye test

Discussion of programming and development for the original Game Boy and Game Boy Color.
Post Reply
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Question about MoonEye test

Post by DarkMoe »

Hi all, I've been trying to iron out some of the many bugs that my emulator still has. Fortunately, each day I manage to fix most of them, and today I think my compatibility ratio for commercial games is near 100%.

I've been trying to fix edge case scenarios, so for starters, this test:
https://github.com/Gekkio/mooneye-gb/bl ... ming.s#L21

Which fails on BGB, kigb, NefustoGB (my emu), but works perfect on higan (byuu always manage to emulate everything wow).

So, I'm a little confused with it, the first test says it's expecting OAM to be locked for the argument parameter for instruction E9 (add sp,e), but not for the instruction reading phase (the E9 itself)

My emulator, returns 0xFF for all the memory map (except high ram) when DMA is being done. When I reach PC = 0xFDFF, my DMA routine already finished and I read instruction as: E9 42, the 42 being the first byte on the OAM memory. This is wrong, the expected value in the test is: E9 FF.

If I adjust my OAM to take 4 more cycles (or 1 machine cycle), I get FF as the instruction (instead of E9), and since that's a restart to 0x38, the flow goes elsewhere and does nothing more. This is even worse.

I'm guessing I don't have to lock all memory when there's a DMA, or maybe when a new instruction is fetch, it's supposed to bypass the DMA lock ?

Any leads on this ?
Thanks,
gekkio
Posts: 49
Joined: Fri Oct 16, 2015 6:18 am

Re: Question about MoonEye test

Post by gekkio »

It sounds like you are reading both bytes of the instruction at the same time.
This is incorrect, because the hardware will only read one byte per 1 machine cycle.
If your emulator uses a big table of instruction lengths like many emulators do, unfortunately that won't be enough to pass many tests like this.

That's what the comments on the top of the file try to explain:

Code: Select all

; ADD SP, e is expected to have the following timing:
; M = 0: instruction decoding
; M = 1: memory access for e
; M = 2: internal delay
; M = 3: internal delay
The total duration of this instruction is 4 machine cycles:
  • the first cycle consists of reading $E9 and finding out that we are doing an ADD SP, e instruction
  • the second cycle consists of reading the parameter e, which in this case is either $42 if DMA is not running, or $FF if the DMA is still running
  • the third and fourth cycles are internal delays with no memory accesses
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

Yeah, I know .. I'm not reading them all at once, I have it separated it like that, and it passes blarggs instructions timings tests.

This is what Im doing:

execute OAM ...
eventually, I reach pc = 0xFDFF:
read memory: 0xE9
advance clocks

read memory: 0x42 (this is wrong, I should get 0xFF in here)
advance clocks

I changed it so OAM execution lasts one more machine cycle, this is the result:

read 0xE9 (I get 0xFF) .. wrong !!

I don't understand how to get this:

read memory: 0xE9
advance clocks
(here DMA should still be locked,but somehow it wasn't locked to read this instruction ?)

read memory: 0xFF (DMA locked, gets FF instead of 0x42)

but if memory is still locked from DMA, how do you get 0xE9 instead of 0xFF for the instruction ?

Thanks
gekkio
Posts: 49
Joined: Fri Oct 16, 2015 6:18 am

Re: Question about MoonEye test

Post by gekkio »

Ah, I understand now. You had the right idea earlier:
I'm guessing I don't have to lock all memory when there's a DMA
Only the OAM area returns $FF when OAM DMA is running. The other areas are readable and writable, but you can get bus conflict scenarios if you access an area used by DMA.
You should be able to pass that test ROM by only blocking OAM, but fully emulating OAM DMA is more complicated. I still need to run some tests to verify the behaviour of the bus conflicts.
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

Wow, really ? I've been using BGB logic to lock everything, I will analyze it a little and fix it.

I wonder how the anti emulator protection works for "airaki" with this logic, since it's supposed to do a DMA and then jump to bank 0. this changes everything.

Thanks for your time, and your awesome tests, I will test the next one =D
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

Ok awesome, I managed to fix my emu, and now it can pass 6 more of your diabolic tests.

I'm still failing most of the "gpu" ones, which are cumbersome.

However, how about this timer related one ?
https://github.com/Gekkio/mooneye-gb/bl ... d_toggle.s

Do you have any reference for those unexpected timer behaviours ? That sounds like a pain to emulate properly.

Thanks again !
nitro2k01
Posts: 252
Joined: Sat Aug 28, 2010 9:01 am

Re: Question about MoonEye test

Post by nitro2k01 »

DarkMoe wrote:Wow, really ? I've been using BGB logic to lock everything, I will analyze it a little and fix it.

I wonder how the anti emulator protection works for "airaki" with this logic, since it's supposed to do a DMA and then jump to bank 0. this changes everything.

Thanks for your time, and your awesome tests, I will test the next one =D
I should answer that. Airaki doesn't actually have emulator detection originally. I added that in the dump I offer for download, for trolling purposes. (Look at the page background from a shallow angle and you might see an easter egg.) Iirc, my check is more advanced than Furrtek's original check in Super Connard, since it jumps to inaccessible memory instead of just reading it and confirming the value.

The protection works by initiating a OAM DMA from $3Fxx and then loading the data at address 3270. If it matches the actual value at that address, $CD, the test is marked as a failure.

If the value isn't $CD, a call to $3E04 is done. Now, one of two things can happen.

If bus conflicts are emulated accurately, the CPU will read a bunch of $00 bytes from the OAM data stream. (You could do something more interesting here to enforce absolute correctness of timings if you wanted.) Eventually it will reach a ret instruction at $3FFF and return to HRAM and do... some fun stuff meant to give people disassembling the code a headache (but that's outside the scope of this post) and eventually it will return with a success.

If you do sloppy emulation and just return $FF for memory that is inaccessible due to DMA, the call to $3E04 will read $FF, the instruction rst $38, a shorthand for call $0038. $0038 in turn will then also read $FF and will call itself recursively for the duration of the DMA. It will eventually run the ld [bc],a when the memory becomes accessible, which should be harmless, then run the ret at $0039 which winds down the stack from the recursive call. Eventually, the CPU returns to $3E05 and takes the same path to the ret at $3FFF and returns a success of the test.

The other check is based on writing to DIV ($FF04) which resets the master divider which is used for various stuff. The test is done by triggering a note on sound channel 1 with a length set. DIV causes the length counter to not expire, which is checked with the NR52 "on" bit for channel 1. Just like the DMA test, this is a low granularity test which doesn't test exact timings, but rather the general presence or non-presence of the quirk in question.
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

That's good to know. BGB is doing the looping FF in 0x38. I implemented those reads as 0. Since my sound emulation is still WIP, I wasn't even turning off channel 1 when the length hit 0, so that worked haha, I still added the code to reset the sound timers when FF04 is written to.

Now the game boots but for some reason I'm getting some garbage tiles. I will have to look into that later.

Anyone who can help with the timer test ?

Thanks !
gekkio
Posts: 49
Joined: Fri Oct 16, 2015 6:18 am

Re: Question about MoonEye test

Post by gekkio »

DarkMoe wrote:Anyone who can help with the timer test ?
Check out AntonioND's docs: https://github.com/AntonioND/giibiiadva ... aster/docs
His emulator is the only one that passes those tests.
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

Managed to get all timer tests passing ! That was a lot of work, but timing looks much more accurate now.

Now I'm trying to understand one of your new tests, stat_irq_blocking

Altered Space 3D is using all those stat 1 interrupts, and it's one of the few games which don't work ok (sprites are a mess, and I get a lot of DMA lock reads and writes). I expect that your test may help improve the emulation of that game.

Any tips on what's the exact behaviour of this stat_irq_blocking ?

Right now, it seems the only way to trigger the first stat interrupt, is that weird stat bug (write to FF41 in MODE = 0 or 1).

Any low level info on the stat bug ? What else am I missing ?

Thanks,
gekkio
Posts: 49
Joined: Fri Oct 16, 2015 6:18 am

Re: Question about MoonEye test

Post by gekkio »

STAT interrupt behaviour is not yet 100% understood, but see my comments here and here.
That should be enough to pass that test although I've got more coming at some point ;)
binji
Posts: 11
Joined: Thu Jun 16, 2016 11:53 am

Re: Question about MoonEye test

Post by binji »

Yeah, Altered Space works in my emulator, and I'm pretty sure stat_irq_blocking is necessary.

The trickiest part about STAT I've done so far is all the stuff to make Wilbert Pol's fork of mooneye-gb's tests pass. Not sure if the tests are accurate, but assuming they are, there are many tests that just didn't pass without some significant restructuring of my PPU code. In particular, they seem to require that the interrupts are triggered 1 M-cycle before IF is set for the vblank or y-compare interrupts, but not for the hblank and mode 2 interrupts (where IF is set immediately). Similarly, there seems to be a distinction between when the internal STAT flag is triggered and when it is cleared. I found that it is only edge-triggered (transition between states) for mode2 and y-compare, but isn't cleared until it leaves that state. I guess it makes some sense, but this does not seem to be the case for hblank and vblank, which can be triggered at any point.

And of course, everything gets more complicated because the timing when the LCD turns on is slightly different, and the behavior of various modes and interrupts is often delayed by 1 M-cycle, but not consistently. For example: normally LY=LYC triggers exactly when LY changes. But it seems you can trigger a y-compare interrupt for LY=153 (which only lasts 1 M-cycle) by writing to LYC when it is has transitioned to LY=0, and y-compare for LY=0 is not triggered at this point, but delayed until the next M-cycle.

I'm not sure I have it completely right, but you can take a look at the ppu_mcycle, check_stat, check_ly_eq_lyc and write_io functions to see how I handle all this stuff.

Oh, I should mention that I'm only trying to emulate DMG, so a lot of this is completely different for CGB.
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

Nice info.

I finally had Altered Space working, but that irq blocking test still fails. So Im not really sure if I want to keep working on the PPU without accurate information, which at the moment seems nobody has it.

I will continue with the audio emulation, which is terrible on my emulator
binji
Posts: 11
Joined: Thu Jun 16, 2016 11:53 am

Re: Question about MoonEye test

Post by binji »

I finally had Altered Space working, but that irq blocking test still fails.
Weird, I wonder why? One thing I noticed while I was playing around with the timing is that sometimes the 3d isometric part works but the intro animation will be busted. For example, when the big spaceship flies in from the right, it just looks like garbage data. Another time the small spaceship seemed to stop in a different location, but then the animation just kind of stopped, the big spaceship never came in. But you could still press start and the game would play normally. Just something to look out for, I guess. I almost didn't notice these things... before I added fast-forward it was boring waiting through the intro, so I'd just hit start to skip everything. :)
DarkMoe
Posts: 70
Joined: Sat Jun 27, 2015 1:09 pm

Re: Question about MoonEye test

Post by DarkMoe »

The game has lots of weird programming choices.

On startup for example, the game will attempt to erase the graphic memory (area 0x8000 onwards) while the screen is on and not even checking the stat mode, so some of the data is not erased.

It looks like the original developers just made it work without really understanding the hardware by pure luck.
Post Reply