================================================================================
madsim - the MAD-65 simulator, v1.0.5
Runs MAD-65 software on a Windows PC
Release date: 2026-10-07
visit: https://mad65.codepainters.com/software/madsim.html
================================================================================

ABOUT

madsim simulates a whole MAD-65 console: both 65C02 processors, the video
circuit, the two SN76489 PSGs and the YM2413 FM chip, and the two joystick
ports. It runs the real firmware ROMs and real cartridge images, so software
that runs here runs on the hardware.

The simulation is paced to the true MAD-65 frame rate of 60.317 Hz, independent
of your monitor's refresh rate. Set your monitor to 60 Hz for the smoothest
result.


PACKAGE CONTENTS

  madsim.exe          the simulator
  roms\cpu_os.bin     MAD-65 firmware, CPU  - V0.9, build 261001
  roms\gpu_os.bin     MAD-65 firmware, GPU  - V0.9, build 260924
  README.txt          this file


RUNNING IT

  Windows. No installation. Unpack the archive and run madsim.exe.

  Run it from the folder it was unpacked into: it looks for the roms\ folder
  next to itself and loads the firmware automatically.

  With no cartridge, the machine boots into the built-in diagnostic demo. The
  demo exercises the sound chips, vector graphics, scrolling text and the
  joystick ports, and prints the build date of both ROMs. If it runs, your
  setup is correct.

  To run a game, drop its cartridge image next to madsim.exe as cart.bin, or
  point at it explicitly:

      madsim.exe --cart mygame.bin


COMMAND LINE OPTIONS

  --cpu1 <FILE>          CPU1 system ROM (16 KiB, $C000-$FFFF).
                         Omitted: auto-discovers cpu_os.bin
  --gpu <FILE>           GPU ROM (16 KiB, $C000-$FFFF).
                         Omitted: auto-discovers gpu_os.bin
  --cart <FILE>          Cartridge image, a multiple of 8 KiB banks, mapped at
                         $8000-$9FFF and bank-switched via $BF60.
                         Omitted: auto-discovers cart.bin
  --scale <N>            Integer window scale (window = 400*N x 300*N)
                         [default: 2]
  --dump-dir <DIR>       Directory for the state dumps written by F2
                         [default: dumps]
  --meter                Start with the utilization meter shown
                         (hidden by default; toggle with F3)
  --pause                Start paused (resume with Space or P)
  --tate                 Start with the screen already turned 90 degrees
                         clockwise, opening the window at the rotated aspect
                         rather than turning it afterwards. F12 still toggles
                         it at runtime
  --audio <on|off>       Audio, on by default. Use --audio off to run silent
  --wait-states <N>      Wait states charged on each slow (EPROM / cartridge)
                         read. 3 is what the hardware does; 0 gives a
                         no-wait-state baseline to measure the cost against
                         [default: 3]
  --threads <on|off>     Run the GPU on its own thread, in parallel with CPU1.
                         On by default; the emulation is exactly the same
                         either way, it only gets done sooner. Off by itself
                         on a single-core PC
  --bench <FRAMES>       Measure instead of play: run this many frames with no
                         window, as fast as the PC allows, and print what one
                         frame costs against the 16.6 ms the MAD-65 takes -
                         for the loaded ROMs and cartridge and for a worst
                         case with both CPUs 100% busy, threads off and on.
                         Try 600
  -h, --help             Print help
  -V, --version          Print version


KEYBOARD

  Joystick 1:  Arrow keys      move
               Right Ctrl      FIRE
               Right Shift     2ND FIRE

  Joystick 2:  W A S D         move
               Left Shift      FIRE
               Left Ctrl       2ND FIRE

  Esc          quit
  Space / P    pause / resume both CPUs
  F2           dump full machine state to the dump directory (developer info)
  F3           toggle the on-screen utilization meter (developer info)
  F5           reset - hardware-style /RESET, restarts both CPUs
  F8           cycle the display filter:
               plain -> B/W CRT -> green phosphor -> amber -> blue phosphor
  F11          toggle borderless fullscreen
  F12          rotate the screen 90 degrees clockwise - the monitor stood on
               its side, for MAD-65's vertical (TATE) mode, which firmware
               V0.9 supports. Press again to restore. In the CRT modes the whole tube turns,
               scanlines and curvature included
  M            mute / unmute audio
  -  and  =    music/SFX mixer knob: '-' turns toward music, '=' toward sound
               effects, centre is an equal mix. Music is SN76489 #1 plus the
               YM2413, SFX is SN76489 #2 - the MAD-65's own static split


