Introduction

The Game Boy Advance is a one of a kind console, and has a couple of things that make it quite interesting to emulate. It also happens to be the console that I played with by far the most growing up, and so I have a soft spot for it in my heart.

Why you should make a Game Boy Advance emulator

If you're reading this, it's probably because you already decided that you want to make an emulator, but you might not be sure that the Game Boy Advance is really what you want to emulate. There are a couple of reasons why I think that the Game Boy Advance is the right choice for most people:

  • It isn't that much more complicated than something like the NES, SNES, or the original Game Boy.
  • You'll learn ARM assembly in the process: an architecture that is still in use to this day. The processor used in the Game Boy Advance is quite similar to the one in your smart phone today.
  • The games are (subjectively) more fun than ones for older consoles and are (objectively) nicer to look at. Our brains enjoy looking at nice things, and you will be looking at your emulator a lot.

To be clear, it is harder, but you will also learn more, be challenged more, and enjoy the end product more as a result.

Terminology and Notation

Through out this book, I will use the same notation as all of ARM's documentation when referring to bit ranges in a value. That is, in a 32 bit number, the bits go in order from bit 31 to bit 0.

For example,

The decode bits of an instruction are defined to be bits[27:20] and bits[7:4].

Technical Specifications

  • Interupts and exceptions always switch to ARM, and should automatically switch back to Thumb on return
  • Little endian by default
  • You can only enter System mode from another privileged mode by modifying the mode bits of the Current Program Status Register (CPSR).
  • Exceptions have a priority
  • Exceptions/traps have normal and high vectors, that are controlled by external signals.
  • 14 hardware interupts
  • While ARM can be bi-endian, it appears the Game Boy Advance only supported little endian. This would probably make things easier in some ways.

Processor and memory

It uses a 32-bit ARM7TDMI processor, which is built on the ARM v4T architecture. It has 32KB of embedded RAM, 96KB of VRAM, and can address 256KB of RAM on the cartridge. The T in v4T means that the processor has two instruction sets. The ARM instruction set uses 32-bit word-aligned instructions, while the Thumb instruction set uses 16-bit halfword-aligned instructions.

Notes about Thumb: Both instruction sets follow the same execution path inside of the processor, and share a single implementation. Only the process of decoding the instructions is different between the two. Everything else is identical. The Thumb instruction set is useful when storing a program in an area of memory that only has a 16-bit bus width.

Display

The display has a resolution of 240x160 with a refresh rate of 59.73Hz. It uses a 15-bit color encoding (with 32,768 possible colors) and 5:5:5 chroma subsampling.

Sound

The sound hardware of the Game Boy Advance is really interesting, but I don't know enough to fill this section out all the way right now.

Memory map

section name (description)address spacetotal sizebus widthuser permissions
BIOS0x0000_0000-0x0000_3fff16kb32bit--x
eRAM (on cartridge)0x0200_0000-0x0203_ffff256kb16bitrwx
RAM (cpu embedded)0x0300_0000-0x0300_7fff32kb32 bitrwx
IO (memory-mapped registers)0x0400_0000-0x0400_03ff1kb16 bitrw-
Palette (2 palettes x 256 entries x 15 bit colors)0x0500_0000-0x0500_03ff1kb16 bitrw-
VRAM0x0600_0000-0x0601_7fff96kb16 bitr--
Object Attribute Memory0x0700_0000-0x0700_03ff1kb32 bitrw-
ROM (bios probably points execution to here)0x0800_0000-0x0dff_ffff32mb16 bitr-x
Persistent RAM (basically save files)0x0e00_0000-0x0e00_ffff (can theorhetically be bigger, but is unnecessary)>= 64kb8 bitrw-

