Nintendo DMG-01 handheld emulation with Sharp SM83 CPU at 4.19MHz, pixel processing unit with sprites and window layers, timer/counter subsystem, and 4-channel audio processing unit.
4.19 MHz, Z80-like ISA
Address decode, DMA
8KB internal WRAM
Background, window, 40 sprites
2 pulse, 1 wave, 1 noise
DIV, TIMA, TMA, TAC
ROM + MBC bank switching
| Subsystem | Details |
|---|---|
| CPU | Sharp SM83 @ 4.194304 MHz (modified Z80, no IX/IY) |
| Registers | A, F (flags), B, C, D, E, H, L, SP, PC (8/16-bit pairing) |
| Memory | 8KB WRAM, 8KB VRAM, 160B OAM, 127B HRAM |
| PPU | 160x144 LCD, 4 shades, BG + window + 40 sprites (8x8/8x16) |
| PPU modes | OAM scan (80 dots), drawing (168-291 dots), HBlank, VBlank |
| APU Ch 1-2 | Pulse waves with sweep, envelope, duty cycle (12.5/25/50/75%) |
| APU Ch 3 | Custom 4-bit wave pattern (32 samples) |
| APU Ch 4 | LFSR noise with configurable polynomial |
| Interrupts | VBlank, STAT, Timer, Serial, Joypad (IE/IF registers) |
The PPU renders each scanline by fetching tiles from VRAM, compositing background, window, and sprite layers with priority resolution.
class PPU < RHDL::Sim::SequentialComponent
input :clk, :rst
input :vram_data, width: 8
input :oam_data, width: 8
output :vram_addr, width: 16
output :pixel, width: 2
output :lcd_x, width: 8
output :lcd_y, width: 8
output :vblank_irq, :stat_irq
# PPU registers
register :lcdc, width: 8 # LCD control
register :stat, width: 8 # LCD status
register :scy, width: 8 # scroll Y
register :scx, width: 8 # scroll X
state_machine :mode,
clock: :clk, reset: :rst,
states: [
:oam_scan, # mode 2: 80 dots
:drawing, # mode 3: variable
:hblank, # mode 0: remainder
:vblank # mode 1: 10 lines
] do
transition :oam_scan => :drawing,
after: 80
transition :drawing => :hblank,
when: line_done
transition :hblank => :vblank,
when: lcd_y.eq?(144)
end
end
The APU generates four audio channels mixed to stereo output. Each channel has independent volume envelope, length counter, and channel-specific modulation.
class APU < RHDL::Sim::SequentialComponent
input :clk, :rst
output :left, width: 8
output :right, width: 8
subcomponent :ch1, PulseChannel,
has_sweep: true
subcomponent :ch2, PulseChannel
subcomponent :ch3, WaveChannel
subcomponent :ch4, NoiseChannel
sequential clock: :clk,
reset: :rst do
# Frame sequencer: 512Hz
# Step 0,4: length counter
# Step 2,6: length + sweep
# Step 7: envelope
# Mix channels to stereo
left <= mix_channels(
nr51[7..4],
ch1.out, ch2.out,
ch3.out, ch4.out)
right <= mix_channels(
nr51[3..0],
ch1.out, ch2.out,
ch3.out, ch4.out)
end
end