FANCO-16 VERSION 5 — COMPACT SIREN PROTOTYPE This section supersedes the v3 memory/sound map in the CPU reference appended below. Instruction encodings, timer, blitter and SYS LOAD remain v3. One fixed machine runs every image: no app profiles or automatic map switches. VERSIONED APP IMAGES FAN1 is four literal ASCII bytes (46 41 4E 31), followed by one unsigned machine-version byte: 05 for v5. It is not a host-endian integer. Subsequent segment records retain the existing encoding; 16-bit numbers are little-endian. The assembler directive .version 5 emits this header. Versions must be 1–255. FAN1 enables sound by default; mute through the sound registers if needed. No flags or library codes are introduced by this format. V5 rejects FAN1 images naming another machine version before loading RAM. Legacy unversioned app images remain accepted without a compatibility guarantee. Without .version, assembly output retains its legacy header and identical bytes. MEMORY 0000–000F CPU registers; r6/SP initially FFFE, r7/PC; downward stack 0010–0014 timer vector/control/status 0020–0027 existing input 0030–003F ordinary scratch RAM 0040–005F SID-style sound façade 0060–006F blitter 0070–0071 sixteen generator phase-restart-enable bits 0072–0073 sixteen native raw-frequency format bits 0074–0075 sixteen native SID-clocked-noise model bits 0080–00A7 ten four-byte touch slots 00D0–00E7 two twelve-byte audio-to-RAM capture descriptors 0100–01FF sixteen 16-byte SIREN generator cores 0200–0227 eight five-byte mixer entries 0228–0242 three nine-byte shared filters 0243 master volume 0244–02F3 eight 22-byte, two-tap delay lines 02F4–02F9 three exact 12-bit pulse-width words (generators 0–2) 02FA–02FF three exact AD/SR words (generators 0–2) 0300–FFFF ordinary program/data/stack RAM; never sound MMIO Unassigned low-page bytes are scratch space, not reserved holes. No new interrupt/callback ports have been allocated there. The old 0400–0BFF native sound window is gone. Complete register bits, ranges and examples: SOUND.txt and PROGRAMMING.txt. SID FACTORY CONFIGURATION 40–5F configures generators 0–2 and filter 0 inside this same SIREN chip. Factory effects are dry. Native pan, sends, feedback and delay lines can be layered on it; later SID pitch writes do not erase those additions. This is compatible with FANCO's SID-inspired sound, not exact C64 analog hardware/readback/digi emulation. Native fields use their native encodings. COMPATIBILITY LIMIT 0100–02FF was valid v3 RAM and is now sound control space. Programs using it for data, including image/library initialization, may affect sound. Program bytes/IDs are not silently rewritten; the user chooses any needed v3 port. 80–A7 was already reserved by the v3 specification. Earlier 42/43-app execution checks used the WRONG old 0400 map. They are historical evidence only, not proof for this compact map. During this prototype, use manual app trials and the requested real-device benchmarks, not added or rerun automated test suites. NATIVE SYNTH PORT The new compact native app has four keyboard notes plus four drums, one shared keyboard filter and the full two-tap delay network. PAD/FLUTE second audible layers are explicitly altered. Kick/volume motion is guest code; hardware ramps are not in this map. Two audio-to-RAM capture units support scope and sound-driven graphics. Historical v4 remains a different, preserved prototype; its old native image is not automatically remapped. HOST Before the first SYS PRESENT the host displays the live framebuffer at 120 Hz; after it, only completed presents. Audio is stereo at 48 kHz. Wall-clock pacing keeps graphics alive if browser audio stalls. Pause, resume and clock transitions flush queued audio. The requested MacBook, iPhone, iPhone 8 and possible 120 Hz iPad Pro measurements are still needed; a host-server speed result is not a phone performance claim. V3 CPU REFERENCE (MEMORY/SOUND CHANGES ABOVE TAKE PRECEDENCE) FANCO-16 VERSION 3 PROGRAMMER'S REFERENCE ====================== Provisional edition October 2026 CONTENTS ======== 1. Introduction 2. Memory and registers 3. Instruction format 4. Instruction set 5. Flags and conditional branches 6. Timing 7. The hardware map 8. Timer interrupts 9. Host services 10. Program images 11. The assembler 12. Two complete programs A. Exact order of execution B. The host interface 1. INTRODUCTION ================ The Fanco-16 is a small imaginary computer. It has a 16-bit processor, 64 kilobytes of memory, a 256 by 256 pixel screen with 256 colors, a three-voice sound chip and a blitter for moving pictures around. It runs in a web browser. This manual describes version 3. Version 3 is not compatible with the binary programs of versions 1 and 2. Nothing in it is final yet: instructions may still change. All numbers in tables are hexadecimal unless the text says otherwise. Sizes are in bytes. Words are 16 bits and are stored little-endian, low byte first. 2. MEMORY AND REGISTERS ======================== Memory ------ Memory is 65,536 bytes of RAM. Every byte may be read, written and executed. There is no memory protection. Addresses wrap around: the byte after FFFF is 0000. The screen is separate from RAM. It has its own 65,536 bytes, one byte per pixel. See section 7. Registers --------- The processor has eight 16-bit registers. They are not hidden inside the processor: they live in the first sixteen bytes of RAM. Writing to address 0E changes the program counter, just as a jump would. Reg Addr Conventional use --- ---- ------------------------------- a 00 Argument and result. Scratch. b 02 Argument. Scratch. x 04 Argument. Kept by subroutines. y 06 Argument. Kept by subroutines. p 08 Pointer argument and result. Scratch. r 0A Pointer argument. Kept by subroutines. May serve as a frame pointer. sp 0C Stack pointer. pc 0E Program counter. "Kept by subroutines" means that a subroutine which changes the register must restore it before returning. Scratch registers may be changed freely. These are conventions, not rules: the hardware does not enforce them. Each register can also be used one byte at a time. "a.lo" is the low byte of a and "a.hi" the high byte. Writing to one half leaves the other half alone. The stack grows downward. A push subtracts 2 from sp and then stores a word; a pop loads a word and then adds 2. Nothing checks that sp stays sensible. Starting up ----------- Reset clears all of RAM, the screen, the flags and the sound chip. It then sets pc = 1000 sp = FFFE and, for programs with sound turned on, sets the master volume (address 58) to 15. Finally the program image is copied into memory. An image may contain bytes for any address, so a program can choose its own start address and stack by including them. 3. INSTRUCTION FORMAT ====================== An instruction is an opcode byte, followed by operand descriptors for instructions that take operands. A descriptor is one byte saying where the operand is, sometimes followed by one or two extension bytes. Operand descriptors ------------------- Byte Operand Extension bytes ----- ---------- ---------------------- 00-07 reg none 08-0F reg.lo none 10-17 reg.hi none 20-27 [reg] none 28-2F [reg++] none 30-37 [--reg] none 38-3F [reg+d] d, a signed byte 40 [addr] addr low, addr high The low three bits of the descriptor select the register, in this order: 0 a 1 b 2 x 3 y 4 p 5 r 6 sp 7 pc Any other descriptor byte is illegal and stops the machine with a trap. [reg] is the memory the register points to. [reg++] uses that memory and then advances the register by the size of the access, 1 or 2. [--reg] first moves the register back by the size of the access and then uses the memory. Immediate values ---------------- There is no separate immediate addressing mode. An immediate value is simply [pc++]: the value is stored right after the instruction, and the processor reads it there and steps past it. The assembler lets you write the number itself: mov a, 1234 ; is really mov a, [pc++] ; followed by 34 12 The result is the same as an immediate mode would give, and the rules stay simple. Do not write through [pc++] or [--pc], and do not read through [--pc]. Those encodings trap. [pc] and [pc+d] are ordinary memory accesses. Order of bytes -------------- An instruction with two operands stores the destination descriptor first, then the source descriptor. Each descriptor is followed by its own extension bytes. Inline [pc++] data comes last, after all descriptors. 4. INSTRUCTION SET =================== Opcode map ---------- Code Instruction Bytes ----- ----------------------- ----- 00 halt 1 01 nop 1 02 ret 1 03 call addr 3 04 call src 2+ 05 sleep n 3 06 blit 1 07 sys load 1 08 sys present 1 09 reti 1 0A wait 1 10-17 inc reg 1 18-1F dec reg 1 20-23 mov a/b/x/y, imm16 3 24-27 pop a/b/x/y 1 28-2B push a/b/x/y 1 2C-2F mov a/b/x/y, [sp] 1 30-33 mov [sp], a/b/x/y 1 40-4A branch, short 2 50-5A branch, long 3 60-6D two-operand op, word 3+ 6E movzx 3+ 6F movsx 3+ 70-7D two-operand op, byte 3+ 7E mod, word 3+ 7F mod, byte 3+ "3+" means three bytes plus any descriptor extensions and inline data. Every opcode not listed is illegal and traps. The single-byte forms from 10 to 33 are short encodings of common instructions. The assembler chooses them automatically. Two-operand operations ---------------------- Word Byte Mnemonic Effect ---- ---- -------- ---------------------- 60 70 mov dst = src 61 71 add dst = dst + src 62 72 sub dst = dst - src 63 73 and dst = dst AND src 64 74 or dst = dst OR src 65 75 xor dst = dst XOR src 66 76 cmp flags from dst - src 67 77 shl shift left 68 78 shr shift right, logical 69 79 sar shift right, signed 6A 7A mul dst = dst * src 6B 7B div dst = dst / src 6C 7C out screen[dst] = src 6D 7D in dst = screen[src] 7E 7F mod dst = dst MOD src The word or byte form is chosen from the registers you name: "add a, b" is a word add, "add a.lo, b.hi" a byte add. When no register is involved, as in "mov [0x58], 15", the size must be given: "mov.b [0x58], 15". Rules for operands: - The destination of add through div must be a register or a register half. - mov may write to memory. Its source may then be a register, a register half or an immediate value, but not other memory. - out and in exist only in the byte form. The 6C and 6D opcodes trap. The screen operand must be memory, as in "out [p++], a.lo"; the other operand must be a register half. - mul keeps the low 16 bits of the product. - div is unsigned and rounds down. mod is the remainder of the same division, so 1234 div 10 is 123 and 1234 mod 10 is 4. For either one, dividing by zero gives FFFF (or FF) and sets N and C. - mod is the odd one out in the opcode map: its word and byte forms are 7E and 7F, not 16 apart like the others. Widening loads -------------- movzx and movsx load a byte into a whole word register, filling the high byte with zeros (movzx) or with copies of the sign bit (movsx). movzx a, [p++] ; a = next byte, 0-255 movsx x, [r+3] ; x = -128 to 127 The destination may be a, b, x, y, p, r or sp, but not pc. The source may be a register half, any byte in memory or a one-byte immediate. No size suffix is needed. Control instructions -------------------- halt Stops the processor. nop Does nothing. call addr / call src Pushes the address of the next instruction and jumps. "call [r+2]" jumps to the address stored at r+2. ret Pops the return address into pc. sleep n Waits n extra cycles, 0 to 65535. blit Runs the blitter. See section 7. sys load / sys present Host services. See section 9. reti / wait Interrupt support. See section 8. Aliases ------- The assembler accepts these shorter spellings: jmp t mov pc, t push reg mov [--sp], reg pop reg mov reg, [sp++] inc a.lo add a.lo, 1 dec a.lo sub a.lo, 1 push and pop work for every register, not only for the four that have single-byte forms. There is no neg instruction. To negate a, use "xor a, 0xFFFF" followed by "inc a". 5. FLAGS AND CONDITIONAL BRANCHES ================================== Flags ----- There are four flags: Val Flag Set when --- ---- ------------------------------- 1 N The result is negative (its top bit is 1). 2 Z The result is zero. 4 C Carry out of add, or borrow out of sub and cmp. 8 V Signed overflow. - add and inc set all four flags. - sub, dec and cmp set all four flags; there C means a borrow happened. - and, or and xor set N and Z and clear C and V. - Shifts use only the low bits of the count (0-15 for words, 0-7 for bytes). A count of zero leaves the flags alone. Otherwise C is the last bit shifted out and V is 0. - mul sets C on unsigned overflow and V on signed overflow. - mov, movzx, movsx, in, out, branches and calls never change the flags. The last point is useful: you can compare, load another value, and then branch on the result of the comparison. Branches -------- Code Name Taken when Meaning ---- ---- ------------ ----------------- 40 bra always 41 bz Z equal, zero 42 bnz not Z not equal 43 blt N <> V less, signed 44 bge N = V greater or equal, signed 45 blo C lower, unsigned 46 bhs not C higher or same, unsigned 47 ble Z, or N <> V less or equal, signed 48 bgt not Z, and greater, signed N = V 49 bls C or Z lower or same, unsigned 4A bhi not C, and higher, unsigned not Z These codes are the short forms. A short branch holds a signed byte offset, counted from the end of the branch instruction, so it can reach 128 bytes back or 127 forward. The long forms, 50 to 5A, hold a full address instead. You need not choose: the assembler picks the short form when the target is near enough and the long form otherwise. See section 11. 6. TIMING ========== The processor runs at 120,000,000 cycles per second of virtual time. Every byte of an instruction costs one cycle: its opcode, its descriptors and their extensions. Reading an immediate value costs one more cycle per byte read. Other memory accesses are free. So "mov a, 1234" costs 3 cycles, and "ret" costs 1. - halt costs 1 cycle. - sleep n costs 3 + n cycles. - blit costs 1 + width * height cycles. - Entering an interrupt costs 4 cycles. This is virtual time. The browser keeps it in step with the real clock while a program plays; the benchmark page runs as fast as it can. The sound chip produces one sample every 2,500 cycles, which is 48,000 samples per second. A display frame at 60 Hz is 2,000,000 cycles. 7. THE HARDWARE MAP ==================== Address Contents --------- ------------------------------ 00-0F Processor registers 10-14 Timer 15-1F Reserved 20-27 Input, written by the host 28-2F Reserved 30-3F Free for your use 40-5F Sound chip 60-6F Blitter 70-FF Reserved 0100-FFFF Free RAM 1000 Usual program start Hardware registers are ordinary memory. Read and write them with mov. Timer ----- Addr Contents ----- ---------------------------------- 10-11 Address of the interrupt handler 12 Rate: 0 = off 1 = 60 Hz 2 = 120 Hz 3 = 240 Hz 4 = 480 Hz 13 Missed ticks, up to 255. You may clear it. 14 Bit 0 is 1 while the handler runs. Read only. Section 8 explains how the timer is used. Input ----- Addr Contents ----- ---------------------------------- 20-21 Buttons, one bit each: 1 left 2 right 4 up 8 down 16 A 32 B 22-23 Pointer X, 0-255 24-25 Pointer Y, 0-255 26-27 Pointer flags: 1 button is down 2 pointer is over the screen On a keyboard, the arrow keys or WASD move, Space or Z is A, and Enter or X is B. On a touch screen, the on-screen pad and buttons do the same. The host updates these bytes between slices of execution, never in the middle of an instruction. The screen ---------- The screen is 256 by 256 pixels. Pixel (x, y) is byte y * 256 + x of screen memory. Draw with out and read back with in: mov p, 0x8040 ; x = 0x40, y = 0x80 mov a.lo, 0xE0 ; red out [p++], a.lo ; draw, then step right Each byte is a color in RGB 3-3-2 form: bit 7 6 5 4 3 2 1 0 red green blue So E0 is bright red, 1C bright green, 03 bright blue, FF white and 00 black. There are no palette registers. Sound ----- The sound chip has three voices with waveforms, envelopes and a filter. Its registers are described in a separate manual, SOUND.txt. All music and sound effects are made by the program itself. Blitter ------- The blitter copies or fills rectangles on the screen. Set up its registers and then execute the blit instruction. Addr Size Contents ----- ---- ------------------------------- 60 word Source address 62 word Destination X, signed 64 word Destination Y, signed 66 word Width, 0-256 68 word Height, 0-256 6A word Source row stride, in bytes 6C byte Mode 6D byte Color, or the transparent color in mode 2 Modes: 0 Fill the rectangle with the color. 1 Copy a picture from RAM to the screen. 2 Copy a picture from RAM, skipping pixels of the transparent color. Use this for sprites. 3 Copy from one part of the screen to another. Overlapping areas are copied correctly. The rectangle is clipped at the screen edges, so a sprite may be partly off screen. It never wraps to the other side. The source address advances by the stride after each row. A width or height above 256, or a mode above 3, traps. blit costs 1 + width * height cycles, even for pixels that are clipped or skipped. 8. TIMER INTERRUPTS ==================== The timer calls a routine of yours at a steady rate. Store the routine's address in 10-11 and the rate in 12. A program whose work is all done in the handler needs only an idle loop: mov.w [0x10], frame mov.b [0x12], 1 ; 60 Hz idle: wait bra idle frame: push a ... ; move, draw sys present pop a reti What happens on a tick ---------------------- Between two instructions, the processor checks for a tick. If one has arrived, it pushes pc, then pushes the flags, sets bit 0 of address 14 and jumps to the handler. This costs 4 cycles. reti pops the flags, then pc, and clears bit 0 of address 14. The interrupted program continues as if nothing had happened, provided the handler kept every register it changed. Push and pop them in the handler. wait idles until the next tick. Without wait, a main loop would spin and waste cycles. Rules ----- - Ticks come at fixed moments of virtual time, every 2,000,000 cycles at 60 Hz, no matter when the timer was started. At 240 Hz, each 60 Hz display frame gets exactly four ticks. - At most one tick can be waiting. A tick that arrives while one is waiting, or while the handler is running, is not delivered. It adds 1 to the missed-tick count at address 13 instead. - Handlers do not nest. Calling wait inside a handler does not let the next tick in. - A tick never interrupts an instruction. A long sleep or blit delays it. - Virtual time stops while sys load waits for the network, so no ticks are lost then. 9. HOST SERVICES ================= sys present ----------- sys present tells the screen that a picture is finished. The display shows the screen as it was at that moment, and keeps showing it until the next sys present. Meanwhile the program may clear and redraw the screen without the viewer seeing half-drawn pictures. sys present takes one cycle and changes no registers or flags. A program that never uses it is shown the screen as it is, unfinished pictures included. To avoid a flash of black before the first picture, present once before starting to draw. sys load -------- sys load fetches a library from the program server and copies it into memory. Put the library's 32-bit program ID in b (high word) and a (low word). For a relocatable library, put the load address in p. mov a, 0xF70A ; ID 0x7DA4F70A mov b, 0x7DA4 mov p, 0x4000 ; load address sys load cmp a, 0 bnz failed The program pauses while the library is being fetched. Virtual time stops. When it continues, a holds the result: 0 Loaded. 1 The fetch failed, or the image was bad. 2 The image is a program, not a library. All other registers and the flags are left as they were. If the load fails, memory is not changed at all. Only libraries can be loaded this way. sys load is for fetching what a program depends on, not for starting another program. A library may put bytes anywhere, including the registers and hardware below address 80. A sound player might set up the sound chip, for example. A library that does this must say so in its documentation, and a program that loads it must allow for it. A library that writes pc or sp takes over the program that loaded it. Calling a library ----------------- By convention, a relocatable library begins with a table of the addresses of its routines. The loader corrects those addresses for the load address, so after loading at 4000: mov r, 0x4000 call [r] ; first routine call [r+2] ; second routine A fixed library is called at the addresses its documentation gives: call 0x8400 10. PROGRAM IMAGES =================== A program or library is stored as an image: a four-byte name tag, called the magic, followed by records. The magic --------- Magic Kind Runs Loads ----- ----------------------- ---- ----- FAN0 Program, sound on yes no FAn0 Program, sound off yes no FANL Library, fixed address no yes FANR Library, relocatable no yes "Runs" means the page's Run button will start it. "Loads" means sys load accepts it. Records ------- The records follow the magic, one after another, up to the end of the image. Each record says where some bytes go. First bytes Record ------------ ------------------------------- 01-FF Length 1-255. Then the address, low byte first, then the data. 00 00 The rest of the image, placed at address 1000. 00 01 The rest of the image, placed at address 0000. 00 02 Long record. A word holding the length minus one (at least 255), the address word, then 256 to 65,536 bytes of data. 00 03 Relocation table. See below. For example, this whole image is a program that halts at once: 46 41 4E 30 "FAN0" 00 00 rest goes to 1000 00 halt The "rest of the image" records save a few bytes for the common case of one block of code. They must come last. An image is checked completely before anything is copied. It is rejected if a record is cut short, if two records overlap, if a record runs past FFFF, if the image is empty, or if the records hold more than 65,536 bytes. Relocatable libraries --------------------- A FANR image is assembled as if it started at address 0. Its record addresses are offsets from the load address, which the caller gives. The record 00 03 lists the places that hold addresses. It is a word holding the count, then that many word offsets, in increasing order. When the library is loaded at address B, B is added to the word at each listed offset. The table may appear once, and must come before any "rest of the image" record. The assembler builds this table for you. 11. THE ASSEMBLER ================== The page assembles source text written in the following form. Lines ----- label: mnemonic operand, operand ; note Everything after a semicolon is a comment. Labels end with a colon. Names consist of letters, digits and underscores, and must not begin with a digit. Label names are case-sensitive; mnemonics, register names and directives are not. Operands are separated by commas. Square brackets mean memory: mov a, b ; register mov a, [p+2] ; memory at p+2 mov a.lo, [p++] ; byte, then step p mov [--sp], r ; push r mov.w [0x66], 256 ; word in memory Numbers ------- 1234 decimal 0x4D2 hexadecimal $4D2 hexadecimal 'A' a character, here 65 -5 negative numbers are allowed Strings ------- A string in double quotes stores one byte per character. Strings may only appear in .byte lines, and may be mixed with numbers. No zero byte is added unless you add one yourself: title: .byte "GAME OVER", 0 These escapes work in strings and characters: \n newline \r return \t tab \0 zero byte \\ backslash \" double quote \' quote \xHH any byte Expressions ----------- Anywhere a number is allowed, you may write an expression. From lowest to highest precedence: | ^ & << >> + - * / % - + ~ (unary) ( ) lo(e) hi(e) lo(e) is the low byte of e and hi(e) the high byte. Arithmetic is on whole numbers; / and % round toward zero. For example: .equ ROWS, 20 .word ROWS * 8, end - start mov a.lo, hi(table) Branch size ----------- Write branches without a suffix and the assembler chooses: the short two-byte form if the target is close enough, the long three-byte form if not. Add .b or .w to insist on one: bz done ; assembler decides bz.b done ; always short bz.w done ; always long Use the suffix only when your code depends on the exact length of an instruction. A .b branch that cannot reach its target is an error. Directives ---------- .org addr Continue assembling at this address. The default is 1000. Sections may be placed anywhere but must not overlap. .byte v, v, ... Store bytes (-128 to 255) or strings. .word v, v, ... Store words (-32768 to 65535). .equ NAME, value Define a constant. It may be used before it is defined. .sound on / .sound off Make a FAN0 or FAn0 program. The default is on. .library Make a fixed-address library (FANL). .relocatable Make a relocatable library (FANR). This must come before any code. Labels then count from 0, and every word that holds a label's address is listed for relocation automatically. A label used as a byte, a displacement or in other arithmetic, such as label * 2, is an error, because it could not be corrected at load time. A difference of two labels, such as end - start, is a plain number and is allowed. The assembler stops with an error rather than guess. A number out of range, an unknown name or a missing size suffix is reported with its line number. There are no macros. 12. TWO COMPLETE PROGRAMS ========================== A subroutine call ----------------- .org 0x1000 mov a, 0x1234 ; 20 34 12 call bump ; 03 lo hi halt ; a is now 0x1235 bump: inc a.lo ; low byte only ret A dot moving at 240 Hz ---------------------- .org 0x1000 mov.w [0x10], tick mov.b [0x12], 3 ; 240 Hz idle: wait bra idle tick: push a inc x out [x], x.lo sys present pop a reti x is never saved by the handler, so it keeps counting from one tick to the next. Each tick draws one more pixel. APPENDIX A. EXACT ORDER OF EXECUTION ===================================== Most programs can ignore this appendix. It matters when an instruction changes a register that it also uses, such as "mov [sp++], sp". 1. The whole instruction is decoded and checked first. An illegal instruction changes nothing at all, not even pc. 2. pc is moved past the instruction. 3. The source is read. Its address is formed, a predecrement is applied, the value is read and a postincrement is applied. An immediate is read here, moving pc on. 4. The destination address is formed using the registers as they are now, after the source's updates. Its predecrement is applied, the value is written, and its postincrement is applied last. 5. A value written to a register replaces any earlier update of that register in the same instruction. call works out the target completely, then pushes the return address, then sets pc. ret pops, then sets pc. An interrupt pushes pc and then the flags, in the same way as call. Writes to the sound registers are timed at the cycle on which the instruction finishes, and every byte written counts, even if its value does not change. Register updates caused by [reg++] and [--reg] are not sound writes. APPENDIX B. THE HOST INTERFACE =============================== This appendix is for people changing the emulator, not for people writing programs. The processor exists only in WebAssembly; there is no JavaScript copy of it. The class WasmVM in wasm-vm.ts wraps it: ram, screen The two 64 KiB memories. readReg(i), flags, cycles, instructions, halted Machine state. step(), run(n), runUntil(cycle) Execute. run stops at a pending host service. drainSoundEvents() Sound-register writes, with their cycle numbers. loadImage(image) Reset and install a program. Libraries are refused with "Not runnable". installImage(image, base) Install a library without reset. waitingForLoad, loadRequestId A sys load is waiting; this is its ID. completeLoad(status) Finish a sys load with status 0, 1 or 2. pendingPresent, completePresent() A sys present is waiting. MachineRuntime in runtime.ts runs the machine in step with the clock, captures each presented picture and feeds the sound chip.