Address Bus Width and CPU Read/Write Access Widths Shows the Bus-Width, supported read and write widths, and the clock cycles for 8/16/32bit accesses. Region Bus Read Write Cycles BIOS ROM 32 8/16/32 - 1/1/1 Work RAM 32K 32 8/16/32 8/16/32 1/1/1 I/O 32 8/16/32 8/16/32 1/1/1 OAM 32 8/16/32 16/32 1/1/1 _ Work RAM 256K 16 8/16/32 8/16/32 3/3/6 ** Palette RAM 16 8/16/32 16/32 1/1/2 _ VRAM 16 8/16/32 16/32 1/1/2 * GamePak ROM 16 8/16/32 - 5/5/8 /* GamePak Flash 16 8/16/32 16/32 5/5/8 /* GamePak SRAM 8 8 8 5 ** Timing Notes:

  • Plus 1 cycle if GBA accesses video memory at the same time. ** Default waitstate settings, see System Control chapter. *** Separate timings for sequential, and non-sequential accesses. One cycle equals approx. 59.59ns (ie. 16.78MHz clock). All memory (except GamePak SRAM) can be accessed by 16bit and 32bit DMA.

CPU Architecture

The CPSR register:

  • n: Negative or less than
  • z: Zero
  • c: Carry, borrow, extend
  • v: Overflow

The v4T architecture has 49 32-bit ARM instructions and 35 16-bit Thumb instructions.

That may sound like a lot, but all of the Thumb instructions have direct translations to the ARM instructions, so you really only have to program 49 instructions and a Thumb translator. The instructions have a lot of overlapping complexity as well, so the first few are the hardest. Once you've got a couple of them implemented the rest are actually pretty easy.

The ARM instructions are

  • adc
  • add
  • and
  • b
  • bic
  • bl
  • bx
  • cdp
  • cmn
  • cmp
  • eor
  • ldc
  • ldm
  • ldr
  • ldrb
  • ldrbt
  • ldrh
  • ldrsb
  • ldrsh
  • ldrt
  • mcr
  • mla
  • mov
  • mrc
  • mrs
  • msr
  • mul
  • mvn
  • or (also referred to as orr)
  • rsb
  • rsc
  • sbc
  • smlal
  • smull
  • stc
  • stm
  • str
  • strb
  • strbt
  • strh
  • strt
  • sub
  • swi
  • swp
  • swpb
  • teq
  • tst
  • umlal
  • umull

The Thumb instructions are

  • adc
  • add
  • and
  • asr
  • b
  • bic
  • bl
  • bx
  • cmn
  • cmp
  • eor
  • ldmia
  • ldr
  • ldrb
  • ldrh
  • ldrsb
  • ldrsh
  • lsl
  • lsr
  • mov
  • mul
  • mvn
  • neg
  • or
  • pop
  • push
  • ror
  • sbc
  • stmia
  • str
  • strb
  • strh
  • sub
  • swi
  • tst

Instruction Encoding

The decode bits of an instruction are defined to be bits[27:20] and bits[7:4].

In memory, instructions are stored as little endian numbers, just like all other data in memory. Pay attention to this if you're ever looking at a hexdump of a ROM. For example, a branch instruction will look like xx xx xx ea instead of ea xx xx xx.

Build a disassembler

Building the debugger interface

Timing

For timing, Lavender aligns by frame and runs instructions in chunks, rather than trying to accurately emulate the real time per cycle. I chose this approach for a couple of reasons.

  • The inaccuracy of trying to keep incredibly small timings synced correctly can cause emulation problems and performance concerns.
  • Running instructions in chunks decreases the total emulation overhead, since we only need to align 60 times per second instead of trying to align on every single clock cycle.
  • The end result shouldn't be distinguishable from the real thing by someone using the emulator, since they see the exact same thing: a display that gets updated 60 times per second.
  • WebAssembly doesn't really enable any sort of timing mechanisms, so the timing has to be controlled from JavaScript. Calling into WebAssembly from JavaScript is relatively expensive, so we want to get away with having to do it as little as possible. On low performance systems, we could potentially step through instructions in two frame intervals and attempt to reduce the overhead that way, but that has yet to be seen, as we are still in early development.

BIOS

The BIOS does stuff. We can talk about it.

Interacting with the outside world

We've talked a lot about how the Game Boy Advance works, and if you've been following along with the book you probably have a pretty sizable code base already. Eventually we need the ability to see what is on the screen of our emulated system, and interact with it via the 10 hardware buttons: Left, right, up, down, L, R, A, B, Select, and Start.

As we discussed briefly in the chapter on memory, there are two sections of memory that we need access to from outside of the emulator. We need to be able to write into IO memory so that the emulator knows when buttons are being pressed, and we need to be able to read from VRAM so that the data contained there can be drawn onto the screen.

IO Registers and CPU interupts

GBA I/O Map

LCD I/O Registers 4000000h 2 R/W DISPCNT LCD Control 4000002h 2 R/W - Undocumented - Green Swap 4000004h 2 R/W DISPSTAT General LCD Status (STAT,LYC) 4000006h 2 R VCOUNT Vertical Counter (LY) 4000008h 2 R/W BG0CNT BG0 Control 400000Ah 2 R/W BG1CNT BG1 Control 400000Ch 2 R/W BG2CNT BG2 Control 400000Eh 2 R/W BG3CNT BG3 Control 4000010h 2 W BG0HOFS BG0 X-Offset 4000012h 2 W BG0VOFS BG0 Y-Offset 4000014h 2 W BG1HOFS BG1 X-Offset 4000016h 2 W BG1VOFS BG1 Y-Offset 4000018h 2 W BG2HOFS BG2 X-Offset 400001Ah 2 W BG2VOFS BG2 Y-Offset 400001Ch 2 W BG3HOFS BG3 X-Offset 400001Eh 2 W BG3VOFS BG3 Y-Offset 4000020h 2 W BG2PA BG2 Rotation/Scaling Parameter A (dx) 4000022h 2 W BG2PB BG2 Rotation/Scaling Parameter B (dmx) 4000024h 2 W BG2PC BG2 Rotation/Scaling Parameter C (dy) 4000026h 2 W BG2PD BG2 Rotation/Scaling Parameter D (dmy) 4000028h 4 W BG2X BG2 Reference Point X-Coordinate 400002Ch 4 W BG2Y BG2 Reference Point Y-Coordinate 4000030h 2 W BG3PA BG3 Rotation/Scaling Parameter A (dx) 4000032h 2 W BG3PB BG3 Rotation/Scaling Parameter B (dmx) 4000034h 2 W BG3PC BG3 Rotation/Scaling Parameter C (dy) 4000036h 2 W BG3PD BG3 Rotation/Scaling Parameter D (dmy) 4000038h 4 W BG3X BG3 Reference Point X-Coordinate 400003Ch 4 W BG3Y BG3 Reference Point Y-Coordinate 4000040h 2 W WIN0H Window 0 Horizontal Dimensions 4000042h 2 W WIN1H Window 1 Horizontal Dimensions 4000044h 2 W WIN0V Window 0 Vertical Dimensions 4000046h 2 W WIN1V Window 1 Vertical Dimensions 4000048h 2 R/W WININ Inside of Window 0 and 1 400004Ah 2 R/W WINOUT Inside of OBJ Window & Outside of Windows 400004Ch 2 W MOSAIC Mosaic Size 400004Eh - - Not used 4000050h 2 R/W BLDCNT Color Special Effects Selection 4000052h 2 R/W BLDALPHA Alpha Blending Coefficients 4000054h 2 W BLDY Brightness (Fade-In/Out) Coefficient 4000056h - - Not used Sound Registers 4000060h 2 R/W SOUND1CNT_L Channel 1 Sweep register (NR10) 4000062h 2 R/W SOUND1CNT_H Channel 1 Duty/Length/Envelope (NR11, NR12) 4000064h 2 R/W SOUND1CNT_X Channel 1 Frequency/Control (NR13, NR14) 4000066h - - Not used 4000068h 2 R/W SOUND2CNT_L Channel 2 Duty/Length/Envelope (NR21, NR22) 400006Ah - - Not used 400006Ch 2 R/W SOUND2CNT_H Channel 2 Frequency/Control (NR23, NR24) 400006Eh - - Not used 4000070h 2 R/W SOUND3CNT_L Channel 3 Stop/Wave RAM select (NR30) 4000072h 2 R/W SOUND3CNT_H Channel 3 Length/Volume (NR31, NR32) 4000074h 2 R/W SOUND3CNT_X Channel 3 Frequency/Control (NR33, NR34) 4000076h - - Not used 4000078h 2 R/W SOUND4CNT_L Channel 4 Length/Envelope (NR41, NR42) 400007Ah - - Not used 400007Ch 2 R/W SOUND4CNT_H Channel 4 Frequency/Control (NR43, NR44) 400007Eh - - Not used 4000080h 2 R/W SOUNDCNT_L Control Stereo/Volume/Enable (NR50, NR51) 4000082h 2 R/W SOUNDCNT_H Control Mixing/DMA Control 4000084h 2 R/W SOUNDCNT_X Control Sound on/off (NR52) 4000086h - - Not used 4000088h 2 BIOS SOUNDBIAS Sound PWM Control 400008Ah .. - - Not used 4000090h 2x10h R/W WAVE_RAM Channel 3 Wave Pattern RAM (2 banks!!) 40000A0h 4 W FIFO_A Channel A FIFO, Data 0-3 40000A4h 4 W FIFO_B Channel B FIFO, Data 0-3 40000A8h - - Not used DMA Transfer Channels 40000B0h 4 W DMA0SAD DMA 0 Source Address 40000B4h 4 W DMA0DAD DMA 0 Destination Address 40000B8h 2 W DMA0CNT_L DMA 0 Word Count 40000BAh 2 R/W DMA0CNT_H DMA 0 Control 40000BCh 4 W DMA1SAD DMA 1 Source Address 40000C0h 4 W DMA1DAD DMA 1 Destination Address 40000C4h 2 W DMA1CNT_L DMA 1 Word Count 40000C6h 2 R/W DMA1CNT_H DMA 1 Control 40000C8h 4 W DMA2SAD DMA 2 Source Address 40000CCh 4 W DMA2DAD DMA 2 Destination Address 40000D0h 2 W DMA2CNT_L DMA 2 Word Count 40000D2h 2 R/W DMA2CNT_H DMA 2 Control 40000D4h 4 W DMA3SAD DMA 3 Source Address 40000D8h 4 W DMA3DAD DMA 3 Destination Address 40000DCh 2 W DMA3CNT_L DMA 3 Word Count 40000DEh 2 R/W DMA3CNT_H DMA 3 Control 40000E0h - - Not used Timer Registers 4000100h 2 R/W TM0CNT_L Timer 0 Counter/Reload 4000102h 2 R/W TM0CNT_H Timer 0 Control 4000104h 2 R/W TM1CNT_L Timer 1 Counter/Reload 4000106h 2 R/W TM1CNT_H Timer 1 Control 4000108h 2 R/W TM2CNT_L Timer 2 Counter/Reload 400010Ah 2 R/W TM2CNT_H Timer 2 Control 400010Ch 2 R/W TM3CNT_L Timer 3 Counter/Reload 400010Eh 2 R/W TM3CNT_H Timer 3 Control 4000110h - - Not used Serial Communication (1) 4000120h 4 R/W SIODATA32 SIO Data (Normal-32bit Mode; shared with below) 4000120h 2 R/W SIOMULTI0 SIO Data 0 (Parent) (Multi-Player Mode) 4000122h 2 R/W SIOMULTI1 SIO Data 1 (1st Child) (Multi-Player Mode) 4000124h 2 R/W SIOMULTI2 SIO Data 2 (2nd Child) (Multi-Player Mode) 4000126h 2 R/W SIOMULTI3 SIO Data 3 (3rd Child) (Multi-Player Mode) 4000128h 2 R/W SIOCNT SIO Control Register 400012Ah 2 R/W SIOMLT_SEND SIO Data (Local of MultiPlayer; shared below) 400012Ah 2 R/W SIODATA8 SIO Data (Normal-8bit and UART Mode) 400012Ch - - Not used Keypad Input 4000130h 2 R KEYINPUT Key Status 4000132h 2 R/W KEYCNT Key Interrupt Control Serial Communication (2) 4000134h 2 R/W RCNT SIO Mode Select/General Purpose Data 4000136h - - IR Ancient - Infrared Register (Prototypes only) 4000138h - - Not used 4000140h 2 R/W JOYCNT SIO JOY Bus Control 4000142h - - Not used 4000150h 4 R/W JOY_RECV SIO JOY Bus Receive Data 4000154h 4 R/W JOY_TRANS SIO JOY Bus Transmit Data 4000158h 2 R/? JOYSTAT SIO JOY Bus Receive Status 400015Ah - - Not used Interrupt, Waitstate, and Power-Down Control 4000200h 2 R/W IE Interrupt Enable Register 4000202h 2 R/W IF Interrupt Request Flags / IRQ Acknowledge 4000204h 2 R/W WAITCNT Game Pak Waitstate Control 4000206h - - Not used 4000208h 2 R/W IME Interrupt Master Enable Register 400020Ah - - Not used 4000300h 1 R/W POSTFLG Undocumented - Post Boot Flag 4000301h 1 W HALTCNT Undocumented - Power Down Control 4000302h - - Not used 4000410h ? ? ? Undocumented - Purpose Unknown / Bug ??? 0FFh 4000411h - - Not used 4000800h 4 R/W ? Undocumented - Internal Memory Control (R/W) 4000804h - - Not used 4xx0800h 4 R/W ? Mirrors of 4000800h (repeated each 64K)

VRAM, Palette, and Object details

Even though we just covered the IO registers, we have a few more to cover that are related to graphics

REG_DISPCNT: Display Control

f e d c b a 9 8 7 6 5 4 3 2 1 0
Object window Window 1 Window 0 Object layer Background layer 3 Background layer 2 Background layer 1 Background layer 0 Force screen blank Object mapping HB OAM unlock Page select Game Boy mode Mode

Graphics mode

  • Mode 3: 15-bit bitmap
  • Mode 4: Palette bitmap

Audio, and the fun associated

Save data

We should also cover how games with clocks work in this section

Emulator testing

I have a bunch of test roms that I will be sharing here soon.

I'll also be working on some unit tests that may be useful for others as well. Unit testing your emulator is probably a very good idea. Emulators are big and complex and breaking something that ends up being hard to trace down would ruin anybodies day. If you have unit tests, you'll probably be able to figure out what is broken pretty easily. Make sure to set it up with continuous integration using Travis, AppVeyor, Circle, Github, or your other favorite that I haven't heard of.

About

This chapter is basically a scratch pad for things that I have found interesting during the development of Lavender. It is somewhat disorganized currently, but I will get around to cleaning it up later in development when I have a more complete picture of how everything works together.

Notes

When an exception occurs, the ARM processor halts execution in a defined manner and begins execution at one of a number of fixed addresses in memory, known as the exception vectors. There is a separate vector location for each exception, including reset. Behavior is defined for normal running systems (see section A2.6) and debug events (see Chapter D3 Coprocessor 14, the Debug Coprocessor) An operating system installs a handler on every exception at initialization. Privileged operating system tasks are normally run in System mode to allow exceptions to occur within the operating system without state loss.

[ARM v5 Architecture Reference Manual] Page A1-4

The ARM instruction set can be divided into six broad classes of instruction:

  • Branch instructions
  • Data-processing instructions on page A1-7
  • Status register transfer instructions on page A1-8
  • Load and store instructions on page A1-8
  • Coprocessor instructions on page A1-10
  • Exception-generating instructions on page A1-10.

The data-processing instructions perform calculations on the general-purpose registers. There are five types of data-processing instructions:

  • Arithmetic/logic instructions
  • Comparison instructions
  • Single Instruction Multiple Data (SIMD) instructions
  • Multiply instructions on page A1-8
  • Miscellaneous Data Processing instructions on page A1-8.

Load and Store Register instructions have three primary addressing modes, all of which use a base register and an offset specified by the instruction:

  • In offset addressing, the memory address is formed by adding or subtracting an offset to or from the base register value.
  • In pre-indexed addressing, the memory address is formed in the same way as for offset addressing. As a side effect, the memory address is also written back to the base register.
  • In post-indexed addressing, the memory address is the base register value. As a side effect, an offset is added to or subtracted from the base register value and the result is written back to the base register.

B, BL, BLX, BX Branch, and Branch with Link. See B, BL on page A4-10. Branch with Link and Exchange. See BLX (1) on page A4-16 and BLX (2) on page A4-18. Branch and Exchange Instruction Set. See BX on page A4-20.

Adding this to the contents of the PC, which contains the address of the branch instruction plus 8 bytes. Why is the address plus 8 bytes? If they increment the PC after reading the instruction then I understand why it might be 4 ahead, but 8 doesn't make sense.

References

These links are all resources for development that I've found to contain useful, up-to-date, accurate, and relevant information. I would really like to express my appreciation to all of the people out there who put in the effort to create these wonderful resources! Especially Jasper Vijn, the author of TONC. It's a great compilation, with a lot of miscellaneous details that I may have never found answers for without, as well as lots of test ROMs. This project may not have been possible (or at least not as good) without their contribution.

Technical References

Written References

Implementation References