Back to showcase
DMG

Game Boy

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.

SM83 CPU 4.19 MHz PPU 160x144 40 Sprites 4-Ch APU Timer Joypad

System Architecture

SM83 CPU

4.19 MHz, Z80-like ISA

Bus Controller

Address decode, DMA

Work RAM

8KB internal WRAM

16-bit address bus / 8-bit data bus

PPU

Background, window, 40 sprites

APU

2 pulse, 1 wave, 1 noise

Timer

DIV, TIMA, TMA, TAC

Cartridge

ROM + MBC bank switching

Specifications

SubsystemDetails
CPUSharp SM83 @ 4.194304 MHz (modified Z80, no IX/IY)
RegistersA, F (flags), B, C, D, E, H, L, SP, PC (8/16-bit pairing)
Memory8KB WRAM, 8KB VRAM, 160B OAM, 127B HRAM
PPU160x144 LCD, 4 shades, BG + window + 40 sprites (8x8/8x16)
PPU modesOAM scan (80 dots), drawing (168-291 dots), HBlank, VBlank
APU Ch 1-2Pulse waves with sweep, envelope, duty cycle (12.5/25/50/75%)
APU Ch 3Custom 4-bit wave pattern (32 samples)
APU Ch 4LFSR noise with configurable polynomial
InterruptsVBlank, STAT, Timer, Serial, Joypad (IE/IF registers)

RHDL Implementation

The PPU renders each scanline by fetching tiles from VRAM, compositing background, window, and sprite layers with priority resolution.

ppu.rb
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.

apu.rb
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