This is a WIP Gameboy Advanced emulator which is nowhere near something you'd want to play actual games on.
Within this repository there are a number of projects:
- GameboyAdvanced.Arm.SourceGenerators - Roslyn source generators to template our various common operations in the ARM core
- GameboyAdvanced.CompatibilityChecker - A console application which runs the emulator against all roms in a directory and outputs 1 image per rom along with a github flavour markdown doc showing compatibility
- GameboyAdvanced.Core.Benchmarks - A (mostly unused) project to generate benchmarks of low level parts of the emulator
- GameboyAdvanced.Core.Tests - A small handful of unit tests written whilst validating the CPU. Not really used any more as test roms are more useful
- GameboyAdvanced.Core - The fundamental core of the emulator which provides a
Device
class that takes a gamepak and allows single/frame stepping - GameboyAdvanced.Headless - A dotnet console application which allows running the core without a UI but has debugging commands
- GameboyAdvanced.Sdl2 - An SDL2 based UI for emulator which provides no features beyond running the application
- GameboyAdvanced.Web - A SignalR based websockets implementation of the emulator which runs the emulator on the server and passes frames to a web ui which includes some debugging information
The core question for any low level emulator is what granularity it steps the various components. This can be done in a number of ways but the most common are:
- 1 step per instruction
- 1 step per memory access
- 1 step per master clock cycle
Typically good 8 bit system (NES/GB) emulators will act at the master clock cycle level but this can be prohitibitively expensive to once working on more modern devices.
This emulator aims to step all of the components of a GBA at the master clock cycle speed, specifically it aims to accurately emulate the various signals in/out of the arm7tdmi core. Some effort has been taken to ensure that the correct signals appear on the rising/falling edge of each cycle but since the core is stepped once per master clock cycle rather than once for each of the rising/falling edge this is not reliably accurate. I don't believe that it impacts accuracy of the overarching system emulation.
The various components are then listed below with links to the code handling their step function:
- CPU - ARM7TDMI
- PPU
- Timer
- Dma
- APU - TODO, register implemented but no cycle step function
All good GBA emulators make use of a scheduler instead of single stepping components which don't need running each cycle (e.g. timers). This emulator does not have a scheduler although it's likely that I'll need to add one at some point to resolve the performance issues tracked here: #48.
Q. Why use source generators? A. C# sadly doesn't come with a macro/preprocessor/templating system and so our only options here for e.g. ALU operations are either to write functions with loads of branches (bad performance), pre-generate a bunch of copy paste functions (even worse than what I've done) or bastardise a macro system out of source generators.
Q. Why is the code so bad it makes my eyes hurt?
A. To be clear, I would slap anyone who submitted this to me for code review. Generally this emulator makes the concious decision to prioritise performance over
readable code. So e.g. there's almost no inheritance or properties. I've also prioritised practicality over good practice. So everything is public
scoped
in order to make serializing data easy for use by the web based UI and save/load state functionality.
Group | Test | Status | Notes |
---|---|---|---|
DenSinH | Arm Data Processing | ✔️ | |
DenSinH | Arm Any | ✔️ | |
DenSinH | Thumb Data Processing | ✔️ | |
DenSinH | Thumb Any | ✔️ | |
DenSinH | EEPROM test | ✔️ | EEPROM implemented |
DenSinH | Flash test | ✔️ | Fixed banking issues and now works |
Open bus | Open Bus Bios Misaligned | ✔️ | Implemented open bus for bios and passed this and jsmolka bios tests |
Marie | Open Bus Bios | ❌ | Fixed top 4, wrong with THM but so is mgba so not that big a deal |
Marie | Retaddr | ✔️ | |
Fleroviux | Openbuster | ❌ | 1/144 - smashed it (mostly because I haven't written a proper open bus rotate for LDRH) |
Dead_Body | cpu_test | ✔️ | Passes after ensuring that the rotates on the data bus are handled for misaligned reads |
Wrestler | arm-wrestler-fixed | ✔️ | Mode 0 enabled this to run. Fixed all LDR/STR/LDM/STM operations now that shifter treats RRX properly and register writebacks happen on the correct cycle. FIQ banking support required for MRS/MSR tests |
JSMolka | arm | ✔️ | Passes all tests now |
JSMolka | thumb | ✔️ | Passes everything after some shenanigans with LDM/STM special cases |
JSMolka | memory | ✔️ | Passes all memory mirror tests after implementing mirroring of relevant regions |
JSMolka | nes | ✔️ | Passed first time |
JSMolka | hello | ✔️ | |
JSMolka | shades | ✔️ | Amusingly reads beyond the end of the provided ROM (w/ pipelining) so that needed fixing before it passed |
JSMolka | stripes | ✔️ | |
JSMolka | bios | ✔️ | |
JSMolka | unsafe | ✔️ | According to readme doesn't pass on real hardware so this is not a great test to be passing! |
Panda | panda | ✔️ | |
PeterLemon | 3DEngine | ✔️ | |
PeterLemon | Hello World | ✔️ | Required SWI, DMA channel 3 immediate, transparent palette color 0 and multiple palettes in same BG |
PeterLemon | BGMode0 | ✔️ | Requires "BG Tile Offset = 0, Enable Mosaic, Tiles 8BPP, BG Map Offset = 26624, Map Size = 64x64 Tiles" |
PeterLemon | BGMode7 | ✔️ | Affine backgrounds |
PeterLemon | BGRotZoomMode2 | ✔️ | Working except unimplemented mosaic |
PeterLemon | BGRotZoomMode3 | ✔️ | Works now I have affine bg backgrounds |
PeterLemon | BGRotZoomMode4 | ✔️ | |
PeterLemon | BGRotZoomMode5 | ✔️ | |
PeterLemon | OBJRotZoom4BPP | ✔️ | 32*64 sprite is wrong, rest are correct |
PeterLemon | OBJRotZoom8BPP | ✔️ | 8bpp sprites fixes made this work (except mosaic which is tracked elsewhere) |
PeterLemon | Fast Line | ✔️ | Ran on first attempt |
PeterLemon | Fast Line Clip | ✔️ | Ran on first attempt |
PeterLemon | Cylinder Map | ✔️ | Ran on first attempt, not clear what it showcases |
PeterLemon | Myst | ✔️ | Looks good to me although only let it play for 30s or so |
PeterLemon | BigBuckBunny | ✔️ | Fixing bg affine backgrounds made this display properly in full screen |
PeterLemon | BIOS - ArcTan | ✔️ | Passes after lots of work on timers |
PeterLemon | BIOS - Div | ✔️ | Passes after lots of work on timers |
PeterLemon | BIOS - Sqrt | ✔️ | Passes after lots of work on timers |
PeterLemon | Timers | ✔️ | Passes after implementing timer register byte reads |
mgba | suite | ❌ | See Notes.md for detailed breakdown of pass/fail |
TONC | Bigmap | ✔️ | Requires byte wide writes to PPU registers which isn't implemented |
TONC | Blddemo | ✔️ | Blending appears to work compared against mgba |
TONC | BM Modes | ✔️ | Looks weird but tested against other emulators |
TONC | Brin Demo | ✔️ | Passes with proper support for multi size tilemaps |
TONC | CBB Demo | ✔️ | mgba was wrong, I'm now right after fixing the default sprite height/width |
TONC | DMA Demo | ✔️ | Interesting hackery using HBLANK DMA to create a round window effect |
TONC | First | ✔️ | First passing test case! |
TONC | Hello | ✔️ | Calls an SWI from Thumb and then ends up executing beyond where it should in bios. Haven't checked what's happening precisely. |
TONC | IRQ Demo | ✔️ | Looking good, compared completely against mgba and correct |
TONC | Key Demo | ✔️ | Working first time it was tested |
TONC | M3 Demo | ✔️ | |
TONC | M7 Demo | ✔️ | Affine backgrounds working nicely but goes blank if I rotate the screen past half way (fixed by correctly using signed 16 bit ints) |
TONC | M7 Demo MB | ✔️ | Requires affine backgrounds |
TONC | M7 Demo Ex | ✔️ | Required affine backgrounds with mid frame changes to Dmx/Dmy |
TONC | Mos Demo | ❌ | Requires mosaic |
TONC | OA Combo | ✔️ | Sprites don't appear in quite the right place, affine sprites not quite right |
TONC | Obj Aff | ✔️ | Good affine sprite test rom |
TONC | Obj Demo | ✔️ | I think this is working properly |
TONC | Octtest | ✔️ | Required affine backgrounds and sprites but now works |
TONC | Pageflip | ✔️ | Requires LYC to behave vaguely sensibly and page flipping (obviously) so those are vaguely tested |
TONC | Prio Demo | ✔️ | |
TONC | Sbb Aff | ✔️ | Requires affine background |
TONC | Sbb Reg | ✔️ | Requires large backgrounds |
TONC | Second | ✔️ | fixed with bug fixes around SWI return/MSR/MRS |
TONC | SWI Demo | ✔️ | Required a really interesting interaction with PC and a load instruction which was bugged for ages |
TONC | SWI VSync | ✔️ | Tests sprite rotations based on vsync in some way |
TONC | Tmr Demo | ✔️ | Tests countdown timers by displaying as a clock, seems to work although I'm sure timer values aren't 100% correct |
TONC | TTE Demo | ✔️ | Full test of all sorts of things |
TONC | Txt Obj | ✔️ | Letters bouncing properly, looks like sprite rendering working here well |
TONC | Txt SE1 | ✔️ | Looks identical to mgba |
TONC | Txt SE2 | ✔️ | Quite tight timings to pass this |
TONC | Win demo | ✔️ | Window implemented and working (but no OBJ window bits) |
beeg | beeg.gba | ✔️ | With scanline renderer this is now fine |
AGB | AGB_CHECKER_TCHK10 | ❌ | Passes all tests on first screen but the graphics after that are all over the place |
zayd | prefetch abuse | ❌ | All wrong values, still don't have a good handle on the prefetch module |
zayd | dmaslow | ❌ | One cycle off on 3 prefetch tests so far |
zayd | dmamedium | ❌ | One cycle off on 3 prefetch tests so far |
zayd | dmamedium2 | ❌ | One cycle off on 3 prefetch tests so far |
zayd | dmafast | ✔️ | |
NBA | IRQ Delay | ❌ | Was working but now displays wrong lol irq lines compared to hardware since fixes for issue #82 |
NBA | DMA - Burst into tears | ✔️ | Required fixing the DMA timing to not trigger prefetch |
NBA | DMA - Latch | ✔️ | |
NBA | DMA - Start Delay | ✔️ | 20 on real GBA and 20 here, fixed by getting write cycles correct |
NBA | Haltcnt | ✔️ | Pass direct and cpuset but fail on iwram and rom by 1 cycle in each case |
NBA | PPU - Basic Timing | ✔️ | HDMA doesn't bloody stop when HBlank turns off. FFS. Idiot. |
NBA | Timer - Start Stop | ✔️ | Fixed by adding 1 cycle delay from register write to stopping timer |
NBA | Timer - Reload | ✔️ | Requires a cycle delay latching more than just start bit (countup etc need the same cycle delay). |