Exophase said:What you're doing is making it so that the interrupt handler never gets called for non-vblank interrupts that happen during the wait. It's not enough that those interrupts are fired, they need to actually call the interrupt handler (if the interrupt's enabled anyway). Otherwise they won't happen until it's too late.
For example, say you have an hblank IRQ interrupt handler that does something to the scanlines like scrolls them for a wavy background effect. The normal game loop runs then calls VBlankIntrWait while the screen is still in the middle of being rendered. If you sit in a tight loop until vblank hits all of those hblanks were something should have happened are going to be missed. They may be serviced along with the vblank interrupt but by then it's too late.
If you look at 0x34C in the GBA BIOS you'll see that after coming out of a CPU halt it checks to see if the right interrupt was triggered, and if not it goes back to waiting. Nowhere does it disable other interrupts. If your emulator doesn't do this it's not doing things correctly and there are some games that will definitely break - maybe not a ton, but they're out there.
Okay, now I see what you're saying You're saying that in HALT mode, ANY interrupt where IE and IF bits match up will exit this state and service those interrupts. VBlankIntrWait and IntrWait just guarantee that the ARM/THUMB code will continue running AFTER the specified interrupt happens. That makes sense when you put things that way. I was under the impression that HALT mode worked as I described it, ignoring interrupts that aren't what the SWI is looking for (which sounds more useful, but then again I don't code GBA games). Perhaps GBATEK could have better documented this.
I still don't think proper implementation should be that hard via HLE. All I have to do is run my tight loop until IE and IF bits match up. The key is then is to make sure the PC doesn't advance to the next instruction if the triggered interrupt isn't what the SWI is looking for. In the case that it isn't, handle_interrupt() can service it, then go back to the PC containing the SWI, and keep running the tight loop again and wait for the next interrupt. When the specified interrupt happens, advance the PC to the next instruction. handle_interrupt() will service the specified interrupt, complete said interrupt, then dump the PC to one instruction after the SWI, and processing continues as normal. Would require an extra if.. else.. or two, but not much. Only downside would be that the same SWI instruction would have to be fetched, decoded, and executing (from the emulated CPU's point of view) multiple times to account for the forced psuedo-infinite loop, but it shouldn't affect timing in my emulator (especially since SWIs aren't clocked at all, just like DMAs). This would be good, obscure behavior for a test ROM!
Exophase said:That's a good work ethic. Just one commit a day can get a lot more done and keep you from losing momentum and stagnating. I wish I could always maintain at least that much.
Thanks! I probably won't be able to maintain this pace though, eventually. I mean, one day I want to start playing emulators again instead of making them. It's sort of a "trap" I've heard other authors talk about Kinda miss those days when I'd just play Advance Wars on VBA for hours on end. But then again, I don't regret knowing more about the programs and consoles I love.
Exophase said:There was an earlier question in this thread if anyone decoded Thumb to ARM, right?
I never did this for GBA, but DraStic did it from the start so at least I can attest that it's a workable approach.
BUT, you have to be aware that some Thumb instructions can't be done in ARM directly. bl is internally split into two distinct instructions, which you have to emulate separately because some games use the sub-instructions independently (eg Golden Sun), and neither of these can be expressed as ARM instructions. In DraStic I got around this by re-encoding them in some of the unused instruction space (you probably want to make sure you're in Thumb mode if these instructions are encountered, otherwise actual invalid ARM instructions will look like them.. but I think those hang the GBA anyway...)
Also, the ldr[pc, ...] and add rd, pc, imm instructions are relative to a PC that has the second-least significant bit cleared, so you have to communicate that somehow to the ARM core (or some other way of handling it properly) rather than just using the PC as-is.
Maybe it's not the best approach, but I dunno, it made sense when I came from the GB/GBC to the GBA. I emulate each instruction based on its "grouping" that appears in ARM's official documentation. That is to say, THUMB.1, THUMB.2, ARM.1, ARM.2. I don't have a generic mov() or ldr() function that can be used for both ARM and THUMB opcodes. ARM and THUMB instructions have their own implementations for each grouping. Even something like THUMB.6 to THUMB.9 each have their own separate functions, so that handles any idiosyncrasies. I decode BL as two instructions, but the same function handles THUMB.19 and determines if the 1st or 2nd part is to be executed. Like I said, I dunno, it just seemed the most straight-forward way to go about it at the time, so I went with this method. Interested in knowing how others handled it.
Last edited: