Architecture

How STELLAR is built

STELLAR is a virtual CPU written in raw 1802 machine code. It uses up to 32 KB of RAM: about 12 KB for the interpreter, a shared page for coordinating VMs, one small "core" of state per VM, and the rest for program code.

$Memory map

$0000–$2EFF
STELLAR firmware — the interpreter (R3 = 1802 program counter). ~12 KB.
$2F00–$2FFF
CIDP — Common VM Interchange Data Page (RA). Scheduler + per-VM state.
$3000–$33FF
VM1 — core ($3000), stacks ($3100), label array ($3200).
$3400–$37FF
VM2 — core + stacks + labels.
$3800–$3BFF
VM3 — core + stacks + labels.
$3C00–$3FFF
VM4 — core + stacks + labels.
$4000–$4FFF
VM1 program block — up to 4 KB of bytecode (R8).
$5000–$7EFF
VM2–VM4 program blocks — $5000 / $6000 / $7000.

R1802 register roles

STELLAR commits the 1802's sixteen hardware registers to fixed jobs. (These are the host registers — not to be confused with a VM's sixteen virtual registers, which live in RAM.)

RegPurpose
R0Reserved (aliases the DMA pointer) — not used
R1Return to the monitor — the exit vector, supplied by the host monitor rather than by STELLAR
R21802 hardware stack ($2FC0–$2FFF)
R3Main program counter (the interpreter)
R4 · R5 · R6SCRT call · return · link
R7Label-array pointer
R8VM program-block pointer (the virtual PC)
R9VM stack pointer (program + call/return)
RACIDP pointer
RCVM core pointer (which core is running)
RB · RD · RE · RFTemporary internal registers

R1 is not free. The 1802 vectors interrupts through R1, but STELLAR has already spent it: the firmware ends a program with $02E0 SEX R2 / MARK / SEP R1, handing control back to whatever address the monitor left there. Nothing in the firmware ever loads R1, precisely because its value has to be the monitor's. That is also why enabling interrupts with EINT does not get you an interrupt handler — any interrupt that arrives goes to the monitor's exit vector. Servicing interrupts would mean moving the exit vector to another register first.

→SCRT — calls without a stack

The 1802 has no call instruction. STELLAR uses RCA's Standard Call and Return Technique: R3 is the program counter, and switching it with another register (via SEP) transfers control. R4 points at the CALL routine ($0340), R5 at RETURN ($0360), and R6 carries the link address. Every STELLAR CALL/RETURN opcode rides on this.

■The VM core ($3000, $3400, $3800, and $3C00 blocks)

Each VM's entire live state is a 256-byte block. This is exactly what the core visualizer renders. VM1's block starts at $3000; VM2/3/4 at $3400/$3800/$3C00.

