Master the fundamental concepts of emulation through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksThe original Game Boy CPU is a Z80-ish 8-bit core with hybrid registers and a 160x144 LCD. It shares emulation fundamentals with CHIP-8 and NES but adds GPU modes, VRAM banking, and a larger opcode matrix.
Major pieces:
Cartridge mappers (MBC1, MBC3) bank-switch ROM and RAM; ignoring banking loads the wrong code mid-game.
For example, opcode \`0xFE\` (CP d8) compares A with an immediate and sets Z/N/H/C flags used by every conditional branch.
Boot ROM optionally initializes hardware before jumping to cartridge \`0x0100\`.
The LCD status register reports mode 0-3 timing; polling it is how many games synchronize sprite updates. Ignoring mode transitions causes raster effects and status bar splits to break.
This exercise asks you to implement Game Boy CPU fetch-decode-execute plus basic MMU routing. You will lay groundwork for PPU and timer tasks that complete a playable emulator skeleton.
You will use the same mental model here when reading production interpreter source later in the track. Sketch one concrete input on paper, predict the outcome, then confirm with code. That discipline catches logic errors early and makes debugging far faster when you extend the implementation in follow-on tasks.
Implement the Game Boy's Sharp LR35902 CPU core, with the MBC1 cartridge mapper's ROM banking:
Execution starts at $0100 with the DMG post-boot state: AF=01B0 BC=0013 DE=00D8 HL=014D SP=FFFE.
One command per line (; starts a comment):
cLoading…
$0000-$3FFF is ROM bank 0, and $4000-$7FFF is the selected ROM bank. The selected bank is taken modulo the number of banks, where the number of banks is the highest bank given plus 1, and at least 2.$2000-$3FFF selects the bank from the value's low 5 bits, and 0 selects 1. Other ROM-area writes are ignored.$8000-$FFFF is plain RAM.LD r,r', LD r,n, LD rr,nn;LD (BC)/(DE)/(HL+)/(HL-),A and the reverse forms;LD (nn),A, LD A,(nn), LDH, LD (FF00+C), LD SP,HL.INC/DEC r and INC/DEC rr; ADD/ADC/SUB/SBC/AND/XOR/OR/CP with r or n; ADD HL,rr.RLCA RRCA RLA RRA CPL SCF CCF.JP/JR/CALL/RET (plain and with a condition NZ Z NC C), JP HL, RST, PUSH/POP BC DE HL AF.NOP, DI/EI (no effect), HALT (ends the run).RLC RRC RL RR SLA SRA SWAP SRL, plus BIT/RES/SET b.DAA STOP RETI ADD SP,e LD HL,SP+e LD (nn),SP are unsupported. D3 DB DD E3 E4 EB EC ED F4 FC FD are illegal.H = (a&F)+(b&F)+c > F, C = sum > FF. Sub/compare: N = 1, H = (a&F) < (b&F)+c, C = a < b+c.Z = !bit, N = 0, H = 1.| cycles | instructions |
|---|---|
| 4 | register ALU, INC/DEC r, LD r,r', the rotates on A, CPL/SCF/CCF, NOP, DI/EI, JP HL |
| 8 | LD r,n, forms that touch (HL), (BC)/(DE)/(HL±), 16-bit INC/DEC, ADD HL, ALU n, LD SP,HL, LD (FF00+C) |
| 12 | LD rr,nn, LDH, LD (HL),n, INC/DEC (HL), POP |
| 16 | JP nn, LD (nn)/A, PUSH, RST, RET |
Conditional instructions:
CB instructions take 8, or 16 on (HL) (12 for BIT (HL)).
cLoading…
%04X %-8s %-16s ...).LD B,$05, LD HL,$C000, ADD A,$03, SUB $11, CP $7F;LD ($C000),A, LDH ($FF80),A, LD (HL+),A, LD ($FF00+C),A;JR NZ,$0105 (the target), CALL $0120, RET NZ, RST $38, PUSH AF;BIT 7,A, SWAP A;??? for illegal opcodes.unsupported opcode $XX at $XXXX, illegal opcode $XX at $XXXX, stopped: T-cycle limit N reached after K instructions.rom: BANK (0-7) OFFSET (0000-3FFF) BYTES..., bad byte: X (the rest of the line is skipped), dump: ADDR 1..16, limit: 1..10000000, no program, unknown command X.Input:
cLoading…
Output:
cLoading…
op>>6, op>>3&7, op&7), with register index 6 meaning (HL). Most of the table then falls out of a few rules.read/write functions that implement the mapper.Hidden tests cover MBC1 bank switching (including bank 0 → 1), half-carry and borrow flags, PUSH/POP AF, CB bit operations, CALL/RET with the stack contents, conditional returns, and every kind of run ending.