Skip to content

Latest commit

Β 

History

281 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MFC 6502

MFC 6502 icon

MFC is My First Computer: a software-defined WDC 65C02 machine with an interactive monitor, a resident FAT16 filesystem, BASIC, FORTH, and a built-in assembler β€” comprehensive enough to program itself, small enough to read.

The icon is the machine's own boot prompt, ] and its cursor, set in the CP437 character ROM the VIC renders text with (tools/make_icon.py reads the glyphs straight out of include/computer/Cp437Font.h, so the mark cannot drift from the typeface it is drawn in).

This project started as a continuation of a CPU/assembler/disassembler I wrote in Python. I wanted to create an actual running environment to enter code directly or load from a file and run.

I also wanted to test the abilities of AI as part of the development and documentation process.

Project Overview

This project implements a complete 6502-based computer system kernel for emulated environments. The kernel provides low-level system initialization, hardware control, and most importantly, a powerful interactive monitor program for direct system interaction.

Key Features:

  • Complete 6502 assembly language kernel optimized for emulated environments
  • Cycle-stepped WDC 65C02 CPU emulator (full CMOS instruction set, validated against the Klaus2m5/amb5l functional, decimal, and 65C02-extended test suites)
  • Interactive monitor with comprehensive debugging tools
  • MFC/OS DOS shell (] prompt) with a resident FAT16 filesystem and launch-by-name for disk programs
  • Disk applications: EDIT (full-screen editor), TERM (ANSI/telnet terminal with XMODEM), IRC (chat client), plus games (CHESS, KERNEL PANIC, VENTURE, The Sunless Vault roguelike, and the Scott Adams adventures) β€” TERM and IRC keep a RAM scrollback buffer you page with PgUp/PgDn
  • Built-in MFC BASIC interpreter (derived from EhBASIC), launched by typing BASIC at the DOS prompt (with human-readable .bas LOAD/SAVE via a host file dialog)
  • System-wide --More-- pager: long output from any program (DOS, monitor, BASIC, FORTH) pauses each screenful (SPACE advances, ESC stops)
  • Memory manipulation and program execution capabilities
  • Streamlined architecture with universal commands and simplified modes
  • File I/O operations for loading and saving programs
  • Comprehensive search, fill, move, and copy operations
  • Assembly examples in examples/ β€” a dozen runnable 6502 programs (loops, keyboard input, color, hex dump, an 8Γ—8 multiply, a guess-the-number game) with a guide and ABI quick reference (examples/README.md)

Monitor Program

The heart of this system is the 6502 Monitor - a complete interactive debugging and programming environment that provides direct control over the computer's memory and execution. The monitor offers a command-line interface with powerful tools for memory operations, program execution, and system inspection.

Architecture Overview

The monitor features a streamlined architecture with:

  • Two primary modes: Command mode (default) and Write mode for interactive editing
  • Simplified command processing with consistent syntax and error handling
  • Command repeatability recall last command for quick replay or modification

Getting Started

The machine boots into the MFC/OS DOS shell, which shows a sign-on splash and the ] prompt:

]

From the DOS prompt you run disk programs by name (EDIT, TERM, IRC, CHESS, …), manage files (CATALOG, TYPE, COPY, …), and launch the monitor with MON. The monitor prompts with the current address followed by > (? for help, Q to return to DOS):

0000>

The NNNN> prompt indicates you're in monitor command mode. You can now enter any monitor command.

Monitor Commands

For complete command documentation including syntax and examples, see the Monitor manual.

Quick Command Summary

Category Commands Description
Memory Operations R:, W:, F:, M:, X: Read, write, fill, move/copy, and search memory
Program Operations G:, L:, S: Execute, load, and save programs
Number Conversion D:, H: Convert between decimal and hexadecimal
Display Commands C:, T:, Z: Clear screen, show stack, show zero page
System Commands ?, ESC, . Help, exit mode, command recall

Key Command Features

Commands are listed alphabetically by command letter (matching the on-screen ? help):

  • C: Clear Screen - Clear the display
  • D: Decimal to Hex - Convert decimal (0-65535) to hexadecimal format
  • F: Fill Memory - High-performance memory filling with progress feedback
  • G: Go/Run - Direct program execution with return to monitor
  • H: Hex to Decimal - Convert hexadecimal (0000-FFFF) to decimal format
  • L: Load File - Load a host-selected file to an address: L:8000 (host shows a file dialog)
  • M: Move/Copy - Smart memory operations with overlap detection (M:src-end,dest,B where B: 0=copy, 1=move)
  • R: Read Memory - Display bytes in memory, supports single addresses or ranges
  • S: Save File - Save a memory range to a host-selected file: S:8000-8FFF (host shows a file dialog)
  • T: Stack - Display the stack page ($0100-$01FF), paged
  • W: Write Memory - Interactive hex editing with address advancement
  • X: Search Memory - Multi-byte pattern search with paged output
  • Z: Zero Page - Display zero page ($0000-$00FF), paged
  • ESC - Exit the current mode and return to the command prompt

