TI-990
The TI-990 was a series of 16-bit minicomputers sold by Texas Instruments (TI) in the 1970s and 1980s. The TI-990 was a replacement for TI's earlier minicomputer systems, the TI-960 and the TI-980. It had several unique features, and was easier to program than its predecessors. Among its core concepts was the ability to easily support multiprogramming using a software-switchable set of processor registers that allowed it to perform rapid context switches between programs.

TI later implemented the TI-990 in a single-chip implementation, the TMS9900, among the first 16-bit microprocessors. Intended for use in low-end models of the TI-990, it retained the 990's memory model and main memory register system. This design was ultimately much more widely used in the Texas Instruments TI-99/4A, where details of its minicomputer-style memory model presented significant disadvantages.

Features

Workspaces
On the TI-990, registers are stored in memory and are referred to through a hard register called the Workspace Pointer. The concept behind the workspace is that main memory was based on the new semiconductor RAM chips that TI had developed and ran at the same speed as the CPU. This meant that it didn't matter if the "registers" were real registers in the CPU or represented in memory. When the Workspace Pointer is loaded with a memory address, that address is the origin of the "registers."

There are three hard registers in the 990; the Workspace Pointer (WP), the Program Counter (PC) and the Status register (ST). A context switch entailed the saving and restoring of only the hard registers.

Extended operation
The TI-990 had a facility to allow extended operations through the use of plug in hardware. If the hardware is not present the CPU traps to allow software to perform the function. The operation code (XOP) allowed for 15 attached devices on a system. Although, device 15 is reserved, by convention, to be used as the systems call entry for user programs to request systems services.

Orthogonal instruction set
The TI-990 used a fairly orthogonal instruction set. The basic instruction formats allowed for one, two and three word instructions. The model 990/12 CPU allowed for a four word instruction with the extended mode operations.

Architectural details

General register addressing modes 
(R is a general register, 0 to 15.)
0. Register - the value is to or from a register: OPR R ; R contains operand
1. Indirect register - register is used as a memory address to read or write: OPR *R ; R contains address
2. Indexed: OPR @MEM(R); R contains index value, R0 is not used in indexing and allows direct memory addressing
3. Autoincrement: OPR *R+ ; R contains address of address, then increment R by the length of the operand type

Several registers had special usages that reserve their use, the register and their usages are:
R0 - shift counter, extended mode counter, floating point AC-0
R1 - floating point AC-1
R2 - floating point AC-2
R3 - floating point AC-3
R11 - XOP pointer (kernel mode), return linkage
R12 - CRU base address (kernel mode)
R13 - Saved Workspace pointer
R14 - Saved Program counter
R15 - Saved Status

TI-990 instructions 
The 990/4, 990/5, 990/9 instruction sets consisted of 69 instructions, the 990/10 had 72 instructions, the 990/10A had 77 instructions and the 990/12 had 144 instructions. The instructions are divided into types that have similar characteristics.

Type 1 instructions
The first part of the word specifies the operation to be performed, the remaining two parts provide information for locating the operands.
MOV (move word) 
MOVB (move byte) 
A (add word) 
AB (add byte) 
S (subtract word) 
SB (subtract byte) 
C (compare word) 
CB (compare byte) 
SZC (set zeros corresponding word) 
SZCB (set zeros corresponding byte) 
SOC (set ones corresponding word) 
SOCB (set ones corresponding byte)

Type 2 instructions
The first part of the word specifies the operation to be performed, the second part is a relative offset to where to go, for JMP instructions, or the relative offset for CRU bit addressing.
JMP (jump unconditionally)
JLT (jump if less than zero)
JLE (jump if less than or equal to zero)
JEQ (jump if zero)
JHE (jump if logically greater than or equal to zero)
JGT (jump if greater than zero)
JNE (jump if not equal zero)
JNC (jump if carry clear)
JOC (jump if carry set)
JNO (jump if overflow clear)
JOP (jump if odd parity - only relevant after byte operations)
JL (jump if logically less than zero)
JH (jump if logically greater than zero)
SBO (set CRU bit to one)
SBZ (set CRU bit to zero)
TB (test CRU bit)

Type 3 instructions
One part of the word specifies the operation, the second part provides the register, the third part provides information for locating the second operand.
COC (compare ones corresponding)
CZC (compare zeros corresponding)
XOR (exclusive or)
XOP (extended operation)

