Post #4750 · 05-12-12 10:42:43 PM
Originally posted by GuyPerfect
In the case of more advanced systems like Game Boy Advance or Nintendo 64, we can generally rely on the fact that every commercial game was made with compilers and not hand-programmed in assembly. They also don't have mappers, save for a few regions reserved for expansion hardware, so dynamic compilation is a very attractive solution when emulating those systems.
Ehh, an emulator should emulate the machine itself, not rely on quirks of some software. That kind of thing is why N64 emulation sucks so much today. You might find a shortcut that works well for the majority of games compiled using version X of program Y, but then you get a few games compiled differently, or using assembly, or ROM hacks or homebrew or unlicensed games, and it all falls apart. Look at how poorly supported any game not using Nintendo's standard microcodes is... IIRC there are some N64 games that
still don't work in most emulators because they used a custom microcode.
My approach to breakpoints was to just add another module to the memory chain, having an array of flags telling whether each byte has a breakpoint set. Multiple modules can be mapped to a region of memory, and each is called in series. The breakpoint module simply tells the CPU to pause when an access breakpoint hits. Of course this module can be unmapped when not in use to save some cycles. Another example of this system in use is that the GPU is mapped to almost the entire memory space, so that during OAM DMA it can block access to everything but the upper page, just like on the real system.
That means every memory access goes through a list of modules (though that list might contain only one item), so I divide the memory space into blocks, with each block having its own list. Right now it's set to 32-byte blocks (2048 blocks); this can be changed at compile time. (I tried 1-byte blocks just for kicks. No noticeable improvement.) So e.g. accesses to ROM only go through Debug (if enabled), GPU (for DMA blocking, which might get optimized into something different later), and memory mapper/ROM emulator; they don't have to get bogged down passing through the modules for input, sound, I/O etc.
Originally posted by GuyPerfect
In other news, just some notes for possible types of exceptions the emulator can trigger:
- Attempting to access unmapped addresses
- Attempting to execute, for instance, a 3-byte instruction at $FFFE
- Execution of invalid opcodes, possibly with an option to execute them as "undocumented"
- Attempting to switch to an unavailable cartridge bank
- A stack overflow occurs
- Attempting to write to video memory while the frame is rendering
- Writing a palette value with either of the upper two bits set
I'm not sure why you want to trap these events or treat them as unusual? Again, it's an emulator, so it should emulate what the machine does, even if that means the game crashes. How do you tell if the stack has overflowed? By SP being set to some particular value, or an uneven number of push/pop instructions? But maybe some game does such things deliberately. Pokémon for example uses it as a 16-bit auto-incrementing pointer to copy tilemaps to VRAM:
ld (old_sp),sp
ld sp,tilemap_src
ld hl,tilemap_dst
pop bc
ldi (hl),b
ldi (hl),c
pop bc
ldi (hl),b
ldi (hl),c
...
ld sp,(old_sp)So for some time, SP is pointing into some arbitrary RAM and doing a lot of pops without a corresponding push. Whether that means the game is just updating VRAM, or is broken and corrupting itself, is of no concern to the CPU. Heck, maybe
we want it to corrupt itself! Maybe that corruption is
something the player wanted to happen! Even if the game does collapse in on itself, the machine is still chugging away executing code; it just isn't producing a result that seems very useful.
One thing I had to make sure of in designing my memory interface is that accesses at the edges of boundaries (like a 16-bit read from 0x7FFF) work correctly, because they do happen. This means every 16-bit access is actually handled as two 8-bit accesses (just like the real thing!). This does bug me a bit, because it seems like something that could be made much faster, but I'm not sure how to improve it...
____________________