Error Handling

The monitor provides clear, consistent error messages:

  • ERROR? - Invalid command syntax or parameters
  • RANGE? - Invalid or out-of-bounds address range
  • VALUE? - Invalid hexadecimal characters in input

Memory Layout

  • $0000-$00FF: Zero Page (system workspace; monitor uses $14-$39, EhBASIC uses the rest)
  • $0100-$01FF: Stack memory
  • $0200-$03FF: Monitor variables and command buffers
  • $0400-$07FF: Formerly the screen (the 80Γ—25 color screen now lives behind the VIC register port at $FE2D-$FE37, not in the address map). Claimed by the monitor: $0400 is the T:/Z: snapshot, $0500-$07FF the assembler's symbol table
  • $0800-$AFFF: User RAM (module working RAM; EhBASIC program/variable space)
  • $B000-$EFFF: Module window (16 KB; bank 0 = RAM, banks 1..255 = ROM modules β€” BASIC 1, FORTH 3, MONITOR 4; bank 2 free since the assembler joined the monitor)

The chipset drawn as a board β€” bus, chips, I/O decode and interrupt lines β€” is in docs/BOARD.md.

  • $F000-$FFFF: Kernel BIOS (4 KB; CODE ~1,560 bytes, rest free for growth). The monitor is bank 4, not here.
  • $FE00-$FE22: PIA registers (keyboard, file I/O, timer) β€” an I/O page reserved within the kernel region

See docs/ARCHITECTURE.md for the full memory map and zero-page allocation.

User programs can access kernel services via the jump table at $FF00:

Address Service Description
$FF00 PRINT_CHAR Print single character
$FF03 PRINT_MESSAGE Print null-terminated string
$FF06 PRINT_NEWLINE Print carriage return/line feed
$FF09 GET_KEYSTROKE Wait for key press
$FF0C CLEAR_SCREEN Clear display
$FF0F GET_RANDOM_NUMBER Generate random byte
$FF12 RETURN_FROM_MODULE Module exit point β€” unmaps the bank, returns to DOS (BASIC BYE)
$FF2D SET_ATTR Set the color/attribute latch for subsequent output (A = [R][BR][bg:3][fg:3])

(Abridged β€” see docs/ARCHITECTURE.md for the full 20-entry ABI table, including the decimal-conversion, module-launch, sound, and timing services.)

File I/O Interface

The kernel provides memory-mapped file I/O at:

  • $FE10: File command register
  • $FE11: File status register
  • $FE12-$FE13: Address registers
  • $FE14-$FE1F: Filename buffer
  • $FE20-$FE21: End address (for save operations)

Building and Development

Prerequisites

Dependency Needed for If missing
CMake 3.20+, Ninja the build itself nothing builds
C++20 compiler (GCC 10+, Clang 10+, MSVC 2019+) the emulator nothing builds
cc65 (ca65, ld65, cl65) assembling every ROM and .PRG configuration fails β€” the emulator cannot run without its ROMs
Qt6 or Qt5 β€” Core, Widgets, Network the GUI machine; Network drives TERM/IRC you get a non-interactive console demo, not the computer
Qt Multimedia SID audio everything works, silently
Python 3 opcode-table drift test that one test is skipped

GoogleTest is not a prerequisite β€” the build fetches v1.14.0 itself when tests are enabled.

# Debian / Ubuntu / Mint  (verified)
sudo apt install build-essential cmake ninja-build cc65 qt6-base-dev qt6-multimedia-dev

# Fedora        (untested)
sudo dnf install gcc-c++ cmake ninja-build cc65 qt6-qtbase-devel qt6-qtmultimedia-devel
# Arch          (untested)
sudo pacman -S base-devel cmake ninja cc65 qt6-base qt6-multimedia
# macOS         (untested)
brew install cmake ninja cc65 qt

If you install a dependency later, delete the build directory before rebuilding. CMake caches "not found" results, so adding Qt or Qt Multimedia to an existing build tree leaves the emulator GUI-less or mute with no error. ./build.sh --fresh does this for you.

Build Instructions

# Option 1: the build script -- configures, builds every program, assembles disk.img
./build.sh
./build.sh --fresh              # after installing a new dependency

