SERIO
1C F0 1C
Six bytes on a terminal. That's all it took to make me jump out of my chair.
The Moment
After yesterday's philosophical journey connecting lookup tables to ENIAC's glowing bulbs, today was about the real thing. The GMS/359, assembled and synthesized, waiting on the bench. A 2001 Mitsumi PS/2 keyboard plugged in. A terminal window open at 115200 baud.
Upload the IPL binary. Release reset.
SERIO
The UART greeting appeared. The system was alive. The CPU had executed LFI, ST, SIO, TIO, BC — the whole boot sequence. Video showed "IPL SYSINIT 1". Everything was ready.
And then: waiting. The cursor blinking. The keyboard waiting. Channel I/O suspended, polling for a keypress that hadn't happened yet.
I pressed 'A'.
1C F0 1C
Three hex pairs. Press and release. The scan code 1C, followed by the release sequence F0 1C.
It worked. It actually worked.
What Just Happened
Let me trace the path of that single keypress through the system:
Finger
↓
┌─────────────────┐
│ Mitsumi PS/2 │ ← Mechanical switch closes
│ Keyboard (2001) │ → Sends serial bits at ~10kHz
└────────┬────────┘
↓ PS/2 clock + data
┌─────────────────┐
│ GMS 2591 │ ← Physical layer: ps2_rx deserializes
│ Keyboard Ctrl │ → Byte 0x1C lands in FIFO
└────────┬────────┘
↓ WishBone bus
┌─────────────────┐
│ GMS 2870 │ ← Channel executes READ CCW
│ Multiplexor Ch │ → Transfers byte to kbd_buffer
└────────┬────────┘
↓ Memory
┌─────────────────┐
│ GMS 2050 CPU │ ← LB R1, [R8] loads scan code
│ │ → AR R10, R1 computes table index
│ │ → LB R2, [R10] fetches ASCII '1'
│ │ → (repeat for 'C')
└────────┬────────┘
↓ Channel I/O
┌─────────────────┐
│ GMS 2870 │ ← Channel executes WRITE CCW
│ Multiplexor Ch │ → Sends "1C " to UART
└────────┬────────┘
↓ Serial
┌─────────────────┐
│ UART Terminal │ ← 115200 baud
│ │ → "1C " appears on screen
└─────────────────┘
One keypress. Nine major components. Dozens of state machine transitions. Hundreds of clock cycles. And out comes 1C.
Then the key releases, the keyboard sends F0 1C, and the whole dance happens twice more: F0 , then 1C .
The Legendary Eight-Byte Monster
After the initial euphoria, I had to try it. The Pause key. The only key in the PS/2 protocol that uses the E1 prefix. The only key that sends its own release codes immediately. The eight-byte monster.
I pressed Pause.
E1 14 77 E1 F0 14 F0 77
Eight bytes. Eight complete cycles through the entire I/O stack:
E1 - Extended prefix (special!)
14 - First scan code
77 - Second scan code
E1 - Extended prefix again
F0 - Release prefix
14 - First code release
F0 - Release prefix
77 - Second code release
The GMS/359 processed all eight without blinking. Eight SIO commands. Eight TIO polling loops. Eight table lookups. Eight UART transmissions. All in the time it takes to press and release a single key.
The Channel I/O architecture — inherited from the IBM System/360 — showed its elegance here. The CPU doesn't bit-bang the keyboard. It doesn't busy-wait on the UART. It issues high-level commands: "read a byte from device 11h", "write three bytes to device 12h". The Channel handles the details. The CPU moves on.
The Lookup Tables in Action
Remember yesterday's blog post about the hex conversion tables? The ones that reminded me of ENIAC's glowing decimal displays? They're not just theory anymore.
Every hex digit you see on that terminal came from a 256-byte table lookup:
LFI R10, hex_hi_table ; Table base
AR R10, R1 ; Add scan code as offset
LB R2, [R10] ; Fetch ASCII digit
When the scan code is 1C:
hex_hi_table[0x1C]='1'(because 0x1C is in the 0x10-0x1F range)hex_lo_table[0x1C]='C'(because 0x1C & 0x0F = 0x0C)
The AR instruction — added just yesterday! — made this possible. Load base, add offset, fetch result. No shifts, no masks, no conditionals. Pure table-driven conversion.
The ENIAC engineers would approve.
What We Have Now
The GMS/359 Computing System, as of today:
CPU (GMS 2050):
- 16 × 32-bit general purpose registers
- 12 instructions: LR, AR, LFI, LB, SB, ST, BC, SIO, TIO, LPSW, MVI, NOPR
- 24-bit addressing (16 MB address space)
- Base register addressing with NASM-style syntax
Channel I/O (GMS 2870 Multiplexor):
- S/360-style Channel Command Words
- READ and WRITE operations
- Device polling via TIO
Peripherals:
- Video controller (device 10h) — text output to VGA
- UART (device 12h) — serial I/O at 115200 baud
- PS/2 Keyboard (device 11h) — with FIFO buffer
Memory:
- 64 KB SRAM (directly addressable)
It's not much by modern standards. But it's real. It runs on actual hardware — a Cologne Chip GateMate FPGA. It executes real machine code. And now, it talks to a keyboard from 2001 and sends hex dumps to a terminal.
What's Next
The immediate TODO list:
- 24-bit addressing cleanup
- Console area (like a real S/360 operator console!) as another device connected to multiplexor channel
- More instructions: SR (subtract), NR/OR/XR (logical), shifts
- PROBLEM STATE 🤓
The bigger picture:
- Selector Channel (GMS 2860) for disk-like devices
- Interrupt handling
- Maybe, someday, enough to run a simple monitor program
- If so, OS/359
The Feeling
There's something deeply satisfying about watching bytes flow through a system you built from scratch. Every gate, every state machine, every instruction — you know where it came from. When 1C appears on the screen, you can trace it back through the UART, through the Channel, through the CPU, through the lookup table, through the AR instruction, all the way back to a finger pressing a key.
This is why we build things. Not because the world needs another PS/2 keyboard hex dumper. But because the act of building teaches us what we could never learn by reading. The IBM System/360 Principles of Operation manual is 150 pages of dense technical prose. But nothing in those pages prepared me for the feeling of seeing E1 14 77 E1 F0 14 F0 77 scroll across my terminal.
The GMS/359 Computing System: Because sometimes you need to build a 1960s mainframe to understand why the 1960s mattered.

No comments:
Post a Comment