SearchA-ZP › Psion Series 3

Psion Series 3

1991 Open source · MIT Online

An in-browser original Psion Series 3 that boots the authentic EPOC16 (SIBO) v1.91F system ROM. No standalone JavaScript NEC V30 executor exists, so PCjs's pure-JavaScript x86 core (CPUx86) is reused as the V30 — the Series 3's 8086/80186-class CPU — and wired to a from-scratch model of the two-chip ASIC1 + ASIC2 gate-array set: the flat memory decode, the 240×80 one-bit mono LCD, the tick/free-running/watchdog timers, the 8-source interrupt controller and the ASIC2 keyboard scanner. The real ROM passes its power-on self-test, boots the EPOC16 kernel, window server and file server, formats a clean internal RAM disk, and paints the System screen with the app suite, which you can navigate with the arrow keys; because the whole machine is plain JavaScript, the shared debugger single-steps the V30 one true instruction at a time, reads and writes the full register file, inspects the flat 1 MB memory map, and sets breakpoints and write watchpoints.

The original Psion Series 3 (1991) was the first SIBO clamshell palmtop: a pocket organiser with a proper QWERTY keyboard and a suite of built-in applications — System, Word, Agenda, Data, Time, World, Calc and Sheet — resident in ROM, sharing the NEC V30 processor and EPOC16 operating system across the whole Series 3 family, but built around the older two-chip ASIC1 + ASIC2 set and a smaller 240×80 display.

The V30 core is provided by PCjs's open-source (MIT) pure-JavaScript x86 executor, reused here and wired to a from-scratch ASIC1+ASIC2 machine written for emulators.org.

Runs on: Web browser

Psion Series 3 Online Emulator

Play Psion Series 3 using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Psion Series 3 — EPOC16 (SIBO)Psion Series 3Psion Series 3greyOpen ⛶

Machines emulated

Chips

Notes

Embedding

The original Series 3's CPU is a NEC V30 — an 8086-compatible part with the 80186 instruction set, clocked at 3.84 MHz. No standalone JavaScript V30 executor exists, so we reuse the uncompiled PCjs pcx86 bundle purely for its CPUx86 class (a real x86 executor, not a disassembler) and discard the whole IBM-PC machine around it. A bare CPU is built on a 1 MB bus and handed a tiny cmp stand-in and our Psion machine:

var cpu = new CPUx86({ id:'psion.cpu', model:'80186' });   // V30 ~ 8086 pins + 80186 ISA
var bus = new Busx86({ id:'psion.bus', busWidth:20 }, cpu);  // 1 MB V30 address space
cpu.initBus(cmp, bus, cpu, null);
var m = new PsionASIC12(romBytes);
m.install(bus, cpu);                // flat CTRL memory over 0..0xFFFFF + ASIC1/ASIC2 I/O ports
cpu.chipset = m.chipsetStub();       // so the CPU can pull IRQ vectors 0x78..0x7F
cpu.reset();                          // CS:IP = FFFF:0000 -> ROM reset vector JMP C000:0000

Run loop. We drive cpu.stepCPU() ourselves in sub-ticked frames, advancing the ASIC1 timers (4/32 Hz tick, the 512 kHz free-running counter, the 4 Hz watchdog) and delivering interrupts between bursts:

for (var s = 0; s < SUBTICKS; s++) { cpu.stepCPU(SUB); m.tick(SUB); }   // timers -> cpu.intFlags |= INTR
m.render();                                                     // scan RAM 0x0400 -> 240x80 canvas

Because the CPU, bus and ASIC1/ASIC2 model are ordinary JavaScript, single-stepping is cpu.stepCPU(0) (exactly one instruction), registers are the core's regEAX… fields and get*/set* accessors, and breakpoints and watchpoints are host-side checks — no change to the CPU core.

Debugger integration