# Option 2: CMake presets directly (see CMakePresets.json)
cmake --preset dev              # dev | debug | release | no-gui
cmake --build --preset dev
cmake --build --preset dev-everything   # programs + ROMs + disk.img

Once configured, two targets cover the whole pipeline:

ninja -C cmake-build-debug everything   # every program, every ROM, the app, the disk
ninja -C cmake-build-debug run          # ...then boot the machine

The .PRG files are build outputs, not committed artifacts β€” they are produced into cmake-build-debug/programs/<name>/ and staged from there, exactly like the ROMs. That is deliberate: when they were checked in, editing a program's C and forgetting to rebuild left the disk carrying yesterday's binary while the test blobs, compiled from the same sources by CMake, picked the change up β€” the tests agreed with you and the machine did not. The twelve Scott Adams games under disk/GAMES/ are committed: they are content, not built from sources in this repo.

Configuration ends with a summary of what was actually enabled β€” check it before filing a bug about missing sound or a missing window:

======== 6502-kernel configuration ========
  Qt GUI ......... yes (Qt6)
  SID audio ...... yes
  cc65 ROMs ...... yes
  Tests .......... yes
===========================================

Build outputs land in cmake-build-debug/: bin/6502-kernel, kernel/*.rom (kernel, dos, basic, assembler, forth), kernel/kernel.map, and disk.img.

Running

cd cmake-build-debug/bin && ./6502-kernel

Run it from bin/. The ROMs and disk image are opened by relative path (../kernel/*.rom, ../disk.img), so launching from anywhere else fails with "Could not open kernel.rom". A healthy boot lands at the MFC/OS prompt:

              MFC 6502  OPERATIONAL
           MFC/OS 1.21   32768 BYTES FREE
]

Type CATALOG to list the disk, or HELP for the command set.

Verifying the build

ctest --test-dir cmake-build-debug        # 27 tests: CPU, banking, FAT16, ACIA/XMODEM, SID, RTC, VIC, ROM layout, disk programs

Disk image (mkdisk)

The GUI loads cmake-build-debug/disk.img, assembled from programs/catalog.txt β€” the single source of truth for everything that can go on a disk. One section per item, saying where its files live, how to build them (or that they are committed content needing no build), and where each lands on the disk. Files are declared by what they are: a program (the .PRG), data the program reads and writes at run time, or a doc for a human to TYPE. The distinction is there so data can never be separated from the program that needs it β€” naming term inescapably brings SYSTEM/DIAL.LST, because the catalog records that TERM reads and writes it.

The build derives both the staging commands and the diskmap.txt that mkdisk consumes, so adding a program is one catalog entry rather than three edits that fail silently if you miss one. CMake refuses to configure if a programs/*/build.sh has no catalog entry. Drawers grow across as many FAT16 clusters as they need, so a drawer is not capped at one cluster of files.

ninja disk                                  # build the programs, then assemble the image
ninja everything                            # ...and the ROMs and the app as well

Both are explicit β€” a plain ninja never rewrites the disk. disk builds every catalog program before staging it, so the image can never carry a stale binary.

The mkdisk host tool (cmake-build-debug/bin/mkdisk) also works standalone:

mkdisk create <image> <diskmap.txt>   # build a fresh image from a bundle
mkdisk read   <image> <outdir>        # extract an image into a bundle (+ diskmap.txt)
mkdisk update <image> <diskmap.txt>   # replace/add listed files, keep the rest

Project Structure

6502-kernel/
β”œβ”€β”€ src/                   # C++ emulator sources
β”‚   β”œβ”€β”€ computer/          # CPU, memory, VIC, PIA, ACIA, SID, RTC, block device
β”‚   β”œβ”€β”€ ui/                # Qt GUI (MainWindow, DisplayWidget)
β”‚   └── kernel/            # 6502 assembly: kernel.asm, basic.asm, dos/, assembler/, forth/
β”œβ”€β”€ include/               # C++ headers
β”œβ”€β”€ programs/              # cc65/asm disk programs: edit, term, irc, venture, kpanic,
β”‚                         #   chess, frontier, micromax, scottfree, vault, common
β”‚                         #   (catalog.txt lists every one and where it lands on disk)
β”œβ”€β”€ examples/              # Runnable 6502 assembly examples (+ README.md)
β”œβ”€β”€ disk/                  # committed disk content (GAMES/ Scott Adams .PRGs)
β”œβ”€β”€ vendor/                # Pristine upstream sources we port/derive from
β”œβ”€β”€ tools/                 # Host tools: cmake modules, mkdisk, mkfat16, dat2c
β”œβ”€β”€ docs/                  # Documentation
└── tests/                 # Unit and integration tests (GoogleTest)

