• Please review our updated Terms and Rules here

DEC's SSU (or TD/SMP), reverse-engineered

paleotechnica

Member
Joined
Oct 20, 2025
Messages
29
As a related side-project to the VT420 emulator, I started digging into the virtually undocumented DEC SSU protocol. The patent is somewhat useful for understanding the overall structure, but it was nowhere near enough to build an implementation.

I ended up using a real VT420, some ROM disassembly, and a lot of experimentation to basically build a working implementation from scratch. :) Along the way I found a few behaviours that aren’t mentioned in the patent at all.

So why would you bother with SSU instead of a raw serial connection? Mainly because the early VT terminals don’t have hardware flow control and rely entirely on XON/XOFF. SSU uses transmission credits, which makes it much harder for the sender to overrun the receiver’s buffers. It also frees up the Comm2 port for a printer while still allowing two sessions, if that sort of thing appeals to you.

SSU also gives you “session framing.” You can open and close sessions, and the terminal can even prompt you for a session name - which hypothetically lets you provide something like an SSH hostname without writing a custom “jump box” layer.

Here are the results from my experiments. The low-level details are in the GitHub repo at: https://github.com/mmastrac/blaze/blob/main/architecture/SSU.md

The SSU server implementation is basically ready for people to play with. Given how little data it pushes around, it should run fine on a very low-end Raspberry Pi. If anyone wants to try it out in their own setups, I’d love to get extra eyes on it and see how it behaves on different hardware. I'll probably need to walk the first few people through the setup until I've ironed out some of the kinks.



Flow Control

SSU uses a simple flow control mechanism based on credits. Each side is allocated a number of credits to use for sending data, and the strategy for dispensing them is left to the implementation. Generally, the credits will reflect the available buffer space for the session.

By default, each side of the session has no credits and must be granted credits before sending data.

When a side runs out of credits on a given channel, its credits have been explicitly zeroed/reset, or if it has never been granted credits, it must not send any more data until it explicitly receives more credits.

Each side should preemptively add more credits as it detects the peer is running low. If the remote side continues sending data after running out of credits, the local side can send a `ZERO` message to force it to zero out its credit balance until there's enough buffer space to receive data from it again.

The entire balance of credit does not need to be dispensed at once. The sender can dispense different levels of credits as it sees fit, for example to reflect different buffer watermarks.

The entire SSU channel between peers may be controlled with XON/XOFF in cases where one side needs time to catch up on control message processing or otherwise needs to pause the total flow of data. This pauses ALL data processing for ALL sessions.

Handshake

The handshake can be initiated from either side and is as follows. Certain `REPORT` responses have been omitted for brevity:
1. The local side sends a `PROBE` message (`!@AB`) to indicate that it is in the disabled state.
2. The remote side responds with `!AAB` (enabled, no sessions) or `!BAB` (enabled, with existing sessions).
3. The local side sends a `REPORT` message confirming activation: `=!a@`.
4. If the remote side sent `!BAB`, the local side should send `;` to request a session restore.
- The remote side sends a `RESTORE_START` message.
- For each open session, the remote side sends a `OPEN_SESSION` message.
- The remote side sends a `RESTORE_END` message.
5. If the remote side did not send `!BAB`, it may still send `OPEN_SESSION` messages to open sessions.
6. If the remote side did not open sessions, the local side may also request named or unnamed sessions be opened via `OPEN_SESSION` messages.
7. Either side may send a `SELECT_SESSION` message and start sending data once it receives an `ADD_CREDITS` message granting credits.

Message Format

The stream is in "data mode" by default and all messages are directed towards the selected session. To send a command to the remote side, send the intro byte
(`0x14`, a.k.a. `DC4`) followed by the opcode, parameters, and the term byte (`0x1C`).

If a raw `0x14` is supposed to be sent, it is encoded as `0x14` `T` instead. XON and XOFF to be sent to a session are similarily encoded as `0x14` `Q` and `0x14`
`S` respectively.

Parameters are encoded with an offset of 0x40, meaning that each character is encoded as a six-bit value, with zero being `@`, one being `A`, etc.
Session IDs are encoded as 1-based indices, meaning that `A` is 1 and `B` is 2.

In certain cases, such as `RESET`, a session ID of 0 is used to indicate all sessions.

The following opcodes are supported. Unrecognized opcodes should be ignored:

OpcodeASCIIOpcode NameDescription
!0x21PROBEProbe/Enable
"0x22OPEN_SESSIONOpen session
#0x23SELECT_SESSIONSelect session
............
*0x2ARESETReset
+0x2BADD_CREDITSAdd credits
,0x2CUNUSED(unused opcode)
-0x2DVERIFY_CREDITSVerify credits
.0x2ECLOSE_SESSIONClose session
/0x2FDISABLEDisable
00x30ZERO_CREDITSZero credits
............
:0x3ASEND_BREAKSend break
;0x3BREQUEST_RESTORERequest restore
<0x3CRESTORERestore
=0x3DREPORTReport/Ack
>0x3ERESTORE_ENDRestore end
?0x3FQUERY_SESSIONQuery session
 