psion-series3-classic-debug.js reads the live machine from window.EMU_BOOT and calls EmuKit.defineMachine with the shared x86 disassembler and the full 8086/V30 register set, reusing the exact CPUx86 hooks that pcjs-debug.js uses (regEAX…regEDI, getCS/getIP/getPS and their setters, regLIP as the linear PC).

Side-effect-free memory over a FLAT map. Unlike the Series 3a (whose ASIC9 pages the V30's 1 MB into a 16 MB space), the classic Series 3's ASIC1 maps memory directly: 0x00000–0x3FFFF is the 256 KB RAM, 0x40000–0x7FFFF is open bus, and 0x80000–0xFFFFF is the 512 KB ROM (holding the reset vector EA 00 00 C0 00 = JMP C000:0000). The hex/disasm view reads through PsionASIC12.peek — the same flat map the CPU uses, with no effects — so what you see is exactly what the V30 fetches. Two more chips expose the raw physical RAM (to watch the LCD framebuffer at 0x0400) and the ROM linearly.

function stepInsn(){ cpu.stepCPU(0); }   // TRUE single instruction — the payoff of the JS route

Breakpoints are a Set of linear PCs; when any is set the loop steps one instruction at a time and compares cpu.regLIP. Watchpoints wrap the ASIC1 memory-write path (PsionASIC12.writeByte) and pause when a watched V30 address is written. Interrupt injection mimics ASIC1's 8-source controller: a chipset stub returns the IDT vector 0x78 + lowest-pending-source, and the ASIC1 timers assert cpu.intFlags |= INTR when an unmasked source is pending — the CPU takes the vector through the guest IVT exactly as hardware would.

Architecture

The original Series 3 is the earliest SIBO clamshell, sharing the family processor — a NEC V30 (8086 pins, 80186 instruction set, 3.84 MHz) running EPOC16 / SIBO from ROM — but built around the older two-chip ASIC1 + ASIC2 set rather than the 3a's single ASIC9. Modelled here from MAME's hardware description:

  • ASIC1 — system controller. Bus controller, memory decode, LCD controller, the programmable timers and the 8-input interrupt controller. Crucially the memory map is flat (no pager): 256 KB RAM at 0x00000, open bus, then the 512 KB ROM at 0x80000–0xFFFFF. ASIC1 also has a write-protection window (A1ProtectionOn/Off/Upper/Lower) that traps out-of-range writes with an address NMI.
  • ASIC2 — peripheral controller. The system clock, power/standby switching, buzzer, the SIBO eight-channel serial protocol controller (SSD packs), and the keyboard column-scan: the OS writes a column number to A2Control1 and reads the eight rows back on A2External. The Esc/On key sets the on-key status, waking the machine.
  • LCD. A 240×80 one-bit mono panel (not the 3a's 480×160 three-level grey), scanned from a single-plane framebuffer at RAM 0x0400, drawn only when ASIC1 status bit 5 (LcdEnable) is set; the row stride comes from the A1LcdSize register.
  • Timers & interrupts. A 4/32 Hz tick, a 512 kHz free-running counter and a 4 Hz watchdog feed ASIC1's 8-source controller on IDT vectors 0x78–0x7F; ASIC2 folds into it through EINT3, and door/expansion and watchdog conditions raise the V30 NMI.
  • V30 CPU. PCjs's 80186 executor supplies the common instruction set, but the NEC V30 adds its own 0x0F-escape instructions (bit-field INS/EXT, single-bit TEST1/CLR1/SET1/NOT1, packed-BCD ADD4S/SUB4S/CMP4S and nibble ROL4/ROR4) that Intel never had — psion-v30.js adds them to the core. EPOC16's window server draws text with the EXT/INS bit-field blitter, so these are essential.

Every part — the V30 executor, the bus, the ASIC1/ASIC2 model — is plain JavaScript, which is what makes the Series 3 a good debugging target: true per-instruction stepping, live registers, and side-effect-free memory over the real flat map.