Altair 8800 computer was equipped with serial board called 88-SIO, or 88-2SIO. It was a device which allowed connecting other devices using RS-232 interface. From one side it was attached to CPU on at least two ports (most commonly
0x11 for terminal). The other side ended with one physical port allowing to connect one device. A variant called 88-2SIO allowed to connect two devices at the same time.
Usually, attached devices were:
- serial terminal
- line printer
- paper tape reader/punch
The following image shows MITS 88-2SIO board.
The plugin emulates only basic functionality of the board (e.g. it does not emulate transfer rate). It has the following features:
- allows to connect one device
- CPU ports are configurable
- supports input/output interrupts
- has a GUI
GUI can be seen here:
The window shows attached device, control channel and data buffer.
|1||Attached device name|
|2||Control channel status. Control channel is used to retrieve 88-sio status, or enable/disable interrupts. The displayed value shows the status. For details see the 88-sio manual.|
|3||88-sio has internal buffer used for caching one byte coming from the connected device. If the CPU is not fast enough to read it, the data can be overwritten by new data coming from the device. However, the buffer is not used when sending data to the connected device from CPU. Thus writing data from CPU won’t clear data coming from device.|
|4||Clear internal data buffer|
88-sio plugin has a separate settings window with three sections, described below.
The 88-sio plugin support various behaviors of the data transfer, which in reality was the behavior of connected devices and not 88-sio. The reason for supporting these features here is to allow some versatility, regardless of which device is connected. These general settings can be seen in the Settings window:
|1||TTY/ANSI mode. On TTY mode, clears input bit 8 (performs |
|2||Converts input data to upper-case (the byte coming from device).|
|3||Maps input/output data Backspace or Delete character to some other character. Possibilities are: Backspace, Delete, Unchanged.|
MITS 88-SIO board in emuStudio is attached to CPU through several ports. In order to support various Altair8800 configurations and thus using wider variety of original software, the device control and data channels are connected to multiple CPU ports. This makes effectively an impression as if there existed multiple 88-sio cards connected to the same device.
By default, used CPU port allocation is:
- Control channel (or “status port”):
0x18(most commonly used:
- Data channel (or “data port”):
0x19(most commonly used:
A side note: there existed various additional boards which supported RS-232 communication besides 88-SIO (or 88-2SIO). These boards were compatible with S100 system board, thus they were usable for original Altair8800 computers as well as its clones. For example: CompuPro Support Board (status port
0x5C, data port
0x5D), CompuPro Interfacer (manual here, consuming 8 CPU ports), Cromemco TU-ART board, IMS C00480 4-line board, IMS I/O board, IMSAI SIO Board, Intersystems 6-SIO, etc.
But back to 88-sio. The port numbers allocation can be changed in the Settings window:
|1||Attach/detach control channel (“status port”) to/from CPU port|
|2||Reset control channel CPU ports to default ones ( |
|3||Attach/detach data channel (“data port”) to/from CPU port|
|4||Reset data channel CPU ports to default ones ( |
88-sio supports input and output interrupts. If enabled, input interrupt is signalled to CPU when a data becomes available from the connected device. An output interrupt is signalled when data is sent to device. Interrupts support can be generally enabled or disabled, along with interrupt vector configuration in Settings window:
|1||Enable/disable interrupt support and set interrupt vectors. If interrupts are disabled, they cannot be enabled in software (see “Port 1” section below).|
|2||Reset to default. Interrupts will be supported and interrupt vector is set to 7 (equivalent to calling |
The following table shows all the possible settings of MITS 88-SIO plugin:
|Name||Default value||Valid values||Description|
| || ||> 0 and < 256; X range from 0 upwards||CPU-ports mapped to status port of 88-sio|
| || ||> 0 and < 256; X range from 0 upwards||CPU-ports mapped to data port of 88-sio|
| || || ||Whether to clear 8th bit of the input written to 88-sio|
| || || ||Whether to clear 8th bit of the output, read from 88-sio|
| || || ||Whether to convert the input written to 88-sio into upper-case|
| || || ||Maps a “DEL” input key/character to given value. For example, if user presses “DEL” the 88-sio can map it as if user pressed “BACKSPACE”.|
| || || ||Maps a “BACKSPACE” input key/character to given value. For example, if user presses “BACKSPACE” the 88-sio can map it as if user pressed “DEL”.|
| || || ||Whether interrupts are supported in general. When disabled, they cannot be enabled in software.|
| || ||0-7||Set input interrupt vector. 88-sio will signal an interrupt to the CPU as RST instruction on the input (e.g. a key press) if input interrupts are enabled|
| || ||0-7||Set output interrupt vector. 88-sio will signal an interrupt to the CPU as RST instruction on the output (e.g. on displaying a char) if output interrupts are enabled|
In order to show something useful, let’s assume that a terminal LSI ADM-3A is attached to the board. Remember, the board only mediates the communication, it does not interpret any of the sent/received characters.
Whole communication between the board (and attached device) and CPU is controlled by programming the two ports: Status port and Data port. The following table shows the ports and how they are used.
|Control (port 1)|| ||Read board status||Used for enabling/disabling input/output interrupts.|
|Data (port 2)|| ||Read data||Write data|
Now, detailed description of the ports follow. Bits are ordered in a byte as follows:
D7 D6 D5 D4 D3 D2 D1 D0
D7 is the most significant bit, and
D0 the least significant bit.
Controls input/output interrupts enable.
D7 D6 D5 D4 D3 D2: unused bits
D1: Enable/disable output interrupts (0 - disable, 1 - enable)
D0: Enable/disable input interrupts (0 - disable, 1 - enable)
Interrupts (both input and output) are signalled to the CPU as
RST instruction with the interrupt vector value used from the 88-SIO plugin settings (by default,
RST 7 is signalled).
Input interrupt is triggered when a device connected to 88-SIO sends data to it, so CPU will be notified to read it. Output interrupt is triggered when CPU sends data to 88-SIO, which effectively calls CPU again.
Read status of the device.
D7: Output device ready. Always 0 in the emulator.
D6: Not used (always 0).
D5: Data available (for writing to the attached device). Always 0 in the emulator, meaning that no data is pending to be written. Data are written immediately after
D4: Data overflow. Value 1 means a new word of data has been received before the previous word was inputted to the accumulator. In emuStudio, this never happens.
D3: Framing error. Value 1 means that data bit has no valid stop bit. In emuStudio, this never happens.
D2: Parity error. Value 1 means that received parity does not agree with selected parity. In emuStudio, this never happens.
D1: Transmitter buffer empty. Value 1 means that the data word has been received from the attached device and it’s available for reading (from the Data port).
D0: Input device ready. Value 1 means that the CPU can write data to the SIO (that the board is ready). Always 1 in the emulator.
Write data to the attached device.
Read data from the attached device.
If the attached device sends asynchronously multiple data, the emulated board stores all in a buffer (queue) with unlimited capacity, so no data should be lost and can be read anytime.
In this section it will be shown a small “How to” program terminal using 88-SIO ports.
In emuStudio, it is enough to write data to Port 2, e.g.:
mvi a, 'H' out 11h mvi a, 'i' out 11h
For writing strings, it is more practical to have a procedure.
lxi h, text ; load address of 'text' label to HL call print ; print text hlt ; halt CPU text: db 'Hello, world!',0 ; Procedure for printing text to terminal. ; Input: pair HL must contain the address of the ASCIIZ string print: mov a, m ; load character from HL inx h ; increment HL cpi 0 ; is the character = 0? rz ; yes; quit out 11h ; otherwise; show it jmp print ; and repeat from the beginning
For reading a character, it is required to read the Port 1 until the character is not ready. Then we can read it from Port 2.
; Procedure will read a single character from terminal ; Input: none ; Output: register A will contain the character. getchar: in 10h ; read Port 1 ani 1 ; is data ready ? jz getchar ; not; try again in 11h ; yes; read it (into A register) ret
Now follows an example, which will read a whole line of characters into memory starting at address in
DE pair. The procedure will interpret some control keys, like: backspace and ENTER keys.
lxi h, text ; load address of 'text' label to HL xchg ; DE <-> HL call getline ; read line from the keyboard into DE lxi h, text ; load 'text' address again call print ; print the text on screen hlt ; halt CPU text: ds 30 ; here will be stored the read text ;Procedure for reading a text from keyboard. ;Input: DE = address, where the text should be put after reading ; C = is used internally getline: mvi c, 0 ; register C will be used as a counter of ; read characters next_char: in 10h ; read Port 1: status ani 1 ; is the char ready for reading? jz next_char ; not; try again in 11h ; yes; read it to A register ; now ENTER and Backspace will be interpreted cpi 13 ; ENTER? jz getline_ret ; yes; it means end of input cpi 8 ; Backspace ? jnz save_char ; if not; store the character ; Backspace interpretation mov a, c ; A <- number of read characters cpi 0 ; are we at the beginning? jz next_char ; yes; ignore the backspace dcx d ; not; decrement DE dcr c ; decrement count of read characters mvi a,8 ; "show" the backspace (terminal will ; interpret this by moving the cursor ; to the left by 1 char) out 11h mvi a, 32 ; "clear" the current character on screen ; by a space character (ASCII code 32) out 11h mvi a,8 ; and move the cursor back again out 11h jmp next_char ; jump to next char save_char: ; stores a character into memory at DE out 11h ; show the character in A register stax d ; store it at address DE inx d ; increment DE inr c ; increment number of read characters jmp next_char ; jump to next char getline_ret: ; end of input ; ENTER will be stored as CRLF mvi a,13 ; CR (Carriage Return) stax d ; store the char inx d ; increment DE mvi a, 10 ; LF (Line Feed) stax d ; store the char inx d ; increment DE mvi a, 0 ; char 0 (End-Of-Input) stax d ; store the char ret ; return
In this example, an interrupt is signalled when user presses a key on keyboard.
; Tests signalling interrupts on input mvi a, 1 ; 88-SIO: input interrupts enable out 0x10 ei ; enable CPU interrupts loop: jmp loop ; do this forever (or until...) ; interrupt handler org 0x38 ; assuming interrupt vector is set to 7 in 0x11 ; read char from 88-SIO (and ignore it) lxi h, key ; load address of 'key' label to HL call print ; print "key pressed" ret ; return from the interrupt key: db 'Key pressed!',10,13,0 ; Procedure for printing text to terminal. ; Input: pair HL must contain the address of the ASCIIZ string print: mov a, m ; load character from HL inx h ; increment HL cpi 0 ; is the character = 0? rz ; yes; quit out 11h ; otherwise; show it jmp print ; and repeat from the beginning