Type 4 instructions
The first part of the word specifies the operation to be performed, the second part is the bit width of the operation, the third part provides information for locating the second operand.
LDCR (load CRU)
STCR (store CRU)

Type 5 instructions
The first part of the word specifies the operation to be performed, the second part is the shift count, the third part specifies the register to shift.
SRA (shift right arithmetic)
SRL (shift right logical)
SLA (shift left arithmetic)
SRC (shift right circular)

Type 6 instructions
The first part specifies the operation to be performed, the second part provides information for locating the second operand.
BLWP (branch and load workspace pointer)
B (branch)
X (execute)
CLR (clear word)
NEG (twos complement negate)
INV (ones complement)
INC (increment)
INCT (increment by two)
DEC (decrement)
DECT (decrement by two)
BL (branch and link)
ABS (absolute value)
SWPB (swap bytes)
SETO (set word to ones)
LDS (long distance source, 990/10, 990/10A, 990/12)
LDD (long distance destination, 990/10, 990/10A, 990/12)
BIND (branch indirect, 990/10A, 990/12)
MPYS (multiply signed, 990/10A, 990/12)
DIVS (divide signed, 990/10A, 990/12)
AR (add real, 990/12)
CIR (convert integer to real, 990/12)
SR (subtract real, 990/12)
MR (multiply real, 990/12)
DR (divide real, 990/12)
LR (load real, 990/12)
STR (store real, 990/12)
AD (add double, 990/12)
CID (convert integer to double, 990/12)
SD (subtract double, 990/12)
MD (multiply double, 990/12)
DD (divide double, 990/12)
LD (load double, 990/12)
STD (store double, 990/12)

Type 7 instructions
The word specified the operation to be performed.
IDLE (cpu idle)
RSET (cpu reset)
RTWP (return workspace pointer)
CKON (clock on)
CKOF (clock off)
LREX (load ROM and execute)
EMD (execute micro diagnostic, 990/12)
EINT (enable interrupt, 990/12)
DINT (disable interrupt, 990/12)
CRI (convert real to integer, 990/12)
CDI (convert double to integer, 990/12)
NEGR (negate real, 990/12)
NEGD (negate double, 990/12)
CRE (convert real to extended integer, 990/12)
CDE (convert double to extended integer, 990/12)
CER (convert extended integer to real, 990/12)
CED (convert extended integer to double, 990/12)
XIT (exit floating point - nop, 990/12)

Type 8 instructions
The first part specifies the operation, the second part specifies the register or mask. The third part, if present, specifies an immediate operand in a second word.
LIMI (load interrupt mask immediate)
LI (load immediate)
AI (add immediate)
ANDI (and immediate)
ORI (or immediate)
CI (compare immediate)
STWP (store workspace pointer)
STST (store status)
LWPI (load workspace pointer immediate)
BLSK (branch immediate push link onto stack, 990/12)

Type 9 instructions
One part of the word specifies the operation, the second part provides the register, the third part provides information for locating the second operand.
MPY (unsigned multiply)
DIV (unsigned divide)

Type 10 instruction
The first part specifies the operation, the second part specifies the map file (0=kernel, 1=user) and the third specifies a register.
This instruction supported on the 990/10, 990/10A and 990/12.
LMF (load map file)

Type 11 instructions
The first word is the opcode; the first part of the second word is the byte count field, the second part is the destination operand and the third part is the source operand. These instructions supported on the 990/12.
NRM (normalize)
RTO (right test for ones)
LTO (left test for ones)
CNTO (count ones)
BDC (binary to decimal conversion)
DBC (decimal to binary conversion)
SWPM (swap multiple)
XORM (xor multiple)
ORM (or multiple)
ANDM (and multiple)
SM (subtract multiple)
AM (add multiple)

Type 12 instructions
The first part of the first word is the opcode, the second part of the first word indicates a checkpoint register; the first part of the second word is the byte count field, the second part is the destination operand and the third part is the source operand. These instruction supported on the 990/12.
SNEB (search string for not equal byte)
CRC (cyclic redundancy code calculation)
TS (translate string)
CS (compare string)
SEQB (search string for equal byte)
MOVS (move string)
MVSR (move string reversed)
MVSK (move string from stack)
POPS (pop string from stack)
PSHS (push string to stack)