For detailed development information and project context, see:

  • CLAUDE.md - Development guidelines and architecture documentation
  • docs/README.md - Documentation index: program manuals (MONITOR, DOS, BASIC, ASSEMBLER, FORTH, EDIT, TERM, IRC), the architecture reference (ARCHITECTURE.md), and the internals deep-dive (SYSTEM_INTERNALS.md)

Tips for Effective Use

  1. Start with Help: Use ? to see all available commands
  2. Use Command Recall: The . command saves time when refining commands
  3. File Operations: L:8000 loads and S:8000-8FFF saves; the host shows a file dialog to pick the file
  4. Search Effectively: Use X: with multiple byte patterns for precise matching
  5. Number Conversion: Use D: and H: commands to convert between decimal and hex
  6. Program Development: Load programs with L:, test with G:, save modifications with S:

Acknowledgments

This project stands on the shoulders of the classic 6502 and free-software community. With thanks to the authors whose work we have ported, derived from, or studied:

  • micro-Max by H.G. Muller β€” the remarkably small but complete chess engine (full FIDE rules and move legality) behind CHESS.PRG. We compile the freely published 1.6 source with cc65 and wrap it in a console front-end for MFC-DOS. Pristine upstream sources are kept under vendor/micromax/. https://home.hccnet.nl/h.g.muller/max-src2.html
  • ScottFree by Alan Cox / Swansea University Computer Society β€” the GPL Scott Adams adventure interpreter we port to run the classic Adventure International games on MFC-DOS. The interpreter is in programs/scottfree; the host tool dat2c pre-parses a game .dat into linkable C tables.
  • Scott Adams / Adventure International β€” author and publisher of the twelve classic text adventures (Adventureland, Pirate Adventure, … The Golden Voyage). The shareware .dat databases are obtained separately and are not redistributed here.
  • kilo by Salvatore Sanfilippo (antirez) β€” the inspiration for the EDIT text editor. EDIT is our own implementation (it renders straight to screen RAM rather than a terminal), but its structure and the incremental search are lifted from kilo's design. https://github.com/antirez/kilo
  • EhBASIC (Enhanced 6502 BASIC) by the late Lee Davison β€” the basis for the built-in MFC BASIC interpreter.
  • fig-FORTH for the 6502 by William F. Ragsdale and the FORTH Interest Group (FIG) β€” the public-domain FIG model behind the FORTH module (bank 3). We mechanically convert the original assembler listing to ca65 (verified byte-identical at its native $0200 origin), then relocate it into the ROM module window and wire its I/O to the kernel. The pristine listing and the conversion/verification tooling live under vendor/fig-forth/.
  • cc65 β€” the 6502 C cross-compiler and toolchain used to build the C programs (CHESS.PRG, the Scott Adams games). https://cc65.github.io/
  • XMODEM/CRC for the 65C02 by Daryl Rictor (2002) β€” the serial file-transfer routine behind the emulated 6551 ACIA spike. We retarget its built-in 6551 driver to our memory-mapped ACIA and relocate it for the host test harness; the pristine original is kept under vendor/xmodem/.
  • IBM VGA 8Γ—16 CP437 font β€” the character generator ROM behind the 80Γ—25 display (full CP437: box-drawing, blocks, accented, symbols). The raw bitmap of the IBM VGA ROM font is public domain (U.S. copyright protects scalable outline programs, not bitmap font data). The dump comes from VileR's vga-text-mode-fonts collection (https://int10h.org/), gratefully acknowledged; the pristine VGA8.F16 and the header generator are under vendor/cp437font/.
  • MOS 6581/8580 SID β€” the sound chip our software SID is modeled on (three voices, ADSR, multimode filter). The synthesizer is written from scratch from public SID documentation (register layout, envelope rates, filter behavior) β€” no reSID or other GPL code is used. With thanks to the SID/C64 community whose datasheets and reverse-engineering notes made a faithful model possible. See docs/sound_design.md.
  • The Sunless Vault (VAULT.PRG) β€” an original text roguelike written from scratch for MFC (no ported code). Its integer, turn-based, data-driven engine follows the design of the author's own Dungeon of Yacor, and its play draws inspiration from two classics of the genre β€” Telengard by Daniel Lawrence and Sword of Fargoal by Jeff McCord β€” as design influences only; no code or assets from those games are used. See programs/vault/.

See docs/cc65_to_prg.md for the C-to-.PRG build pipeline.

Where we port or adapt third-party code, the original, unmodified source is preserved under vendor/ so its authorship and licensing remain clear.

The monitor is designed for both interactive exploration and efficient program development workflows.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages