This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Motherboard and Main Loop (Game Loop)
Summary
Class implementationViboywhich acts as the "motherboard" of the emulator, integrating
all components (CPU, MMU, Cartridge) in a unified system. Creating the main loop (Game Loop)
that executes instructions continuously, simulating the operation of the real Game Boy. The system now
can run real code from ROMs, although it will stop when it encounters unimplemented opcodes. debug mode
implemented to show detailed traces of each executed instruction. Without this loop, the CPU cannot
"live" and process game code.
Hardware Concept
The Game Boy operates at a clock frequency of4.194304MHz(4,194,304 cycles per second). This means that the processor executes approximately 4.2 million instructions per second (although each instruction consumes multiple clock cycles).
HeSystem Clock(System Clock) is the "heartbeat" that synchronizes all components: CPU, PPU (Pixel Processing Unit), APU (Audio Processing Unit), timers, etc. Without a clock, the system cannot function in a coordinated manner.
Aframeon the Game Boy it lasts approximately70,224 clock cyclesto maintain a refresh rate of 59.7 FPS. This means that every second, the system processes approximately 59.7 frames, each consuming ~70,224 cycles.
HeGameLoop(Main Loop) is the heart of the emulator. It is an infinite loop that:
- Executes a CPU instruction
- Updates other components (PPU, APU, timers) according to the cycles consumed
- Repeats until interrupted or error occurs
Important:Without timing control, a modern computer would execute millions of instructions in a second and the game would go at the speed of light. For now, we do not implement real-time synchronization (sleep), we only execute instructions in a continuous loop. Synchronization will be added later when let's implement the PPU and rendering.
Fountain:Pan Docs - System Clock, Timing
Implementation
The class was createdViboyinsrc/viboy.pywhich acts as the "motherboard" of the emulator.
This class integrates all components and provides a unified interface for the main loop.
Components created/modified
- src/viboy.py:New class
Viboywith methodstick()andrun() - main.py:Refactored to use the class
Viboyinstead of initializing components manually - tests/test_viboy_integration.py:Complete integration test suite (8 tests)
Design decisions
1. Viboy Class as "Motherboard":It was decided to create a central class that integrates all the components
instead of having logic scattered inmain.py. This improves modularity and makes testing easier.
2. tick() method for step-by-step execution:The methodtick()execute a single instruction
and returns the cycles consumed. This allows granular control over execution and makes debugging easier.
3. run() method for main loop:The methodrun()contains the main infinite loop.
Handles exceptions (KeyboardInterrupt, NotImplementedError) to exit cleanly.
4. Debug Mode:A debug mode was implemented that prints detailed information for each instruction: PC, opcode, register status, cycles consumed. This is critical to understanding what the emulator is doing.
5. Post-Boot State:The method was implemented_initialize_post_boot_state()that simulates
the state after the Boot ROM is executed. For now, just initialize PC=0x0100 and SP=0xFFFE. Later,
when we deploy the Boot ROM, these values will be set automatically.
6. No time sync (for now):No real-time synchronization (sleep) implemented because we don't have PPU or rendering yet. The loop executes instructions as fast as it can. This is acceptable for this phase, since the objective is to see the "trace" of instructions being executed.
Affected Files
src/viboy.py- New Viboy class (motherboard) with main loopmain.py- Refactored to use the Viboy classtests/test_viboy_integration.py- Complete suite of integration tests (8 tests)
Tests and Verification
Created a complete suite of integration tests intests/test_viboy_integration.py:
- test_viboy_initialization_without_rom:Verify that Viboy initializes correctly without ROM
- test_viboy_tick_executes_instruction:Verify that tick() executes an instruction and advances the PC
- test_viboy_total_cycles_counter:Verify that the total cycle counter is incremented correctly
- test_viboy_load_cartridge:Verify that load_cartridge() loads a cartridge correctly
- test_viboy_initialization_with_rom:Verify that Viboy initializes correctly with ROM
- test_viboy_executes_nop_sequence:Verify that Viboy executes a NOP sequence correctly
- test_viboy_post_boot_state:Verify that the post-boot state is initialized correctly
Manual validation:The emulator was run withpython3 main.py tetris_dx.gbc --debugand it was observed
The system boots correctly, loads the ROM, and begins executing instructions. The system stops when
finds an unimplemented opcode (expected behavior).
Test results with real ROM (Tetris DX):
- ROM Loading:✅ File uploaded successfully (524,288 bytes, 512 KB)
- Header Parsing:✅ Title "TETRIS DX", Type 0x03 (MBC1), ROM 512 KB, RAM 8 KB
- System initialization:✅ Viboy initialized successfully with ROM
- Post-Boot State:✅ PC and SP were initialized correctly (PC=0x0100, SP=0xFFFE)
- Instruction execution:✅ The system started executing instructions from 0x0100
- First instruction (0x0100):✅ NOP (0x00) executed successfully, PC advanced to 0x0101
- Second instruction (0x0101):✅ JP nn (0xC3) executed correctly, jumped to 0x0150
- Debug mode:✅ Traces correctly show PC, opcode, logs and cycles consumed
- Stopping for unimplemented opcode:✅ System stops correctly at 0x0150 with opcode 0xF3 (DI - Disable Interrupts) not implemented
- Total cycles executed:5 cycles (1 cycle for NOP + 4 cycles for JP nn)
Important remarks:
- The game boot code starts with a NOP followed by an unconditional jump (JP) to 0x0150, which is typical of the Game Boy game initialization code.
- The next instruction at 0x0150 is 0xF3 (DI - Disable Interrupts), which is a critical instruction for initialization of the system. This instruction should be implemented soon.
- The debug mode works perfectly, showing detailed information of each executed instruction, which is essential for debugging and development of the emulator.
Logs:Debug mode shows detailed traces of each executed instruction, allowing you to verify that the PC is running correctly and that the logs are updated as instructed.
Sources consulted
- Bread Docs:System Clock, Timing
- Bread Docs:Boot ROM, Post-Boot State
Note: Implementation based on general knowledge of LR35902 architecture and Pan Docs technical documentation.
Educational Integrity
What I Understand Now
- System Clock:The Game Boy runs at 4.194304 MHz. Without a clock, the system cannot function in a coordinated manner. The clock synchronizes all components.
- Game Loop:The main loop is the heart of the emulator. Continuously execute instructions until it is interrupted or an error occurs. Without this loop, the CPU cannot "live".
- Post-Boot State:After the Boot ROM runs, the system is left in a specific state: PC=0x0100 (start of cartridge code), SP=0xFFFE (top of stack), registers with specific values.
- Timing:Without timing control, a modern computer would execute millions of instructions per second. For now, we do not implement real-time synchronization, we just execute instructions in a continuous loop.
What remains to be confirmed
- Time synchronization:How to implement real-time synchronization (sleep) to maintain 59.7FPS. This will be implemented later when we have PPU and rendering.
- Boot ROM:The exact values of the registers after the Boot ROM is run. For now, we only initialize PC and SP with basic values.
- Interruptions:How to handle interrupts (VBlank, Timer, etc.) in the main loop. This will be implemented later.Note:The test with Tetris DX shows that the first instruction after the jump is DI (0xF3 - Disable Interrupts), which confirms that interrupts are critical for system initialization.
- ✅ Validation with real ROMs: FILLED- Successfully validated with tetris_dx.gbc (Actual Game Boy Color ROM). The system boots correctly, loads the ROM, and begins executing instructions. 2 instructions were executed (NOP at 0x0100 and JP nn at 0x0101 which jumped to 0x0150) before stopping at 0x0150 with opcode 0xF3 (DI - Disable Interrupts) not implemented. The behavior is as expected: the system executes real game code until finding an unimplemented opcode. The next instruction to implement is DI (0xF3), which is critical for system initialization.
Hypotheses and Assumptions
Assumption 1:For now, we assume that we do not need real-time synchronization because we do not yet We have no PPU or rendering. The loop executes instructions as fast as it can, which is acceptable for this phase.
Assumption 2:We assume that the post-boot state only requires initializing PC=0x0100 and SP=0xFFFE. Later, when we deploy Boot ROM, these values will automatically be set more precisely.
Next Steps
- [ ] PRIORITY:Implement opcode 0xF3 (DI - Disable Interrupts) - The next instruction needed by Tetris DX
- [ ] Implement more CPU opcodes (XOR A, LD HL, imm, etc.) so that the emulator can execute more code
- [ ] Implement MBC1 (Memory Bank Controller) for ROMs larger than 32KB (such as 512KB Tetris DX)
- [ ] Implement interrupts (VBlank, Timer, etc.) and their handling in the main loop
- [ ] Implement real-time sync (sleep) to maintain 59.7 FPS
- [ ] Implement PPU (Pixel Processing Unit) to render graphics