Type 13 instructions
The first word is the opcode; the first part of the second word is the byte count field, the second part is the shift count and the third part is the source operand. These instructions supported on the 990/12.
SRAM (shift right arithmetic multiple)
SLAM (shift left arithmetic multiple)

Type 14 instructions
The first word is the opcode; the first part of the second word is the position field and the second part is the source operand. These instructions supported on the 990/12.
TMB (test memory bit)
TCMB (test and clear memory bit)
TSMB (test and set memory bit)

Type 15 instruction
The first part of the first word is the opcode, the second part of the first word indicates a width; the first part of the second word is the position, the second part is the source operand. This instruction supported on the 990/12.
IOF (invert order of field)

Type 16 instructions
The first part of the first word is the opcode, the second part of the first word indicates a width; the first part of the second word is the position, the second part is the destination operand and the third part is the source operand. These instructions supported on the 990/12.
INSF (insert field)
XV (extract value)
XF (extract field)

Type 17 instructions
The first word is the opcode; the first part of the second word is the value field and the second part is the register and the third part is the relative offset. These instructions supported on the 990/12.
SRJ (subtract value from register and jump)
ARJ (add value to register and jump)

Type 18 instructions
The first part of the word is the opcode and the second part is the register specification. These instructions supported on the 990/12.
STPC (store PC in register)
LIM (load interrupt mask from register)
LST (load status register)
LWP (load workspace pointer)
LCS (load control store)

Type 19 instruction
The first word is the opcode; the first part of the second word is the destination operand and the second part is the source operand. This instruction supported on the 990/12.
MOVA (move address)

Type 20 instructions
The first word is the opcode; the first part of the second word is the condition code field, the second part is the destination operand and the third part is the source operand. These instructions supported on the 990/12.
SLSL (search list logical address)
SLSP (search list physical address)

Type 21 instruction
The first part of the first word is the opcode, the second part of the first word specifies the destination length; the first part of the second word specifies the source length, the second part is the destination operand and the third part is the source operand. This instruction supported on the 990/12.
EP (extend precision)

Assembly Language Programming Example 
A complete "Hello, world!" program in TI-990 assembler, to run under DX10:

         IDT     'HELLO'
         TITL    'HELLO - hello world program'
 *
         DXOP    SVC,15          Define SVC
 TMLUNO  EQU     0               Terminal LUNO
 *
 R0      EQU     0
 R1      EQU     1
 R2      EQU     2
 R3      EQU     3
 R4      EQU     4
 R5      EQU     5
 R6      EQU     6
 R7      EQU     7
 R8      EQU     8
 R9      EQU     9
 R10     EQU     10
 R11     EQU     11
 R12     EQU     12
 R13     EQU     13
 R14     EQU     14
 R15     EQU     15
 *
         DATA    WP,ENTRY,0
 * 
 * Workspace (On the 990 we can "preload" registers)
 *
 WP      DATA    0               R0
         DATA    0               R1 
         DATA    >1600           R2 - End of program SVC
         DATA    >0000           R3 - Open I/O opcode
         DATA    >0B00           R4 - Write I/O opcode
         DATA    >0100           R5 - Close I/O opcode
         DATA    STRING          R6 - Message address
         DATA    STRLEN          R7 - Message length
         DATA    0               R8
         DATA    0               R9
         DATA    0               R10
         DATA    0               R11
         DATA    0               R12
         DATA    0               R13
         DATA    0               R14
         DATA    0               R15
 * 
 * Terminal SVC block
 *
 TRMSCB  BYTE    0               SVC op code (0 = I/O)
 TRMERR  BYTE    0               Error code
 TRMOPC  BYTE    0               I/O OP CODE
 TRMLUN  BYTE    TMLUNO          LUNO
 TRMFLG  DATA    0               Flags
 TRMBUF  DATA    $-$             Buffer address 
 TRMLRL  DATA    $-$             Logical record length
 TRMCHC  DATA    $-$             Character count
 * 
 * Message
 *
 STRING  TEXT    'Hello world!'
         BYTE    >D,>A
 STRLEN  EQU     $-STRING
         EVEN
         PAGE
 * 
 * Main program entry
 *
 ENTRY   MOVB    R3,@TRMOPC      Set open opcode in SCB
         SVC     @TRMSCB         Open terminal
         MOVB    @TRMERR,R0      Check for error
         JNE     EXIT
         MOVB    R4,@TRMOPC      Set write opcode
         MOV     R6,@TRMBUF      Set buffer address 
         MOV     R7,@TRMLRL      Set logical record length
         MOV     R7,@TRMCHC       and character count
         SVC     @TRMSCB         Write message
         MOVB    @TRMERR,R0      Check for error
         JNE     CLOSE
 CLOSE   MOVB    R5,@TRMOPC      Set close opcode
         SVC     @TRMSCB         Close terminal
 EXIT    SVC     R2              Exit program
 *
         END