GAMEPADS

  A USB gamepad is picked up automatically. No configuration, no flags.

  The first pad connected drives joystick port 1, a second one port 2. Unplug
  a pad and its port falls back to the keyboard; both sources are OR-ed, so
  they can be used together. Hot-plugging works while the simulator runs.

    D-pad and left analogue stick        directions
    Vertically opposed face buttons      FIRE
      (A/Cross, Y/Triangle)
    Horizontally opposed face buttons    2ND FIRE
      (X/Square, B/Circle)

  Each fire line is wired to both buttons of its pair, so it does not matter
  which of the two your thumb lands on. The right stick is deliberately
  ignored.

  RUMBLE. A pad with motors shakes while the game asks for it. A game does it
  through the CPU diagnostic LEDs: LED7 shakes the pad on joystick port 1,
  LED6 the pad on port 2, both motors at full for as long as the LED is lit.
  The real MAD-65 has no rumble - on the hardware those two bits just light
  LEDs - so this is a simulator feature, and a game loses nothing on the
  console by using it. Works with XInput pads, force-feedback pads, and the
  generic USB gamepads / PS adapters (VID 0810, PID 0001) that Windows cannot
  rumble on its own. Rumble stops when you pause and when you quit. Pulses
  shorter than about 10 frames are not felt on a real pad.

  A PS1/PS2-to-USB adapter must be in ANALOGUE mode - press the pad's ANALOG
  button, LED on. In digital mode the pad reports its right stick as the four
  face buttons and its D-pad as the left stick; those are the same signals real
  button presses use, so nothing downstream can tell them apart and the right
  stick appears to fire.


THE UTILIZATION METER

  F3 shows how much of each frame CPU1 and the GPU actually used, plus the
  share of time each spent stalled on wait states (the blue part of the bar).
  It is the quickest way to see whether a game is running out of frame budget
  on real hardware.

  The eight diagnostic LEDs of each processor sit at the end of its row, LED0
  leftmost - POST progress during boot, whatever a program puts there after
  it. A third row, FPS, shows two frame rates, emulated/shown: how many
  frames the machine ran per second, and how many of them reached the screen.
  60/60 is right. When the PC can't put frames on the screen fast enough, the
  simulation catches up by running several frames per redraw, so the game
  still runs at full speed but the picture drops frames: 60/25 means exactly
  that. The window title carries only the name, the version, and [PAUSED] or
  [MUTE] while those are on.


IF IT RUNS SLOWLY

  Run madsim.exe from a command prompt and read what it prints at startup.

  The DISPLAY line names the graphics adapter it draws with. "Microsoft Basic
  Render Driver" means Windows is drawing in software on the processor, which
  is slow and keeps the CPU busy: install the graphics driver for your PC (a
  missing driver also limits the screen resolutions Windows offers). madsim
  prefers a real GPU over the software renderer by itself, so with a driver
  in place it should pick it up.

  madsim.exe --bench 600 tells you whether the PC can carry the emulation at
  all. Under 16.6 ms per frame for the 100% busy case is enough; a Pentium
  J3710 needs about 6 ms.


SCOPE

  - The frame model is per frame: CPU1 and the GPU each run a full frame, then
    the VSYNC swap. This is correct because the two processors only ever talk
    through the ping-pong shared RAM, which swaps at the frame boundary - and
    it is also why they can run on two threads at once with exactly the result
    of running them one after the other.
  - Video is a per-frame snapshot taken at VSYNC. madsim does not model
    mid-scanline raster timing; for register tricks inside a frame, use the
    gate-level Verilator simulator in the MAD-65 sources.
  - Audio register writes are captured per CPU cycle and replayed at sample
    resolution, so note timing within a frame does not smear. The SN76489
    model is faithful but not gate-level; the YM2413 is the emu2413 reference
    emulator.