OffsetFieldNotes
+$00–$1FRegisters R0–RFSixteen 16-bit general purpose registers, high byte first
+$20–$21Accumulator (Acc)The main 16-bit working value
+$28–$2BDecimal Accumulator (DA)32-bit packed BCD
+$2C–$2EDA Remainder3 bytes, left by a decimal divide
+$2FDA sign / decimal point$00 = positive
+$30Program Command RegisterThe opcode currently executing
+$31–$32Program Location RegisterThe VM's position in its program (virtual PC)
+$33–$36Command Counter4-byte count of opcodes executed
+$37EF FlagZD — Z: $00 normal, $01 invert; D: which EF line, 1–4
+$38Input Port$09–$0F
+$39Output Port$01–$08
+$3AInput / Output ByteThe last byte read or written
+$3BMemory mode$00 absolute, $01 relative — relative is the reset default, and what almost every program wants
+$3CProgram Stack LocationStack pointer, counts down from $FF
+$3DLast PUSH sizeHow many bytes the last push placed on the stack
+$3E–$3FLast LocationUsed with labels
+$40Address Modifier$01–$FF
+$41Memory Address Pointer Direction$00, $01 or $02
+$42–$43Random Number RegisterThe seed RAND advances
+$44Delay Counter$00–$FF
+$45Test FlagSet by the TEST opcodes; read by the branches
+$46Debug Flag$00–$FF; selects the DEBUG display mode
+$47System Status Flag$00 while a program runs, $0B once it ends
+$48Carry Flag$01 means a carry or borrow occurred
+$49Overflow Flag$01 when a result did not fit
+$4AHex/Decimal Flag$00 = hex, $01 = decimal
+$4BunusedReads as $00; nothing in the firmware uses it
+$4CInterrupt Flag$00 disabled, $01 enabled — set by DINT and EINT, read by TESTIS
+$4DRemaining DelayWhat is left of a delay in progress
+$4EExitCode RegisterThe result the last opcode reported
+$4FError Register$00 none; $07 divide by zero, $01 undefined label, $0F unimplemented opcode
+$50Breakpoint RegisterWhether a breakpoint is armed
+$51Breakpoint CommandStop when this opcode is about to run
+$52–$53Breakpoint LocationStop when the program pointer reaches this address; the run loop compares it every opcode
+$54–$55Breakpoint Bytes
+$56Q State$00 off, $01 on — the 1802's single output bit
+$57Hardware Identification$01 on an Elf2K
+$58Call/Return Stack LocationStack pointer for CALL/RETURN, counts down from $7F
+$59VM number$00–$04
+$5AVM Core LocationHigh byte of this VM's core page, $30–$3F
+$5BVM Program BlockHigh byte of this VM's program block, $40–$7F — the base every relative address is resolved against
+$5CVM CIDP LocationHigh byte of the CIDP, $2F
+$5DVM Data Block Size
+$5EInterrupt CounterThe multitasking quantum countdown
+$5FInterrupt Counter MaxHow long each VM's turn lasts; $03 by default
+$60–$62Scratch Register AFirmware workspace
+$63–$65Scratch Register BFirmware workspace
+$66–$67Scratch Register CFirmware workspace
+$68–$6BMath Accumulator (MA)32-bit, most significant byte first
+$6C–$6DMA RemainderLeft by a divide; read it with COPYREMA
+$6EMA sign / decimal point$00 = positive. Negative results are stored as a magnitude plus this byte, not two's complement
+$70–$CFTemporary workspaceFirmware scratch
+$D0–$DFTrace parameters
+$E0–$FF1802 register copyWhere the real CPU's registers are parked across a VM switch

Every field of the block, in address order. Offsets are from the start of the VM's core: add $3000 for VM1, $3400 for VM2, and so on.

⇄CIDP — the multitasking page ($2F00)

The Common VM Interchange Data Page is how the scheduler and the VMs coordinate. The first 64 bytes are a per-VM array (16 bytes each); the system registers at $2F50 hold the global scheduler state. This whole page is decoded live on the multitasking view.

AddressFieldMeaning
$2F00+Per-VM arrayVM#, core page, program page, max size, count, State, #Cmds, mode, status — 16 bytes × 4 VMs
$2F51VM Error$00 none · $01 no VMs · $02 abort
$2F52Current VM#Which VM holds the CPU right now
$2F53 / $2F54Max / Total VMsCeiling (4) and how many are present
$2F55Multitasking Allowed$00 = off, $01 = on
$2F56VM Operation$00 = sequential, $01 = simultaneous
$2F57VM Repeat$00 = run to end, $01 = repeat

VM State values: $00 done · $01 active · $02 completed this cycle · $03 idle/waiting · $04 paused · $05 killed · $06 stopped on error.

↻Cooperative multitasking

VMs are not pre-empted by a timer. Each active VM runs a small number of opcodes — the quantum, default three — then yields, and the scheduler (Do_Next_Task at $0080) scans the CIDP for the next VM in state $01. A VM waiting on serial input (SERIN) yields cooperatively instead of stalling everyone. Because all VMs share one UART, their terminal output interleaves — which the multitasking view makes visible.

Next

Ready to see it move? Open the VM Core Visualizer to watch one core execute, or the opcode reference to look up any instruction.