You can try out the above for yourself on a TI-990 simulator. Dave Pitts's sim990 simulates the TI-990 and includes software kits for native operating systems (including DX10).

The following program is a standalone version that prints on the serial terminal connected to CRU address 0. It illustrates the CRU I/O and workspace linkage for the PRINT subroutine.

         IDT     'HELLO'
         TITL    'HELLO - hello world program'
 *
 R0      EQU     0
 R1      EQU     1
 R2      EQU     2
 R3      EQU     3
 R4      EQU     4
 R5      EQU     5
 R6      EQU     6
 R7      EQU     7
 R8      EQU     8
 R9      EQU     9
 R10     EQU     10
 R11     EQU     11
 R12     EQU     12
 R13     EQU     13
 R14     EQU     14
 R15     EQU     15
 *
 * Terminal CRU bits
 *
 TRMCRU  EQU     >0              Terminal device address
 XMIT    EQU     8
 DTR     EQU     9
 RTS     EQU     10
 WRQ     EQU     11
 RRQ     EQU     12
 NSF     EQU     13
 *
         PAGE
 *
 * Main program entry
 *
 ENTRY   LWPI    WP              Load our workspace pointer
         BLWP    @PRINT          Call our print routine
         DATA    STRING
         DATA    STRLEN
         IDLE
 *
 WP      BSS     32              Main program workspace
 *
 * Message
 *
 STRING  TEXT    'Hello world!'
         BYTE    >D,>A
 STRLEN  EQU     $-STRING
         EVEN
         PAGE
 *
 * Print a message
 *
 PRINT   DATA    PRWS,PRENT
 PRENT   EQU     $
         MOV     *R14+,R2        Get buffer address
         MOV     *R14+,R1        Get message length
         SBO     DTR             Enable terminal ready
         SBO     RTS
 PRI010  LDCR    *R2+,8          Send out a character
         TB      WRQ             Wait until done
         JNE     $-2
         SBZ     WRQ
         DEC     R1
         JGT     PRI010
         RTWP
 *
 PRWS    DATA    0,0,0,0,0,0,0,0
         DATA    0,0,0,0,TRMCRU,0,0,0
 *
         END     ENTRY

TI-990 models 
The TI-990 processors fell into several natural groups depending on the original design upon which they are based and which I/O bus they used.

All models supported the Communications Register Unit (CRU) which is a serial bit addressable I/O bus. Also, supported on higher end models was the TILINE I/O bus which is similar to DEC's popular UNIBUS. The TILINE also supported a master/slave relationship that allowed multiple CPU boards in a common chassis with arbitration control.

TILINE/CRU models
The following models used the TILINE as their principal mass storage bus:
TI-990/5 — TMS-9900 microprocessor with 64K bytes of memory
TI-990/10 — TTL processor with memory mapping support to 2M bytes of ECC memory
TI-990/10A — TMS-99000 microprocessor with memory mapping support to 1M bytes of memory 
TI-990/12 — Schottky TTL processor with memory mapping to 2M bytes of ECC Memory, workspace caching, hardware floating point, extended mode instructions and writeable control store

CRU only models
The following models used the CRU as their principal bus:
TI-990/4 — TMS-9900 microprocessor with 56K bytes of memory
TI-990/9 — The original TTL implementation

Operating systems 
Several operating systems were available for the TI-990

From TI:
TX990/TXDS
DX10
DNOS Distributed Network Operating System

From third parties:
UCSD Pascal

Sources
 990 Computer Family Systems Handbook
 990 Assembler Reference Manual

External links
 Dave Pitts' TI 990 page — Includes a simulator, cross assembler, cross linker, utilities and Operating System images.
  Translator of SCI 990 to COBOL
 ti990.co.uk in internet archive Site detailing the preservation and restoration of TI 990 series minicomputers

Texas Instruments computers
Minicomputers
16-bit computers