Skip to content

VTM key codes - the lookup table

What this is. What VTINBT hands back for each key, measured on D100 on 2026-08-25 with SINTRAN/XMSG/SINTRAN-CHAT/KEYPROB.PLNC. No ND document describes any of this - VTM's manual number is literally "Internal" (ND-20034-1-EN:1248) - so this page is the only record there is.

Read VTM-API-REFERENCE.md first for how to call VTINBT. Two traps there will make it look as though VTM decodes nothing: type it as three values IN and one OUT, and pass VARIABLES rather than literals.


1. How this was measured, so it can be repeated

1. Set the line's terminal type      @SET-TERMINAL-TYPE ,6      (or ,53 for a TDV)
2. Open RetroTerm in the matching emulation - VT100 or TDV2200
3. @KEYPROB
4. Send each key FOLLOWED BY a full stop as a marker
5. Q to stop; the codes print on the way out, in order

THE MARKER IS WHAT MAKES IT TRUSTWORTHY. A full stop is code 46, so a correct run reads key 46 key 46 .... If the count is wrong, the reading is wrong - and it caught a real fault here, see ESC[42_ below. Send at most five keys per run and check the count; a twenty-key sweep drifted silently and produced a table that was wrong from the middle onwards.

Anchor every sweep on a key you already know. ESC[46_ is HJELP and must give 191; ESC[30_ is ANGRE and must give 216. If an anchor lands in the wrong place, discard the run.


2. DEC VT100 - terminal type 6

Cursor and editing keys

Key what the terminal sends VTM code seen
cursor UP ESC [ A (ANSI normal mode) 28 3 runs
cursor DOWN ESC [ B 11 2 runs
cursor RIGHT ESC [ C 24 1
cursor LEFT ESC [ D 8 1
FIND ESC [ 1 ~ 130 1
INSERT HERE ESC [ 2 ~ 133 1
REMOVE ESC [ 3 ~ 129 1
SELECT ESC [ 4 ~ 160 1
PREV SCREEN (page up) ESC [ 5 ~ 201 4 runs
NEXT SCREEN (page down) ESC [ 6 ~ 197 3 runs

THE KEY NAMES ABOVE ARE DEC'S, CHECKED AGAINST THE VT220 MANUAL (vt100.net, VT220 Programmer Reference, chapter 3, table 3-1). An earlier version of this page called them HOME, DELETE and END. Those are xterm's names, not DEC's, and the distinction is not pedantry - a VT220 keyboard has keys physically labelled FIND, SELECT and REMOVE, and anyone looking for HOME on one will not find it.

AND THESE ARE VT220 KEYS, NOT VT100 KEYS. The same manual says plainly: "In VT100 or VT52 modes the editing keys do not generate codes." A real DEC VT100 has no editing keypad at all and can never send any of these six sequences.

That makes the measurement above interesting rather than wrong: SINTRAN terminal type 6 is "DEC VT100 (80 columns)", and VTM decoded all six anyway. So VTM's type-6 table accepts sequences a genuine VT100 could not produce. Useful in practice - a modern emulator sends them and they work - but it means the type-6 entry is not a strict VT100. SINTRAN's own list has separate entries for the VT220 at types 131 and 132; whether those decode differently has NOT been tested.

Ordinary and control keys

Key byte sent VTM code note
any printable its ASCII the same parity already stripped - A is 65, not 193
TAB 9 9 passes through
RETURN 13 13 passes through
Ctrl-A 1 1 passes through
Ctrl-C 3 3 passes through - usable as a quit key
Ctrl-Z 26 26 passes through
Ctrl-H / Backspace 8 127 TRANSLATED, see below

Ctrl-H DOES NOT COLLIDE WITH LEFT ARROW, and that is worth knowing because it looks as though it must. Left arrow reports 8. Backspace also sends byte 8 - but VTM turns it into 127. So the two are distinguishable, and a program that treats 8 as "left arrow" is correct.

ALT IS NOT USABLE

ESC + a came back as two codes, 27 then 97 - VTM does not recognise it and the bytes fall straight through. Do not design around Alt. Whatever it is on the user's keyboard, VTM will not tell you about it.

The ESC O x family - what it actually is, and why it is NOT settled

ESC O is SS3, and in ANSI application mode it prefixes the auxiliary keypad keys (VT220 manual, table 3-3):

sequence key
ESC O P PF1
ESC O Q PF2
ESC O R PF3
ESC O S PF4

These are the numeric keypad's PF keys, NOT the function keys F1 to F4 - another label this page had wrong. On a VT220 the keys marked F1 to F5 are local (Hold Screen, Print Screen, Set-Up, Data/Talk, Break) and send nothing at all.

ESC O also prefixes the CURSOR keys when the terminal is in application mode - SS3 A/B/C/D rather than CSI A/B/C/D. Since VTM sends ESC = (keypad application mode) during start-up, which of the two forms a given terminal actually sends is a live question and not one this page can answer yet.

Both sequences really are emitted by the terminal. RetroTerm maps VK_F1..VK_F4 to ESC O P..ESC O S unconditionally, in both its VT100 and VT220 mappers - so what reached VTM was well formed, and the asymmetry is in VTM's decoder, not in what was sent. DECKPAM does not enter into it either: the application-keypad mapping covers only ESC O p..ESC O y and ESC O j/k/l/m/n/o, so ESC = changed nothing about the uppercase forms.

The naming caveat worth carrying: on real DEC hardware ESC O P..S are the KEYPAD's PF1..PF4, not the top-row function keys. Modern emulators map the F1..F4 keys onto them by convention, so "F1" on a PC keyboard and "PF1" on a VT100 keypad are the same four bytes. If VTM tells them apart it does so by context, not by the sequence.

Measured, and inconsistent: ESC O P gave 29, but ESC O Q returned TWO codes in the same run. One clean per-key run is needed before any of it is written down. Left here as a warning, not as data.


3. Tandberg TDV 2200/9 ND-NOTIS - terminal type 53

Every special key on this terminal sends ESC [ <n> _, so the whole keyboard can be swept by varying n rather than by finding a name for each key.

n VTM code key, where known
30 216 ANGRE (undo)
31 209
32 139
33 152
34 194
35 146
36 206
37 134
38 25
39 214
40 9
41 203
42 174 FUNK (G51, function-shift) - A PREFIX, see below
43 159 SHIFT-FUNK
44 161 SKRIV (G52, "write"/print)
45 162 SHIFT-SKRIV
46 191 HJELP (G53, help)
47 165 SHIFT-HJELP
48 163 SLUTT (G54, "end")
49 164 SHIFT-SLUTT
50 132 F1
51 193 SHIFT-F1
52 140 F2
55 149 F3
58 171 F4
60 217 F5
62 204 F6
64 220 F7
66 221 F8

A KEY HAS TWO NAMES: ITS LEGEND AND ITS GRID POSITION

This confused me for half an hour and it is not a defect. HJELP and G53 send the same ESC[46_ and give the same 191 because they are the same key: HJELP is the legend printed on it, G53 is where it sits on the keyboard grid. Same for F1 and F51, F2 and F52, F3 and F53, F4 and F54 - one key, two names, identical bytes.

And that is why the F-names stop at F54. The F5..F8 legends sit on grid row E, not row F, so they are E51..E54. There is no F55. The table is not half filled in; the F row simply has four keys on it.

Confirmed by the RetroTerm session on 2026-08-25 from its own key registry - Reg("F51", "F1") and Reg("E51", "F5"). Do not report the duplication as a bug; it was already reported and it is correct behaviour. What WAS wrong was RetroTerm's parameter text saying "F1..F52", which mixes the two naming systems into a range that matches neither. That has been corrected.

SHIFT IS IN THE SEQUENCE NUMBER, NOT A MODIFIER

F1 is ESC[50_ and SHIFT-F1 is ESC[51_ - a different key as far as the wire is concerned, and a different code (132 against 193). So the sweep above already covers the shifted keys; they are simply other values of n. There is no modifier bit to combine with anything.

The F-key numbers are not evenly spaced - 50, 52, 55, 58, 60, 62, 64, 66 - and the codes they produce are not ordered at all (132, 140, 149, 171, 217, 204, 220, 221). It is a lookup, not a formula. Do not try to compute one.

ESC[42_ SWALLOWS THE FOLLOWING BYTE - AND THAT IS CORRECT

MEASURED twice, on purpose. Sent alone with one marker after it, the marker disappeared entirely. Sent with THREE markers, only TWO came back:

ESC[42_ . . .   ->   174, 46, 46

It is the FUNK key - grid G51, legend FUNK, short for funksjon. On a Tandberg keyboard that is a function-shift: you hold FUNK and press another key, and the pair selects a function. It is a PREFIX and produces nothing on its own.

So VTM is doing exactly the right thing - it swallows the next byte because it is waiting for the key FUNK modifies. This is not a defect in VTM and not a defect in the terminal. It is, however, a real hazard for a program that reads keys one at a time: press FUNK by accident and the NEXT keystroke vanishes.

Identified from RetroTerm's own key registry, 2026-08-25. It is also what silently corrupted a twenty-key sweep here, which is why the method now uses a marker after every key and checks the count.


3b. THE EMULATOR AND THE HOST AGREE - checked end to end

The same key sent two different ways gives the same code. MEASURED 2026-08-25: each key sent once through RetroTerm's own sendkey (which looks the sequence up in its key registry) and once as raw bytes with sendraw:

key via sendkey via sendraw agree?
HJELP 191 ESC[46_ -> 191 yes
ANGRE 216 ESC[30_ -> 216 yes
F3 149 ESC[55_ -> 149 yes

That is worth doing because the two paths prove different things. sendraw only tests VTM's decoder - it says nothing about whether the emulator would ever produce those bytes. sendkey tests the emulator's key registry as well, end to end from a keyboard legend to a code inside a PLANC program on the ND.

They agree, which is the first end-to-end check of that registry against a real host. If the two ever disagree for a key, the fault is on the emulator side and worth reporting there.

4. THE CODES ARE NOT THE SAME ACROSS TERMINALS - do not assume they are

It is tempting to think VTM normalises every terminal onto one logical key set. That is NOT established, and the evidence so far is against it. None of the 29 TDV codes measured is 201, the VT100's PAGE UP. The sweeps do not overlap in an obvious way at all.

Two honest qualifications:

  • the TDV sweep covered n = 30..52 and a handful of F-keys, NOT the whole range, so a match could be sitting at an n nobody has tried;
  • a TDV has keys a VT100 does not have and the reverse, so a complete mapping cannot exist anyway.

Until somebody sweeps both terminals exhaustively, treat the codes as PER TERMINAL TYPE. A program that must work on both should read its key bindings from a table chosen by terminal type, not hard-code one number per logical key.

And VTM only decodes the terminal it has been told it is talking to. The VT100 ESC [ 5 ~ sent to a line set to type 53 came back as four raw bytes - 27, 91, 53, 126 - undecoded. Two consequences:

  • never treat a stray 27 as a key; it may be the head of a sequence VTM did not recognise and the rest is arriving as separate calls;
  • a bare ESC is not a dependable quit key. On a VT100 line, pressing ESC alone did not produce 27 at all. CHATUI bound its exit to it, became impossible to leave, and its terminal had to be freed with STOP-TERMINAL from another session. Use a typed command such as /exit.

5. Two practical notes

The rubbish at start-up is VTM's terminal-type negotiation. A program that takes the keyboard straight after blankscreen reads about eight junk bytes - 63 63 128 103 0 29 63 63 - and they land in whatever it thinks is its input. On a line whose type was already set with SET-TERMINAL-TYPE, none of them appear. Either set the type in advance or drain the input before taking the keyboard.

RetroTerm keeps the two emulations properly separate, so a VT100 session cannot accidentally send TDV keys - terminal_sendkey refuses outright:

SENDKEY needs a TDV emulator (current: VT100Emulator) - use SENDRAW for other terminals

That was checked deliberately, because if the emulator had been sending TDV sequences on a VT100 line every VT100 figure above would have been meaningless.


See also