CHANGELOG

  v1.0.5 - 2026-10-07
    - 60 fps on low-end PCs. madsim used to settle for Windows' software
      renderer when the graphics driver offered OpenGL but not Direct3D 12
      or Vulkan - an Intel HD 405, for one - and drew every frame on the
      CPU, at about 25 fps. It now picks a real GPU when there is one, and
      prints the one it uses at startup (the DISPLAY line), with a warning
      when it is the software renderer.
    - CPU1 and the GPU run on two threads. The emulation is bit-for-bit the
      same; a frame just takes less time. --threads off restores the old
      single-threaded schedule.
    - A processor waiting on WAI costs next to nothing, so a typical frame
      takes about 40% less time.
    - The F3 meter's FPS row shows emulated/shown frames per second, so a
      display that drops frames no longer hides behind a 60.
    - --bench <frames> measures what a frame costs on your PC.
    - Fixed: --audio off was rejected as an unexpected argument.

  v1.0.4 - 2026-10-01
    - Rumble. A pad shakes while its port's CPU1 LED is lit: LED7 for joystick
      port 1, LED6 for port 2, both motors at full, read at the end of every
      frame. Simulator only - the real console has no rumble.
    - The window title is down to the essentials. The CPU/GPU load figures,
      the frame rate and the LEDs moved to the F3 meter, which gained an FPS
      row and the LEDs at the end of the CPU and GPU rows.
    - The frame rate counts emulated frames, and is timed over 60 of them, so
      it reads a steady 60 instead of flickering between 60 and 61.
    - Fixed: the frame rate collapsing for a second after resuming from pause,
      and --pause not showing [PAUSED] in the title from the start.
    - Firmware ROMs updated: CPU OS build 261001 (LED_CPU_MIR, a readable copy
      of the LED latch), GPU OS build 260924.

  v1.0.3 - 2026-09-03
    - Fixed PLA, PLX and PLY not setting the N and Z flags from the pulled
      byte, as a real 65C02 does. Code that branches on the result of a
      stack pull (a common shape in RLE/control-byte decoders) could take
      the wrong branch about half the time.
    - Fixed an IRQ pushing a stale B flag instead of a clear one, so
      firmware that tells BRK from IRQ by that bit now reads it correctly.
    - The CPU core moved to a vendored, patched copy (upstream has no fix
      available), with a new flag-semantics test suite covering the pulls,
      transfers, loads, arithmetic and shifts.

  v1.0.2 - 2026-08-22
    - --tate starts the simulator with the screen already rotated, so the
      window opens at the rotated aspect instead of being turned afterwards.
      F12 still toggles it while running.
    - The utilization meter now follows the display rather than the picture:
      it comes out upright in the top-left corner in both orientations,
      instead of sideways when the screen is rotated.

  v1.0.1 - 2026-08-19
    - USB gamepad support: first pad connected drives port 1, a second one
      port 2, with hot-plugging and no configuration.
    - F11 toggles borderless fullscreen.
    - F12 rotates the screen 90 degrees clockwise, for MAD-65's vertical
      (TATE) mode. In the CRT modes the tube itself turns, so the
      scanlines and curvature turn with it.
    - Keyboard and gamepad are tracked separately and OR-ed per port, so
      releasing a direction on one device no longer cancels a direction still
      held on the other.

  v1.0.0 - 2026-07-15
    - The v6 architecture: shadow code-RAM on both CPUs, with the $BF70
      SHADOW_REG and the GPU's VIDEO_REG bit 3.
    - Wait-state accounting. Only reads stall, and after boot only the
      cartridge path stays slow; the meter reports a stall percentage
      alongside each CPU's figure.

  v0.11.0 - 2026-07-13
    - Music/SFX mixer knob on '-' and '='.
    - Configurable CRT background colour.

  v0.10.1 - 2026-07-07
    - YM2413 level balanced against the two PSGs, so the OPLL no longer
      swamps them.

  v0.10.0 - 2026-07-03
    - Optional CRT filter: curvature, scanlines, glow, vignette and a phosphor
      tint. F8 cycles plain, B/W, green, amber, blue.

  v0.9.7 - 2026-06-27
    - cart.bin is auto-discovered in the default ROM search paths.
    - The utilization meter is hidden by default; --no-meter became --meter.

  v0.9.5 - 2026-06-22
    - Second fire button, the Amiga-style second fire on DE-9 pin 9: Right
      Shift for port 1, Left Ctrl for port 2.

  v0.9.4 - 2026-06-19
    - F5 hardware-style /RESET. RAM and VRAM survive, as on real silicon; the
      audio chips are silenced so a stuck tone cannot outlive the reset.

  v0.9.3 - 2026-06-18
    - Input lag substantially reduced.

  v0.9.2 - 2026-06-16
    - LED diagnostic latches surfaced on the title bar, used by the firmware
      for POST progress.

  v0.9.0 - 2026-06-13
    - Initial release: both CPUs, the v5.2 memory map, monochrome 400x300
      double-buffered video with the hardware background copy, full audio,
      the per-CPU utilization meter and 60.317 Hz frame pacing.

================================================================================