That's very cool, thanks for doing this and sharing your findings!

FWIW, I have a terminal server that works quite well with VT330 multi-sessions, when they are enabled in the terminal setup, and also allowed on the server end (with "set port multisession" or something like that), and does not understand VT420 multi-session at all, and I see garbage on the screen, which look like repeated !@AB (or something similar -- I'm away from my VTs to exactly check that). That indicates that there existed at least two versions of the protocol that were not quite backward compatible.
 
FWIW, I have a terminal server that works quite well with VT330 multi-sessions, when they are enabled in the terminal setup, and also allowed on the server end (with "set port multisession" or something like that), and does not understand VT420 multi-session at all, and I see garbage on the screen, which look like repeated !@AB (or something similar -- I'm away from my VTs to exactly check that). That indicates that there existed at least two versions of the protocol that were not quite backward compatible.
Interesting. That's the same probe message from the 420. I believe `!@AB` is protocol version 1, but if you see something like `!@@B` that might be a protocol version 0 that I haven't seen before.

The VT420 uses Ctrl+T as an introducer character for SSU messages, but it's possible the VT330 has another way of doing it. I have some VT3xx hardware here that I haven't played around with yet.

Which terminal server are you using? It might be useful for me to compare and possibly add support.
 
Which terminal server are you using? It might be useful for me to compare and possibly add support.
I am away from my equipment at the moment (until next week), so I cannot tell exactly what it is, but IIRC it's Equinox ELS-16 (not 100% sure)...
 
Interesting. That's the same probe message from the 420. I believe `!@AB` is protocol version 1, but if you see something like `!@@B` that might be a protocol version 0 that I haven't seen before.

The VT420 uses Ctrl+T as an introducer character for SSU messages, but it's possible the VT330 has another way of doing it. I have some VT3xx hardware here that I haven't played around with yet.

Which terminal server are you using? It might be useful for me to compare and possibly add support.
Between a real VT420 ("Sessions on Comm1") and a DECserver 700 ("set port multisessions enabled")* running DNAS V3.6, I can establish sessions to multiple hosts and switch between them with F4.

I can dump what's on the wire if needed, but as an curmudgeon I don't do Ruby, crates, etc. So I can't use your code.

BTW, it is definitely "Session Support Utility (SSU)", not "Session Setup Utility (SSU)". DEC AV-Q1VNA-TE says "Digital Equipment Corporation is pleased to provide the session support utility SSU Version 2.0 for OpenVMS[TM] Systems."

* Note that if you do "set port multisessions enabled" on the DS700, you have to issue the command after you have a single session established on the port - this characteristic is "for the duration of the current DECserver local login" and not "persists until the DECserver is rebooted". In production use, you'd use 'define port..." or "change port..." to save the configuration, which will make it persist across reboots (and be on by default when the port gets a new local login on the DECserver).
 
Last edited:
I can dump what's on the wire if needed, but as an curmudgeon I don't do Ruby, crates, etc. So I can't use your code.
Wiredumps would be great. The parts I could use some extra information around would be session open/restoration, and error conditions (ie: if you can forcibly close a session somehow).

At some point this code might be able to run on a Raspberry Pi Pico w/o any additional hardware outside of RS232 level converters, which I think will be the most useful way to run it - wire it up to the DEC port and then two standard RS232 serial ports downstream with hardware flow control.

BTW, it is definitely "Session Support Utility (SSU)", not "Session Setup Utility (SSU)". DEC AV-Q1VNA-TE says "Digital Equipment Corporation is pleased to provide the session support utility SSU Version 2.0 for OpenVMS[TM] Systems."
Thanks! I'll update the docs to match.
 
Wiredumps would be great. The parts I could use some extra information around would be session open/restoration, and error conditions (ie: if you can forcibly close a session somehow).
I'll do those. And I can definitely tear down the sessions by forcibly logging the port out from a telnet session to the terminal server remote console. If you don't see them in a few days, DM and remind me.
At some point this code might be able to run on a Raspberry Pi Pico w/o any additional hardware outside of RS232 level converters, which I think will be the most useful way to run it - wire it up to the DEC port and then two standard RS232 serial ports downstream with hardware flow control.
Raspberry Pi Model 3B or better with Raspberry Pi OS and a USB/serial converter, maybe?
Thanks! I'll update the docs to match.
Wonderful. Speaking in general terms, and definitely not pointing fingers at anyone (except possibly myself) we've completely lost so much history - hardware / software / documentation. Projects like yours don't just preserve the history we have, but add additional information that wasn't known (at least outside of DEC) and have put it in a public repository where people will be able to learn from it for years to come.

I'm currently engaged in a massive documentation scanning project (about 15,000 pages), much of which simply doesn't exist anywhere except in the manuals on my bookshelves. Preserving that type of knowledge for the future is vital.
 
Back
Top