Terminals - the ND-120's console on a screen and a keyboard¶
Full path: Verilog/Terminals/
Created 27-AUG-2026.
Every ND-120 FPGA target so far puts the OPCOM/SINTRAN console on a UART and
you attach a PC terminal program to the other end. Two of the new targets -
MiSTer (fpga/mister/) and MEGA65 (fpga/mega65/) - are whole
machines with their own HDMI/VGA output and their own keyboard. What is
missing is the piece in between: a terminal. That is what lives here.
First test rig: the Nexys 4 DDR, not MiSTer or MEGA65 - it is the only board that today has a booting SINTRAN, a VGA connector AND a USB keyboard host all at once, so the terminal was the only new thing in the build.
That work is DONE. The screen-and-keyboard console is built and running
on the Nexys, the MiSTer and the MEGA65. There is no plan document to read:
fpga/nexys4ddr/PLAN-vga-console.md was deleted in commit c4896a4 because
it was finished, and docs/PLAN-vt100-terminal-core.md says of itself that
its stages are "history, not plan". This README is the current state.
This folder is board-independent RTL. It knows nothing about MiSTer, MEGA65, Tang or Nexys. Each board supplies the two ends - a source of key events and a video sink - and the board folder owns that wiring.
Module list (31-AUG-2026)¶
| File | What it is | Used by |
|---|---|---|
rtl/terminal_top.v |
the whole terminal: bytes in, pixels out. Selects VT100 vs TDV2200 at compile time - see below | every board |
rtl/terminal_ctrl.v |
VT100 (type 6): ESC/CSI parser, CUP/ED/EL, SGR, DECSTBM, charsets | every board (via -DND120_TERMINAL_VT100) |
rtl/terminal_ctrl_tdv.v |
TDV2200 (type 93, DEFAULT): TDV-native C0 table, DLE binary cursor addressing, generic mode/DCS swallow | every board (default, no define) |
rtl/text_screen.v |
the pixel pipeline: RAM -> font -> pixel, attributes | every board |
rtl/char_ram.v |
cells (80x24 VT100 / 80x25 TDV2200), 8 bits character + 8 bits attribute | every board |
rtl/byte_fifo.v |
16-byte slack in front of the controller (region scroll > 1 byte time) | every board |
rtl/vga_timing.v |
parameterised sync generator, 800x600@60 default | every board |
rtl/font_rom.v |
8x16 glyphs from font/font8x16.hex: four 128-glyph pages - US, Norwegian, DEC Special Graphics (VT100 build), TDV2200 character set 2 (TDV build - both ESC 6 Box and SS2/ESC N, what PED draws frames with) |
every board |
rtl/cdc_byte.v |
one-byte clock crossing, full valid/ready handshake | every board |
rtl/term_banner.v |
power-on self-test message sender | every board |
rtl/term_banner_rom.v |
the message itself - GENERATED, see below | every board |
rtl/ps2_decoder.v |
scancode -> byte, VT100 marker scheme: modifiers, caps, ctrl; arrows/HOME emit sequence markers | Nexys and MiSTer |
rtl/ps2_decoder_tdv.v |
sibling of the above for the TDV2200 table - same logic, different ps2_ascii_table_* instance |
Nexys (default) |
rtl/key_vt100.v |
expands VT100 markers to ESC [ x toward the UART, with the FIFO that makes 3-5 bytes per keypress survive |
Nexys/MiSTer (via -DND120_TERMINAL_VT100) |
rtl/key_tdv2200.v |
expands TDV2200 markers to ESC [ nn _ (shift = n+1) toward the UART |
Nexys (default) |
rtl/ps2_ascii_table.v |
the VT100 scancode lookup tables | Nexys and MiSTer |
rtl/ps2_ascii_table_tdv.v |
the TDV2200 scancode lookup tables - bare C0 bytes for arrows/Home/Delete, ESC[nn_ markers for F-keys |
Nexys (default) |
rtl/ps2_keyboard.v |
PS/2 serial front end (11-bit framing, prefixes) + VT100 decoder | Nexys only, via -DND120_TERMINAL_VT100 |
rtl/ps2_keyboard_tdv.v |
same framing, TDV2200 decoder | Nexys only, default |
rtl/console_uart_rx.v / _tx.v |
the machine seam, 7/8 bits + optional parity | Nexys only |
VT100 and TDV2200 are two genuinely separate module pairs, never both
elaborated in the same build - selected by `ifdef ND120_TERMINAL_VT100
in terminal_top.v and each board's top level (Nexys: -VT100Terminal
build flag). Default is TDV2200 (type 93) since 31-AUG-2026: PED and LED are
built for the Tandberg keyboard's own key set, not VT100 CSI input - see
docs/SPEC-tdv2200.md. The MiSTer runs TDV2200 too: its TDV keyboard path
(ps2_decoder_tdv + key_tdv2200) was wired 02-SEP-2026, and
fpga/mister/rtl/nd120_console_mister.v warns not to re-add the VT100 define.
The keyboard is split in two on purpose. MiSTer's hps_io delivers scancodes
that Linux has already framed, and the MEGA65 has a matrix behind a CPLD -
neither has a serial line to decode. Keeping the part with the actual logic in
it (ps2_decoder.v) separate from the framing means a keyboard bug found on
one board is fixed on all of them.
term_banner_rom.v is generated by font/make_banner.py. Edit the text there
and re-run it; do not edit the ROM. The generator exists so that the message
and its length can never disagree - and in fact no length constant survives at
all, because the ROM returns 0x00 past the end and the sender stops there.
Layout¶
| Path | What |
|---|---|
rtl/ |
the terminal RTL (Verilog, not VHDL - see the plan for why) |
sim/ |
testbenches, per the repo convention; every one gets registered in Verilog/tests/run_all_tests.sh with a strict pass pattern |
font/ |
character-generator ROM data + the script that builds it |
docs/ |
the plan and the spec notes |
History: the VT100 decision of 30-AUG-2026 (reversed 31-AUG-2026)¶
TDV2200 has been the default since 31-AUG-2026 (see the module table above). The text below is the 30-AUG reasoning, kept as a record; the "80x24" and "TDV build REMOVED" statements are no longer true of the default build.
Decision (Ronny): plain VT100, matching SINTRAN terminal type 6
(@SET-TERMINAL-TYPE <n> 6), not TDV2200. RetroTerm's own class hierarchy
settled the question: its TDV2200 emulator derives from the same ECMA-48 core
its VT100 is, adding ND private CSI finals, rectangles, work areas and nine
character sets on top - so a TDV2200 is a VT100 plus a delta, and the delta is
not needed to give SINTRAN a working console. The earlier TDV-native C0 build
(FF=roll up, EM=erase page, DLE cursor addressing, 80x25) was REMOVED with the
30-AUG rewrite of terminal_ctrl.v; the geometry is now the VT100's 80x24
and the C0 codes have their ASCII meanings. If TDV2200 is ever wanted it is a
delta on this parser (the header of terminal_ctrl.v says exactly where).
What the controller implements - the list, with the reasons, is the header of
rtl/terminal_ctrl.v:
- CSI: CUP/HVP, CUU/CUD/CUF/CUB, CHA, VPA, ED 0/1/2, EL 0/1/2, SGR (reverse, bold, underline, blink), DECSTBM scroll regions, DECLL lamps, modes DECSCNM / DECOM / DECAWM / DECTCEM / LNM.
- ESC: IND, NEL, RI, DECSC/DECRC, RIS, charset designation.
- The VT100 last-column flag (autowrap done right).
- Character sets: G0/G1 with DEC Special Graphics (
ESC ( 0/ESC ) 0- SO/SI) - font page 2, generated by
font/make_font.py, so line-drawn boxes render. Page 0 US, page 1 Norwegian NS 4551-1 (thenationalpin).
- SO/SI) - font page 2, generated by
- NOT implemented, deliberately: anything needing a transmit path (DA/DSR/CPR reports - SINTRAN is configured, it does not probe), VT102 IL/DL/ICH/DCH, 132 columns, double-size lines. Unknown sequences are swallowed, never printed.
Scrolling: full-screen scroll is still the top_row ring; a DECSTBM region
scroll is a real row copy through the character RAM's second port (~96 us
worst case at 40 MHz), which is why byte_fifo.v now sits in front of the
controller - one 115200 byte time is only 87 us and the UART does not wait.
Keyboard: the full DEC key set (30-AUG-2026). Every special key leaves
ps2_ascii_table.v as a sequence marker (bit 7 flag, 2 family bits, 5
payload bits) and key_vt100.v expands it on the way to the console UART:
arrows ESC[A-D; Home/Insert/Delete/End/PgUp/PgDn as the DEC editing six
ESC[1~..6~ (Home=FIND, End=SELECT - ESC[H was xterm's Home, not DEC's,
and is gone); F1-F4 as DEC PF1-PF4 ESC O P/Q/R/S; F5-F12 as the VT220
codes ESC[15~..24~. Modifiers per the standard: Shift/Ctrl on a special
key send the base sequence (no modifier encoding existed in this era), Alt
sends nothing. Sequences up to five bytes ride a 16-deep FIFO. Full
analysis + the VTM-side evidence: docs/SPEC-vt100-keys.md.
Wired on the Nexys top; MiSTer build 1 has no UART, its local-echo path
drops the markers harmlessly (noted in nd120_console_mister.v).
Verilator lint clean (make lint in sim/, using the SAME suppression
set as Verilog/sim and Verilog/runSim - deviating would make "clean" mean
something different here than everywhere else), and zero inferred latches
with -Wno-LATCH removed, checked separately because the repo suppresses that
warning by default and latches are the one thing this project cannot have.
All nine testbenches pass (cd Terminals/sim && make, iverilog under
WSL) and are registered in Verilog/tests/run_all_tests.sh.
sim/terminal_ctrl_tb.v was rewritten with the VT100 controller: it checks
the parser against sequences split across gaps, CAN aborts, the last-column
flag, region scrolls through the copy engine, SGR attribute bits landing in
the cells, charset shifts, DECOM addressing and RIS - seventeen sections.
Synthesized and timing-clean on the Nexys 4 DDR (30-AUG-2026): first
Vivado run failed the 1080p pixel clock (139.7 MHz) on the cursor-move
paths; two rounds of fixes (defer the moves into the apply state, then
latch their operands before use - commits 191bdee, e26f44b) closed it at
WNS +0.211 ns, all endpoints met, better margin than the build before the
rewrite. Utilization delta vs the old TDV terminal: +695 LUTs, +494 regs,
+2.5 BRAM. Flashed to the board 30-AUG 06:22 as part of the CACHEFIX
session's cache build 8; first hardware exercise (TPE console traffic) ran
right after - what a human sees on the screen is still unreported.
The scancode table is verified against the standard, not against fingers.
Every set-2 code in ps2_ascii_table.v was cross-checked 30-AUG-2026 against
two independent published references - the OSDev wiki set-2 table and Vetra
Systems' translation table - and both agree with every entry, F7=0x83
included. Scan code set 2 is a fixed standard, so the codes are settled by
documentation. What still needs checking on real hardware:
that the Nexys USB-HID bridge behaves as the standard says, and the
Norwegian layout positions, which come from RetroTerm's KBD-ND-246 grid
rather than from any scancode standard - parentheses, colon, comma, full
stop and slash are the ones SINTRAN needs first.
Plan: docs/PLAN-vt100-terminal-core.md.
The seam¶
The ND-120 console UART is IO_UART_42 inside the board;
ND120_TOP.v:890 brings .TXD(uartTx) out, and boards wire that to
cpu_txd / RXD. The terminal attaches with two byte FIFOs:
- TX: ideally tap the console byte before serialization, so baud and
framing never reach the screen. Note the framing is not a constant: the
console chip is a programmable
SC2661_UART(IO_UART_42.v:179) and this repo's ownconsole.ps1:17says the OPCOM console is "7E1 in some configurations", board checks 8N1. The "7E2" in the MEGA65 plan is not confirmed anywhere. On the Nexys test rig we instead deserializecpu_txd, which needs no shared-RTL change and proves the real framing. - RX: keyboard bytes are injected as if they had been received.
Identical on every board. Only the ends differ:
| Nexys 4 DDR (Artix-7) | MiSTer (Cyclone V) | MEGA65 (Artix-7) | |
|---|---|---|---|
| Keys in | onboard USB host hands the FPGA plain PS/2 on F4/B2 - a ~50-line receiver, no USB stack | hps_io gives ps2_key[10:0] - USB keyboard handled by Linux |
vendored mega65-core keyboard files (LGPLv3, own directory) |
| Video out | 12-bit VGA connector straight off the pins, 40 MHz pixel clock free from the existing MMCM | CLK_VIDEO + CE_PIXEL + VGA_* into the framework video_mixer; HDMI free |
VGA timing straight to the board's VDAC |
| Console switch | build define ND120_CONSOLE_VGA; the serial console stays live in parallel |
an OSD status bit picks screen vs framework UART | build-time or a config switch |
Where the specification comes from¶
RetroTerm (its own repository) is Ronny's own MIT-licensed terminal
emulator and it already implements VT100, VT52, ECMA-48, TDV and Tektronix.
That makes it a clean, unrestricted specification source - unlike PDP2011's
VT100, whose files are non-commercial-only and cannot be touched (details in
docs/PLAN-vt100-terminal-core.md, "The licensing finding"). Verified paths, 27-AUG-2026:
src/RetroTerm.Core/Terminal/Emulators/-TerminalEmulatorBase.cs(360 KB, the real behaviour),VT100Emulator.cs,Vt52Emulator.cs,Ecma48Emulator.cs,TDV/,Tektronix/spec/TDV2200/Term and keyboard info/TDV-Keys.md- implementation guide (control sequences, screen model, the TDV-specific keys)spec/TDV2200/OCR/*.md- the OCR'ed Tandberg/ND manuals. These are the primary source; TDV-Keys.md is a derived guide that hedges in places ("firmware-specific", "may vary"), so anything load-bearing gets checked against the manual.spec/TDV2200/DOC TDV2200/- keyboard documentation + a ROM disassemblyspec/TDV2200/Export2215Code/- Ghidra export of TDV2215 firmware
What that source already tells us (read 27-AUG-2026)¶
Read the ordering warning first. Two of the facts below came from
TDV-Keys.md, which is a derived implementation guide, and one of them was
wrong. The RetroTerm session (retroterm-09, which owns that code) corrected
it the same day. Treat the OCR text and the derived guides as claims until
they agree with the ROM disassembly or with RetroTerm's actual behaviour -
that repo has had three of its own documents turn out wrong, two of which
would have hidden a real defect.
Corrected by retroterm-09, 27-AUG-2026 - the important one¶
Overtaken 30-AUG-2026: the paragraphs below are the case for a TDV layer, kept as history. The decision went the other way - the terminal is a plain VT100 and SINTRAN's line is set to terminal type 6, where VT100 IS the right grammar. The TDV facts stay true; they just describe type 53, which we no longer emulate.
TDV 2200 is NOT "ANSI with extras". It is a genuinely different grammar,
and a plain VT100 pointed at SINTRAN will not do. Keys come back as TDV grid
positions - e.g. ESC [ 4 6 _, note the underscore terminator, which is
not in the ANSI final-byte set at all. RetroTerm resolves every key through a
TDV key registry with deliberately no VT100 fallback: a key with no TDV
equivalent returns null rather than degrading to something ANSI.
So the plan stands - build the VT100/ECMA-48 core first - but the TDV layer is not a small delta on top of it. Budget it as its own parser and its own key registry.
(What TDV-Keys.md ยง1 says - "conforms largely to ISO 6429/VT100 with
proprietary enhancements" - is about the TDV 1200, and is a derived claim.
Do not carry it over to the 2200.)
Where to read the keyboard truth (paths from retroterm-09)¶
Do not start from TerminalEmulatorBase.cs - 360 KB, and it is the
ECMA-48 core, not the TDV story. For keys:
src\RetroTerm.Core\Terminal\Input\KeyboardMapper.cs-TDV2200KeyboardMapper, keyed by VK code. This is the one the factory actually builds.src\RetroTerm.Core\Terminal\Emulators\TDV\TDVKeyboardMapper.cs- keyed by key name. A DIFFERENT class. Conflating the two has already cost time over there; do not repeat it.- the
TDV2200KeyRegistryboth resolve through.
From TDV-Keys.md - useful, but still claims¶
- Screen model: 80x25 cells (corrected 27-AUG by retroterm-09 from RetroTerm's EmulatorFactory - the TDV-Keys.md figure of 80x24 is the TDV 1200); per-cell attributes are character code, bold/reverse/underline, a protection bit (SPA/EPA) and a G0-G3 character-set selector. Double height/width lines take two lines of memory.
- Mode switching exists:
CSI > 0 canswers with firmware ID 115 (the TDV signature);CSI ? 40 h/lselects TDV 2115 compatibility. - The unique keys are the hard part (Ronny, 27-AUG). HELP, DO, CANCEL,
COMMAND, FIND, SELECT, REMOVE, INSERT HERE, PREV/NEXT, KEY 21-32, plus soft
keys reprogrammable over DCS. The sequences listed in that guide are in ANSI
ESC [ n ~form, which the correction above says is not how a 2200 actually reports keys - so read them as "these functions exist", not as the wire format. Mapping them onto a PS/2 or a MEGA65 keyboard is a design problem either way - stretch goal, not the first build.
Still coming from retroterm-09¶
Asked 27-AUG-2026; it is mid-session with Ronny on a Sixel test and will send: the must-have / nice-to-have / never-seen split of what SINTRAN III actually exercises, the geometry and attribute answer, and the traps list. Folded in here when it lands.
Scope - where the stages landed (30-AUG-2026)¶
- Stage A (TDV C0 glass TTY) - built 27-AUG, removed 30-AUG when the VT100 decision landed. Its history and the TDV C0 facts live on in docs/SPEC-tdv2200.md.
- Stage B (VT100 proper) - BUILT, and it is the terminal now. See "The terminal is a VT100" above.
- Stage C (TDV 2200) - not planned. If it ever returns it is a delta on
the VT100 parser;
terminal_ctrl.v's header says where the ND finals and private modes would hook in. - Open: eyes on the real screen (rendering confirmed good by Ronny 30-AUG) and the Norwegian layout positions at the real keyboard - the set-2 scancodes themselves are settled against published references.