This project is a GameBoy emulator. The primary objective is for
educational purposes. No external dependencies are allowed other than SDL2
and
the C
standard library.
There are a number of Scripts
which can be used as an example for how to run
the emulator. To get bootstrapped quickly try running:
$ ./Scripts/bootstrap.sh
$ ./Scripts/run.sh
- Get a GameBoy "development environment" (meaning a GameBoy assembler and disassembler)
- Write a test program in GameBoy assembly that loops 3 times and then halts execution
- Build a simple CPU instruction interpreter that can read instructions from a stream of bytes and call an appropriate handler for each instruction (no need to implement all instructions yet!)
- Get your program from (1) to run in the interpreter from (2), printing out the state of the CPU at every step of execution
- Add RAM (really just a byte buffer) to your CPU and implement instructions for moving memory in/out of RAM
- Figure out how the GameBoy ROM file format works, and parse_rom a ROM into your emulator's memory at startup
- Build a simple PPU renderer that looks at all the PPU state in the emulator and renders the "screen" into an SDL window
- Stack push/pop
- Call/return from ASM 'functions'
- Implement the GameBoy buttons (D-pad, A, B, Select and Start). Listening for SDL keyboard events and update the appropriate memory location in the Emulator to reflect the button states. Then write a GB program that requires you to input a specific button sequence.
- Write a program that fills the screen with a striped pattern.
- Add a GUI window that displays the button states (or perhaps: add a togglable button state overlay to the game window)
- Write a program that scrolls the background layer using the SCX and SCY registers.
- Write a program that puts a sprite on screen and animates its movement
- Add a GUI window that displays the contents of tile memory rendered as a tile map. This will give you fast visual clues later if/when something goes wrong in emulation.
- Add a GUI window that displays outlines of all the moving surfaces (without content, i.e a "wireframe" view) so you can see how things move around separately from the main emulator screen.
- Proper boot up sequence
- Load one of the older ROMs into your emulator (something like Tetris, Tennis or Super Mario Land) and see if you can get it to render the title screen.
- Interrupts
- Sprite OAM procedure
- All unprefixed raw op codes
- All prefixed
cb
opcodes - Render title properly
- Be able to set window title based on title in ROM
- Proper 'halt' which can unhalt if an interrupt occurs and continues to execute instructions
- Super Mario Land
- Tetris
- Snake
- Pokemon Red
- Get CMake script for SDL working on Linux
- Get CMake script for SDL working on Windows
- Get Clang Tidy set up
- Set up UBSan to ensure no UB is leaking into the codebase
- ADC_A_A (0x8F)
- ADC_A_B (0x88)
- ADC_A_C (0x89)
- ADC_A_D (0x8A)
- ADC_A_E (0x8B)
- ADC_A_H (0x8C)
- ADC_A_HL_ADDR (0x8E)
- ADC_A_L (0x8D)
- ADC_A_d8 (0xCE)
- ADD_A_A (0x87)
- ADD_A_B (0x80)
- ADD_A_C (0x81)
- ADD_A_D (0x82)
- ADD_A_E (0x83)
- ADD_A_H (0x84)
- ADD_A_HL_ADDR (0x86)
- ADD_A_L (0x85)
- ADD_A_d8 (0xC6)
- ADD_HL_BC (0x09)
- ADD_HL_DE (0x19)
- ADD_HL_HL (0x29)
- ADD_HL_SP (0x39)
- ADD_SP_r8 (0xE8)
- AND_A (0xA7)
- AND_B (0xA0)
- AND_C (0xA1)
- AND_D (0xA2)
- AND_E (0xA3)
- AND_H (0xA4)
- AND_HL_ADDR (0xA6)
- AND_L (0xA5)
- AND_d8 (0xE6)
- CALL_C_a16 (0xDC)
- CALL_NC_a16 (0xD4)
- CALL_NZ_a16 (0xC4)
- CALL_Z_a16 (0xCC)
- CALL_a16 (0xCD)
- CCF (0x3F)
- CPL (0x2F)
- CP_A (0xBF)
- CP_B (0xB8)
- CP_C (0xB9)
- CP_D (0xBA)
- CP_E (0xBB)
- CP_H (0xBC)
- CP_HL_ADDR (0xBE)
- CP_L (0xBD)
- CP_d8 (0xFE)
- DAA (0x27)
- DEC_A (0x3D)
- DEC_B (0x05)
- DEC_BC (0x0B)
- DEC_C (0x0D)
- DEC_D (0x15)
- DEC_DE (0x1B)
- DEC_E (0x1D)
- DEC_H (0x25)
- DEC_HL (0x2B)
- DEC_HL_ADDR (0x35)
- DEC_L (0x2D)
- DEC_SP (0x3B)
- DI (0xF3)
- EI (0xFB)
- HALT (0x76)
- ILLEGAL_D3 (0xD3)
- ILLEGAL_E3 (0xE3)
- ILLEGAL_E4 (0xE4)
- ILLEGAL_EB (0xEB)
- ILLEGAL_EC (0xEC)
- ILLEGAL_ED (0xED)
- ILLEGAL_F4 (0xF4)
- ILLEGAL_FC (0xFC)
- ILLEGAL_FD (0xFD)
- INC_A (0x3C)
- INC_B (0x04)
- INC_BC (0x03)
- INC_C (0x0C)
- INC_D (0x14)
- INC_DE (0x13)
- INC_E (0x1C)
- INC_H (0x24)
- INC_HL (0x23)
- INC_HL_ADDR (0x34)
- INC_L (0x2C)
- INC_SP (0x33)
- JP_C_a16 (0xDA)
- JP_HL (0xE9)
- JP_NC_a16 (0xD2)
- JP_NZ_a16 (0xC2)
- JP_Z_a16 (0xCA)
- JP_a16 (0xC3)
- JR_C_r8 (0x38)
- JR_NC_r8 (0x30)
- JR_NZ_r8 (0x20)
- JR_Z_r8 (0x28)
- JR_r8 (0x18)
- LDH_A_a8_ADDR (0xF0)
- LDH_a8_ADDR_A (0xE0)
- LD_A_A (0x7F)
- LD_A_B (0x78)
- LD_A_BC_ADDR (0x0A)
- LD_A_C (0x79)
- LD_A_C_ADDR (0xF2)
- LD_A_D (0x7A)
- LD_A_DE_ADDR (0x1A)
- LD_A_E (0x7B)
- LD_A_H (0x7C)
- LD_A_HL_ADDR (0x7E)
- LD_A_HL_ADDR_DEC (0x3A)
- LD_A_HL_ADDR_INC (0x2A)
- LD_A_L (0x7D)
- LD_A_a16_ADDR (0xFA)
- LD_A_d8 (0x3E)
- LD_BC_ADDR_A (0x02)
- LD_BC_d16 (0x01)
- LD_B_A (0x47)
- LD_B_B (0x40)
- LD_B_C (0x41)
- LD_B_D (0x42)
- LD_B_E (0x43)
- LD_B_H (0x44)
- LD_B_HL_ADDR (0x46)
- LD_B_L (0x45)
- LD_B_d8 (0x06)
- LD_C_A (0x4F)
- LD_C_ADDR_A (0xE2)
- LD_C_B (0x48)
- LD_C_C (0x49)
- LD_C_D (0x4A)
- LD_C_E (0x4B)
- LD_C_H (0x4C)
- LD_C_HL_ADDR (0x4E)
- LD_C_L (0x4D)
- LD_C_d8 (0x0E)
- LD_DE_ADDR_A (0x12)
- LD_DE_d16 (0x11)
- LD_D_A (0x57)
- LD_D_B (0x50)
- LD_D_C (0x51)
- LD_D_D (0x52)
- LD_D_E (0x53)
- LD_D_H (0x54)
- LD_D_HL_ADDR (0x56)
- LD_D_L (0x55)
- LD_D_d8 (0x16)
- LD_E_A (0x5F)
- LD_E_B (0x58)
- LD_E_C (0x59)
- LD_E_D (0x5A)
- LD_E_E (0x5B)
- LD_E_H (0x5C)
- LD_E_HL_ADDR (0x5E)
- LD_E_L (0x5D)
- LD_E_d8 (0x1E)
- LD_HL_ADDR_A (0x77)
- LD_HL_ADDR_B (0x70)
- LD_HL_ADDR_C (0x71)
- LD_HL_ADDR_D (0x72)
- LD_HL_ADDR_DEC_A (0x32)
- LD_HL_ADDR_E (0x73)
- LD_HL_ADDR_H (0x74)
- LD_HL_ADDR_INC_A (0x22)
- LD_HL_ADDR_L (0x75)
- LD_HL_ADDR_d8 (0x36)
- LD_HL_SP_INC (0xF8)
- LD_HL_d16 (0x21)
- LD_H_A (0x67)
- LD_H_B (0x60)
- LD_H_C (0x61)
- LD_H_D (0x62)
- LD_H_E (0x63)
- LD_H_H (0x64)
- LD_H_HL_ADDR (0x66)
- LD_H_L (0x65)
- LD_H_d8 (0x26)
- LD_L_A (0x6F)
- LD_L_B (0x68)
- LD_L_C (0x69)
- LD_L_D (0x6A)
- LD_L_E (0x6B)
- LD_L_H (0x6C)
- LD_L_HL_ADDR (0x6E)
- LD_L_L (0x6D)
- LD_L_d8 (0x2E)
- LD_SP_HL (0xF9)
- LD_SP_d16 (0x31)
- LD_a16_ADDR_A (0xEA)
- LD_a16_ADDR_SP (0x08)
- NOP (0x00)
- OR_A (0xB7)
- OR_B (0xB0)
- OR_C (0xB1)
- OR_D (0xB2)
- OR_E (0xB3)
- OR_H (0xB4)
- OR_HL_ADDR (0xB6)
- OR_L (0xB5)
- OR_d8 (0xF6)
- POP_AF (0xF1)
- POP_BC (0xC1)
- POP_DE (0xD1)
- POP_HL (0xE1)
- PREFIX (0xCB)
- PUSH_AF (0xF5)
- PUSH_BC (0xC5)
- PUSH_DE (0xD5)
- PUSH_HL (0xE5)
- RET (0xC9)
- RETI (0xD9)
- RET_C (0xD8)
- RET_NC (0xD0)
- RET_NZ (0xC0)
- RET_Z (0xC8)
- RLA (0x17)
- RLCA (0x07)
- RRA (0x1F)
- RRCA (0x0F)
- RST_00H (0xC7)
- RST_08H (0xCF)
- RST_10H (0xD7)
- RST_18H (0xDF)
- RST_20H (0xE7)
- RST_28H (0xEF)
- RST_30H (0xF7)
- RST_38H (0xFF)
- SBC_A_A (0x9F)
- SBC_A_B (0x98)
- SBC_A_C (0x99)
- SBC_A_D (0x9A)
- SBC_A_E (0x9B)
- SBC_A_H (0x9C)
- SBC_A_HL_ADDR (0x9E)
- SBC_A_L (0x9D)
- SBC_A_d8 (0xDE)
- SCF (0x37)
- STOP (0x10)
- SUB_A (0x97)
- SUB_B (0x90)
- SUB_C (0x91)
- SUB_D (0x92)
- SUB_E (0x93)
- SUB_H (0x94)
- SUB_HL_ADDR (0x96)
- SUB_L (0x95)
- SUB_d8 (0xD6)
- XOR_A (0xAF)
- XOR_B (0xA8)
- XOR_C (0xA9)
- XOR_D (0xAA)
- XOR_E (0xAB)
- XOR_H (0xAC)
- XOR_HL_ADDR (0xAE)
- XOR_L (0xAD)
- XOR_d8 (0xEE)