FM-7
FM-7 is a compact emulator built here in JavaScript around a genuine Motorola 6809 core, running Fujitsu's 1982 home computer in the browser so it boots straight to the F-BASIC 3.0 "Ready" prompt with no downloads. The FM-7 is a dual-6809 design, so this build runs two copies of the core: a main 6809 for F-BASIC and the boot ROM, and a sub 6809 for the subsystem monitor that owns the 48 KB of video RAM and the keyboard. Every register, chip and byte of memory on both processors is a plain JavaScript object, so the whole machine can be paused, single-stepped and inspected live in the shared debugger.
Runs on: Web browser
FM-7 Online Emulator
Play FM-7 using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| F-BASIC | FM-7 | Fujitsu FM-7 | grey | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
There is no ready-made pure-JavaScript FM-7, so this build reuses the proven JavaScript Motorola 6809 core from JSVecX (e6809.js, a port of Valavan Manohararajah's e6809.c) and wraps the whole FM-7 around it. The FM-7 is a dual-6809 machine, so we run two copies of the core: a MAIN 6809 that runs F-BASIC and the boot ROM, and a SUB 6809 that runs the subsystem monitor ROM — the SUB owns all 48 KB of video RAM, draws the 8×8 ANK font in software and scans the keyboard.
Boot. Give each 6809 its own bus object with read8 / write8, map the ROMs, reset both, and drive them from an interleaved loop you own — one main instruction, then enough sub instructions to keep pace — so neither core hides a run loop from the debugger:
var main = new e6809(); main.init(mainBus); // F-BASIC + boot ROM
var sub = new e6809(); sub.init(subBus); // subsystem monitor ROM
main.e6809_reset(); sub.e6809_reset();
(function field(){
for (var c = 0; c < 33600; ) { // ~2 MHz / 60 Hz
c += main.e6809_sstep(mainIRQ(), mainFIRQ(), 0);
if (!subHalt) sub.e6809_sstep(subIRQ(), subFIRQ(), subNMI());
}
render(); requestAnimationFrame(field);
})();
The whole machine is plain objects. Everything the debugger needs is a field or method on the two e6809 instances or on window.EMU_BOOT — there is no wasm heap:
| Member | Kind | What it does |
|---|---|---|
mainCpu.e6809_sstep(irq, firq, nmi) | method | Execute exactly one 6809 instruction with the current interrupt lines; returns its cycle count. The single-step primitive (a third nmi argument was added for the sub's display timer). |
mainCpu.reg_a / reg_b / reg_dp / reg_cc / reg_pc | field | The 8- and 16-bit registers as numbers; reg_x/y/u/s are boxed (.value). |
EMU_BOOT.mainRead(a) / subRead(a) | method | Side-effect-free reads of either CPU's 64K bus (used by the debugger's memory views). |
EMU_BOOT.vram (48K) / shared (128B) / mainRAM (32K) | field | Raw byte arrays — the three video planes, the main/sub shared block and user RAM. |
EMU_BOOT.pressKey(code) | method | Inject one FM-7 keyboard code (drives the same latch the physical keyboard does). |
Because both CPUs, both buses and all RAM are ordinary JavaScript, the debugger single-steps the main 6809, reads and writes its registers directly, and implements breakpoints and watchpoints as host-side checks around the loop — no changes to the reused core beyond the added NMI entry.
Debugger integration
The plug-in (fm7-debug.js) reads the live machine from window.EMU_BOOT and hands the shared framework a description of the FM-7. Nothing is patched into the emulator; the whole surface is ordinary field access.
- Registers are the MAIN 6809's —
A B D X Y U S DP PC CCplus theE F H I N Z V Ccondition flags decoded fromreg_cc— read each refresh and written back the same way. Single-step and watch them change. - Memory is four chips: a side-effect-free MAIN CPU bus, the 32K user RAM, a side-effect-free SUB CPU bus and the 48K VRAM. Reads run with I/O side effects suppressed (so an auto-refreshing view never clears an interrupt flag or flips the CRT-enable strobe). Disassembly uses the shared
m6809decoder. - Single step is
mainCpu.e6809_sstep— one MAIN 6809 instruction, with the SUB 6809 advanced in lockstep so the shared-RAM handshake stays coherent. - Breakpoints are host-side: with any set, the loop compares
mainCpu.reg_pcbefore each instruction. Watchpoints wrap both CPUs' bus-write paths and pause on a write to a watched address. - Input is the real FM-7 encoder: the physical keyboard maps each character to its FM-7 code, and an on-screen FM-7 keyboard drives the same codes for touch.
Architecture
The FM-7 is a dual-6809 machine, and this build keeps each block as its own object:
- Two
e6809cores — a MAIN 6809 (F-BASIC + boot ROM) and a SUB 6809 (subsystem monitor ROM), run by one interleaved host loop and single-stepped bye6809_sstep. - Main bus — 32K RAM at
$0000, the F-BASIC 3.0 ROM at$8000(switchable to RAM via$FD0F), the shared-RAM window at$FC80, the$FD00I/O page and the boot ROM at$FE00. - Sub bus — 48K VRAM at
$0000(blue/green/red planes, 16K each), console and work RAM, the shared RAM at$D380, the display and keyboard ports at$D400, and the subsystem monitor ROM at$D800. - The handshake — the main CPU halts the sub through
$FD05to take the shared-RAM bus, drops a graphics command in, releases the sub and polls a busy flag ($D40A) until it is done. The sub raises an "attention" FIRQ on the main by reading$D404; the main's 2 ms timer and the sub's 20 ms display NMI keep both sides ticking. - Video — the sub software-renders the 8×8 ANK font into VRAM; the three planes are combined into a 640×200, eight-colour picture and drawn to the canvas. With no cassette or disk, F-BASIC sizes memory, prints its banner and drops to
Ready— a complete, inspectable machine that boots straight to BASIC.
Sound
This is a synthesis (Pattern S) integration: the reused 6809 core is CPU-only, so the FM-7's sound hardware is modelled from scratch and driven by the program's own writes to the sound ports the MAIN 6809 decodes. The base FM-7 has two sound sources, both on the main I/O page, and both are emulated:
- AY-3-8910 PSG — the machine's three-voice programmable sound generator, used by F-BASIC's
PLAYandSOUND. It is addressed with the standard General-Instrument BDIR/BC1 bus protocol: a write to$FD0Dsets the bus mode (3 = latch register address, 2 = write data) and$FD0Eis the data latch. Those writes are routed straight into a faithful, self-authored AY-3-8910 (fm7-ay.js, the same chip first written for Time Pilot): three square-wave tone channels (f = clock / (16 · period)), a 17-bit-LFSR noise generator, a 16-step hardware envelope and the 4-bit logarithmic volume DAC. - Buzzer — the single-bit beeper gated by a write to
$FD03(used byBEEP), rendered as a fixed ~1200 Hz square while enabled.
Sample rate / pitch. The PSG is clocked at the FM-7's 1.2288 MHz and the chip renders directly at EmuAudio.sampleRate (it advances its internal generators by (clock/8)/sampleRate ticks per output sample and averages), so pitch is exact with no separate resampling stage. Each video frame the run loop asks the chip for exactly Math.round(EmuAudio.sampleRate / 60) mono samples, mixes in the buzzer, duplicates the result to left and right and pushes one round(sampleRate/60)-pair interleaved-stereo Int16 frame to the shared sink via EmuAudio.push.
Mute contract. window.EMU_BOOT.transport.isMuted() / setMute(m) delegate to EmuAudio. It starts muted (browsers block audio before a user gesture); the shared Sound button resumes the context and unmutes from a real click. Sound plays whenever the software makes it: type an F-BASIC PLAY, SOUND or BEEP and it comes straight out of the PSG and buzzer.