Back to showcase
Apple ][

Apple II

Full system emulation with 6502 CPU, 48KB RAM, text and hi-res graphics video modes, Disk II controller with WOZ image support, keyboard input, and piezo speaker.

6502 CPU 48KB RAM Text Mode Lo-Res Hi-Res Disk II Keyboard Speaker

System Architecture

6502 CPU

1.023 MHz processor

MMU

Memory mapping, bank switching

48KB RAM

Main memory + aux banks

system bus

Video Generator

Text, Lo-Res, Hi-Res modes

Keyboard

$C000 latch + strobe

Disk II

Stepper motor, WOZ images

Speaker

$C030 toggle, 1-bit

Specifications

SubsystemDetails
CPUMOS 6502 @ 1.023 MHz (NTSC timing)
Memory48KB main RAM, 12KB ROM (Monitor + Applesoft BASIC)
Video: Text40x24 characters, normal and inverse
Video: Lo-Res40x48 blocks, 16 colors
Video: Hi-Res280x192 pixels, 6 colors (artifact coloring)
StorageDisk II controller, 140KB 5.25" floppies, WOZ format
InputASCII keyboard with $C000 data latch, $C010 strobe
Audio1-bit speaker toggle at $C030
I/O slots8 expansion slots ($C100-$C7FF ROM, $C080-$C0FF soft switches)

RHDL Implementation

The system bus connects the CPU to memory and I/O devices through memory-mapped soft switches in the $C000-$C0FF range.

apple2.rb
class Apple2 < RHDL::Sim::SequentialComponent
  input  :clk, :rst
  input  :key_data,  width: 7
  input  :key_strobe
  output :video_out, width: 8
  output :spkr_out

  subcomponent :cpu,   MOS6502
  subcomponent :ram,   RAM48K
  subcomponent :rom,   ROM12K
  subcomponent :video, VideoGenerator
  subcomponent :disk,  DiskII
  subcomponent :kbd,   Keyboard

  wire :addr, width: 16
  wire :data, width: 8
  wire :rw

  behavior do
    # Memory map
    data <= case_select(addr[15..12], {
      0x0..0xB => ram.data_out,
      0xC       => io_read(addr),
      0xD..0xF => rom.data_out,
    })
  end
end

The video generator produces text, lo-res, and hi-res output by scanning the peculiar Apple II memory layout where rows interleave across three 2KB sections.

video_generator.rb
class VideoGenerator < RHDL::Sim::SequentialComponent
  input  :clk, :rst
  input  :mem_data,  width: 8
  input  :mode,      width: 2
  output :mem_addr,  width: 16
  output :pixel,     width: 4
  output :h_sync, :v_sync

  sequential clock: :clk,
             reset: :rst do
    # Apple II screen memory layout
    # Row address = base + (row/8)*40
    #             + (row%8)*0x80
    base = mux(page,
      0x0400, 0x0800)
    row_group = v_count[5..3]
    row_third = v_count[2..0]

    mem_addr <= base +
      (row_group * 40) +
      (row_third << 7) +
      h_count
  end
end