The Texas Instruments TI-99/4A (1981–1984) was one of the first 16-bit home computers ever sold. Designed around the TMS9900 CPU — a chip derived from TI's minicomputer lineage — the machine predated the IBM PC and offered a genuinely advanced architecture for its era, albeit with several unusual design choices that both empowered and constrained developers.
The system sold approximately 2.8 million units before TI exited the home computer market in 1983. It remains beloved by hobbyists for its quirky architecture, powerful sprite hardware, and the active modern community still writing software for it today.
TMS9900 @ 3.0 MHz
16-bit registers & ALU
8-bit external data bus
256 bytes scratchpad
+ 16 KB (32KB w/exp.)
VDP: 16 KB VRAM
8 KB Console ROM
26 KB+ GROM
Cartridge up to 8 KB
TMS9918A VDP
256×192 px, 15 colors
32 sprites, 3 modes
TMS9919 / SN76489
3 tone channels
1 noise channel
Cassette (CS1/CS2)
PEB: floppy/HDD
Solid-state cartridges
The TMS9900 (Texas Instruments Metal-oxide Semiconductor 9900) is a 16-bit NMOS microprocessor introduced in 1976. It was TI's answer to the minicomputer market, shrunk onto a single chip. The TI-99/4A runs it at 3.0 MHz (3,000,000 cycles per second), though effective throughput is considerably lower due to the memory wait states imposed by the system design.
16 bits internal
8 bits external
Multiplexed A/D bus
64 KB linear
0x0000–0xFFFF
16-bit address bus
3.0 MHz crystal
CLKOUT = CLKIN/1
Φ1 and Φ2 phases
NMOS process
40-pin DIP package
+5V single supply
| Status Bit | Pos | Meaning |
|---|---|---|
L> Logical > | 15 | Set if result is logically greater than |
A> Arithmetic > | 14 | Set if signed result is arithmetically greater |
EQ Equal | 13 | Set if result equals zero / comparison equal |
C Carry | 12 | Carry out from MSB |
OV Overflow | 11 | Signed arithmetic overflow |
OP Odd Parity | 10 | Odd number of 1-bits in byte result |
XOP | 9 | Extended operation (XOP instruction used) |
IM Int Mask | 3–0 | Interrupt priority mask (0=all enabled, F=all masked) |
The TMS9900 has a CISC-style instruction set with 72 distinct opcodes operating on bytes, words, and bits. Instructions are 16 bits wide (one or two words). Most instructions support multiple addressing modes.
| Mode | Notation | Example | Description |
|---|---|---|---|
| Register | Rn | MOV R1,R2 | Operand is workspace register |
| Register Indirect | *Rn | MOV *R1,R2 | Rn contains memory address of operand |
| Symbolic (Direct) | @ADDR | MOV @1000H,R1 | Absolute memory address follows opcode |
| Indexed | @ADDR(Rn) | MOV @TABLE(R3),R1 | Base address + register offset |
| Auto-Increment | *Rn+ | MOV *R1+,R2 | Indirect; Rn incremented by 1 (byte) or 2 (word) after use |
| Immediate | #nn | LI R0,#1234H | Literal value in following word |
| Instruction | Opcode | Cycles | Description |
|---|---|---|---|
MOV | 0x0200 | 14+ | Move word (16-bit copy) |
MOVB | 0x0A00 | 14+ | Move byte (8-bit copy) |
A / AB | 0x0600 | 14+ | Add word / byte |
S / SB | 0x0700 | 14+ | Subtract word / byte |
MPY | 0x03C0 | 52 | Unsigned multiply (32-bit result in Rn:Rn+1) |
DIV | 0x03D0 | 92+ | Unsigned divide |
BL | 0x0680 | 12 | Branch and Link (return addr → R11) |
BLWP | 0x0400 | 26 | Branch/Link with Workspace switch (full context save) |
RTWP | 0x0380 | 14 | Return with Workspace Pointer (restore context) |
LI | 0x0200 | 12 | Load Immediate (word into register) |
SBO / SBZ | CRU ops | 12 | Set CRU bit one / zero (I/O bus) |
TB | CRU ops | 12 | Test CRU bit (I/O input) |
LDCR / STCR | CRU ops | 20+ | Load/Store n bits to CRU (serial I/O port) |
The TMS9900 supports 16 priority interrupt levels (0–15) via the 4-bit interrupt mask in the Status Register. Level 0 = highest priority (NMI-like). Level 4 is the normal VDP interrupt. The RESET vector is at 0x0000 and loads WP and PC from the first two words.
| Level | Vector Address | Source |
|---|---|---|
| 0 (RESET) | 0x0000–0x0003 | Hardware reset |
| 1 | 0x0004–0x0007 | External INT1 (VDP VBLANK on TI-99/4A) |
| 2 | 0x0008–0x000B | External INT2 (VDP HBLANK line — rarely used) |
| 3 | 0x000C–0x000F | External INT3 (Tape/keyboard) |
| 4 | 0x0010–0x0013 | External INT4 (Speech synthesizer) |
| 15 | 0x003C–0x003F | XOP (software trap via XOP instruction) |
The TMS9900's external bus runs at 3 MHz but every memory cycle requires multiple clock phases. The system inserts wait states for DRAM refresh and ROM access:
| Range | Size | Type | Description |
|---|---|---|---|
0x0000–0x001F | 32B | ROM (vectors) | INT0–INT15 vector pairs (WP,PC) |
0x0000–0x1FFF | 8 KB | Console ROM | BIOS, GPL interpreter, keyboard handler, VDP init, BASIC entry points |
0x2000–0x3FFF | 8 KB | Low RAM (opt.) | Rarely installed; some mini-memory modules use this range |
0x4000–0x400F | 16B | VDP Read port | Memory-mapped read from VDP data (0x4000) and VDP status (0x4002) |
0x4010–0x5FFF | — | VDP Write port | Write data to VDP (0x4000), write register/address (0x4002) |
0x6000–0x7FFF | 8 KB | Cartridge ROM | Module port — cartridge solid-state ROM mapped here |
0x8000–0x82FF | 768B | RAM (mirrors) | Mirrors of the 256-byte scratchpad (3× mirror) |
0x8300–0x83FF | 256B | Scratchpad RAM | Fast on-board SRAM; CPU workspace registers live here |
0x8400–0x87FF | 1 KB | Sound chip I/O | Write-only port to TMS9919/SN76489 sound chip |
0x8800–0x8BFF | 1 KB | VDP status read | Read VDP status register (address 0x8800) |
0x8C00–0x8FFF | 1 KB | VDP write | Write to VDP data/register (0x8C00 data, 0x8C02 ctrl) |
0x9000–0x93FF | 1 KB | Speech I/O | Speech synthesizer read/write ports |
0x9800–0x9BFF | 1 KB | GROM read | Read from GROM data/address |
0x9C00–0x9FFF | 1 KB | GROM write | Write to GROM address register |
0xA000–0xDFFF | 16 KB | 32K RAM Exp. | Upper portion of 32 KB RAM expansion (via PEB or side port) |
0xE000–0xFFFF | 8 KB | ROM/Expansion | Additional ROM space; second bank in 32K expansion |
The Console ROM at 0x0000–0x1FFF contains the fundamental firmware of the TI-99/4A. It is a mask-programmed, read-only memory — the only way to update it would be to replace the chip itself. TI released several console ROM versions during the machine's production life.
0x0000–0x001F: 16 vectors (WP+PC pairs) for all interrupt levels. RESET vector at 0x0000.
32 bytesInterpretive engine for GROM-based programs (GPL = Graphics Programming Language). The console ROM contains the entire GPL runtime.
~3 KBLow-level VDP initialization, screen clear, and character output routines. Called by BASIC and GPL programs.
~1 KBScans the keyboard matrix via CRU bits. Returns keycode in workspace register. Handles repeat and shift states.
~1 KBInitializes scratchpad RAM, checks for cartridge, presents the master title screen, launches GROM-based menu.
~1 KBFloating-point arithmetic, BCD routines, and string manipulation called by TI BASIC ROM (in GROM).
~2 KBThe cartridge slot maps 8 KB of ROM into the CPU address space at 0x6000–0x7FFF. Cartridge ROMs are solid-state (no moving parts) — a major selling point of the system. The cartridge also contains GROM chips (up to 40 KB of additional GPL program data) accessed via the GROM port, not directly by the CPU.
| Byte | Content | Notes |
|---|---|---|
| 0x6000 | 0xAA | GPL magic byte 1 |
| 0x6001 | 0x01 | GPL magic byte 2 (ROM program flag) |
| 0x6002–3 | Name ptr | Points to null-terminated program name string |
| 0x6004–5 | Entry ptr | GPL or assembly entry point (BLWP or B) |
| 0x6006–7 | Next ptr | 0x0000 if single program, else next header |
The scratchpad RAM is the only RAM built into the TI-99/4A console itself. It is a fast static RAM (SRAM) chip — zero wait states, accessible at full bus speed. Despite its tiny size, it is absolutely critical: it holds the CPU workspace registers, the GPL interpreter's working variables, and the top of the system stack.
The 256-byte block is mirrored at 0x8000, 0x8100, and 0x8200 due to incomplete address decoding — a common technique in 1980s hardware design to simplify chip-select logic.
The standard memory expansion for the TI-99/4A adds 32 KB of DRAM in two 16 KB banks. The first bank (0x2000–0x3FFF) is rarely used. The second bank (0xA000–0xDFFF) is the standard "expansion RAM" used by TI Extended BASIC, assembly programs, and most third-party software.
Dynamic RAM (DRAM)
Requires refresh cycles
4+ wait states
Peripheral Expansion Box (PEB) or
Side-port memory module
CPU can address directly BUT must go through expansion bus — adds latency vs scratchpad
8-bit busTI Extended BASIC requires 32 KB expansion. Uses both banks (0x2000 and 0xA000).
RequiredThe TMS9918A VDP has its own dedicated 16 KB of VRAM, completely separate from the CPU's address space. The CPU cannot directly address VRAM — it must communicate through the VDP's two I/O ports. This is a crucial architectural distinction.
| VRAM Region | Typical Use (Graphics 2 mode) |
|---|---|
| 0x0000–0x17FF | Pattern Name Table (768 bytes — 32×24 screen) |
| 0x1800–0x1AFF | Sprite Attribute Table (128 bytes) |
| 0x2000–0x27FF | Pattern Generator Table (2 KB — character bitmaps) |
| 0x3800–0x3BFF | Sprite Pattern Table (2 KB — sprite bitmaps) |
| 0x2000–0x3FFF | Color Table (6 KB in Graphics 2 mode) |
GROM (Graphics ROM) is a proprietary TI chip that stores programs in a special byte-code language called GPL (GROM Programming Language, also called Graphics Programming Language). GROMs are accessed through a dedicated port — the CPU does not address them directly in its memory map. Instead, the CPU writes an address to the GROM port and reads back bytes of GPL code, which the GPL interpreter in Console ROM then executes.
| GROM # | Byte Range | Content |
|---|---|---|
| GROM 0 | 0x0000–0x17FF | Console system GROM: Master title screen, system menu, font data |
| GROM 1 | 0x2000–0x37FF | Console system GROM 1: TI BASIC language core (part 1) |
| GROM 2 | 0x4000–0x57FF | Console system GROM 2: TI BASIC language core (part 2) |
| GROM 3–7 | 0x6000–0xFFFF | Cartridge GROMs (module-specific programs) |
GROMs 0, 1, and 2 are physically inside the TI-99/4A console. GROMs 3–7 are in cartridges. The 3 console GROMs contain approximately 18 KB of system software.
GPL is a compact, stack-based byte-code language interpreted by the Console ROM's GPL engine. It is higher-level than machine code but runs much slower (roughly 1/10 the speed of native TMS9900 assembly). Most TI-written cartridges use GPL for their main logic and call assembly routines for speed-critical sections.
The TMS9918A is a dedicated video processor with its own 16 KB of VRAM, completely independent of the CPU. It generates a composite NTSC (or PAL for European models) video signal. The CPU communicates via two I/O ports. The VDP generates a VBLANK interrupt (INT1, level 1) at the end of each frame (~60 Hz NTSC).
256 × 192 active pixels
Tile-based rendering
8×8 pixel characters
15 colors + transparent
2 colors per 8×1 pixel row
16-color palette (fixed)
32 sprites maximum
Up to 4 per scanline
8×8 or 16×16 size
2× magnification option
Composite NTSC/PAL
RF modulated (built-in)
No direct RGB output
| Mode | Resolution | Colors | Use Case |
|---|---|---|---|
| Text Mode (M1) | 40×24 chars | 2 (fg+bg only) | TI BASIC default. 240 chars on screen. No sprites. Monochrome. |
| Graphics 1 (M0) | 32×24 chars | 2 per group of 8 chars | General use. 256 tile patterns. 32 sprites. |
| Graphics 2 (bitmap) | 256×192 px | 2 per 8px row | True bitmap mode. 6 KB color table. Used for games. |
| Multicolor | 64×48 blocks | 16 per block | Each 4×4 block has one color. Limited resolution, many colors. |
| Register | Purpose | Key Bits |
|---|---|---|
| R0 | Mode control 1 | M3 (bitmap), external video, sprite size, sprite magnify |
| R1 | Mode control 2 | M1/M2 (text/gfx), VBLANK enable, display enable, 16×16 sprite |
| R2 | Name Table Base | VRAM address >> 10 (which 1KB block holds the screen map) |
| R3 | Color Table Base | VRAM address >> 6 |
| R4 | Pattern Gen. Base | VRAM address >> 11 (character/tile bitmap data) |
| R5 | Sprite Attr. Base | VRAM address >> 7 (sprite Y,X,name,color table) |
| R6 | Sprite Pat. Base | VRAM address >> 11 (sprite bitmap data) |
| R7 | Text/Border Color | High nibble = fg color, low nibble = bg/border color |
| # | Color | R | G | B | Notes |
|---|---|---|---|---|---|
| 0 | Transparent | — | — | — | Shows backdrop/border color |
| 1 | Black | 0 | 0 | 0 | |
| 2 | Medium Green | 33 | 200 | 66 | Default screen color |
| 3 | Light Green | 94 | 220 | 120 | |
| 4 | Dark Blue | 84 | 85 | 237 | |
| 5 | Light Blue | 125 | 118 | 252 | |
| 6 | Dark Red | 212 | 82 | 77 | |
| 7 | Cyan | 66 | 235 | 245 | |
| 8 | Medium Red | 252 | 85 | 84 | |
| 9 | Light Red | 255 | 121 | 120 | |
| 10 | Dark Yellow | 212 | 193 | 84 | |
| 11 | Light Yellow | 230 | 206 | 128 | |
| 12 | Dark Green | 33 | 176 | 59 | |
| 13 | Magenta | 201 | 91 | 186 | |
| 14 | Gray | 204 | 204 | 204 | |
| 15 | White | 255 | 255 | 255 |
The VDP supports 32 sprites with hardware collision detection. The Sprite Attribute Table in VRAM defines each sprite:
The TMS9900's CRU (Communications Register Unit) is a serial bit-addressable I/O bus — unique to TI's architecture. Instead of traditional parallel I/O ports, the CRU allows the CPU to read or write individual bits or groups of up to 16 bits at a time. The CRU address space is 4096 bits (512 bytes equivalent) mapped to a separate address region.
SBO (Set Bit One), SBZ (Set Bit Zero), TB (Test Bit), LDCR (Load CRU — write n bits), STCR (Store CRU — read n bits). The CRU base address register is the upper byte of workspace register R12 — meaning each software module can have its own CRU base.| CRU Base Addr | Peripheral | Bits Used |
|---|---|---|
| 0x0000–0x001E | Keyboard columns 0–7 | 8 columns select (output) |
| 0x0000–0x000E | Keyboard row scan | 8 rows read (input) |
| 0x0010–0x001E | Joystick input | Fire, up, down, left, right |
| 0x0100–0x010E | Cassette output (CS1) | 1 bit write (motor + data) |
| 0x0110–0x011E | Cassette input (CS2) | 1 bit read (data) |
| 0x0200–0x020E | Alpha Lock key | 1 bit (key state) |
| 0x1000–0x17FE | PEB expansion bus | Peripheral cards use these |
The TI-99/4A keyboard is a 8×8 matrix (8 columns × 8 rows = 64 key positions, though not all are populated). The CPU scans it by:
LDCRSTCRTwo cassette ports (CS1 primary, CS2 secondary) use frequency-shift keying (FSK) at 1200 baud. The Console ROM contains the cassette read/write routines. Data is stored as audio tones: ~2400 Hz for a '1' bit and ~1200 Hz for a '0' bit. The motor relay is controlled via CRU bit.
Two 9-pin Atari-compatible joystick ports. Directions and fire button are read via CRU bits. The joystick inputs share CRU addresses with the keyboard rows when specific columns are selected. The Console ROM's joystick subroutines handle the multiplexing.
The right side of the TI-99/4A console has a 44-pin edge connector exposing the full system bus: address lines, data lines, control signals, CRU lines, and power. This connects to the Peripheral Expansion Box (PEB) via a flat cable, or to direct-connect peripherals like the Speech Synthesizer.
A0–A14 (15 bits) + MEMEN signal. A15 not bused out. Limits peripherals to 32 KB address window each.
15-bitD0–D7 (8 bits). Only 8 bits external despite 16-bit CPU. Two cycles per word.
8-bitCRUIN, CRUOUT, CRUCLK lines allow serial I/O to peripheral cards. Each card decodes a unique CRU base address.
serialMEMEN (memory access), WE (write enable), DBIN (data bus input), READY (wait state insert), IAQ (instruction acquisition)
controlThe PEB is a large external chassis housing up to 8 peripheral cards on an internal bus. Each card plugs into the PEB backplane and is connected to the console via a proprietary ribbon cable. Available cards include:
| Card | Description |
|---|---|
| 32 KB RAM Expansion | Provides the full 32 KB expansion RAM at 0x2000–0x3FFF and 0xA000–0xDFFF |
| Disk Controller (DSDD) | Controls up to 3 floppy drives (90K–360K each). Uses DSK1/DSK2/DSK3 device names. |
| RS-232 Card | Two serial ports (RS-232C, up to 9600 baud) + one parallel port. Uses CRU base 0x1300. |
| 80-Column Card | Adds 80×24 text mode via a second video chip. Non-standard, few programs support it. |
| UCSD Pascal Card | Z80 coprocessor card running UCSD Pascal. Completely takes over the system. |
| Myarc 128K/512K RAM | Third-party expanded RAM beyond the standard 32 KB, with bank switching. |
| Horizon RAMdisk | Up to 2 MB battery-backed RAM acting as a solid-state disk drive. |
The TI-99/4A uses the TMS9919 (functionally equivalent to the SN76489) programmable sound generator. It is a write-only device — the CPU can only send commands, never read its state. Accessed at I/O address 0x8400.
3 independent tone generators
Frequency: ~109 Hz – 111 KHz
10-bit frequency divider each
1 noise generator
White or periodic noise
3 frequency steps + tone 3 sync
4-bit attenuation each channel
0 = max volume, 15 = silence
No hardware envelope
Single mixed mono output
Digital (square wave)
No FM, no PCM
All commands are single or two-byte writes to port 0x8400:
The TI-99/4A Speech Synthesizer is an external module plugging into the side expansion port. It contains a TMS5200 (or later TMS5220) Linear Predictive Coding (LPC) speech processor, 128 KB of speech ROM (vocabulary), and connects to the console's I/O bus.
LPC (Linear Predictive Coding) synthesis
10th-order filter
8 KHz sample rate
~350 words in built-in ROM
Expandable via module ROMs
Human-quality for 1980
Memory-mapped at 0x9000
Read: 0x9000 (status)
Write: 0x9400 (data)
Can trigger INT4 (level 4) when speech data buffer empty or ready
| Chip | Function | Notes |
|---|---|---|
| TMS9901 | Programmable Systems Interface | CRU-addressed I/O expander. Handles keyboard, joystick, cassette via its internal registers. Bridges CRU to parallel I/O. |
| TMS9904 | Clock generator / oscillator | Generates the 3 MHz system clock from a crystal. Provides PHI1/PHI2 clock phases to TMS9900. |
| 74LS138 | 3-to-8 decoder (address decode) | Decodes upper address bits to generate chip select signals for ROM, RAM, VDP, GROM, and I/O. |
| SN74LS245 | Bus transceiver | Bidirectional buffer on the data bus. Handles direction control (DBIN/WE) between CPU and peripherals. |
| 4116 / 4164 | DRAM (16K × 1 bit) | 16 chips × 1 bit each = 16 KB VRAM for VDP. Also used in 32 KB expansion modules. |
MOV (word) instead of two MOVB (byte) instructions where possible.*Rn+ for bulk memory copies — fewer instruction fetches.
TI BASIC is built into the three console GROMs (18 KB total). Every TI-99/4A has it — no cartridge needed. Programs use line numbers 1–32767. The interpreter is GPL-based and runs slowly but is always available. Line numbers govern execution order; RUN starts from the lowest. Variables are global, untyped (numeric or string). Strings end with $.
TI Extended BASIC (XB) is a cartridge module that expands TI BASIC with ~60 additional commands, structured programming features, sprite control, assembly call support, and more. Requires the 32 KB RAM expansion. XB programs are source-compatible with TI BASIC programs (all TI BASIC commands work in XB) but XB programs will not run on bare TI BASIC.
| Feature | TI BASIC | Extended BASIC |
|---|---|---|
| Named subroutines | GOSUB/RETURN only | SUB/SUBEND with local vars |
| Structured loops | FOR, WHILE | + EXIT FOR, EXIT SUB |
| Screen output | PRINT, PRINT AT | DISPLAY AT (row,col) format |
| Input | INPUT | ACCEPT AT with VALIDATE |
| VRAM access | None (only via CALL HCHAR etc.) | CALL PEEKV / CALL POKEV |
| RAM access | None | CALL PEEK / CALL LOAD |
| Assembly calls | None | CALL INIT, LOAD, LINK |
| PI constant | 4*ATN(1) | PI built-in |
| Timer | Not available | TIME function |
| String repeat | Loop only | RPT$(str$,n) |
| Formatted output | Manual with STR$/TAB | PRINT USING "format" |
| Sprite distance | COINC only | CALL DISTANCE(#a,#b,var) |
| Sprite size | Fixed 8×8 or 16×16 | CALL MAGNIFY(1-4) |
| Program chaining | Not supported | RUN "filename", MERGE |
| Number format | Manual STR$/print | PRINT USING |
| Memory required | None (ROM-based) | 32 KB RAM expansion + cartridge |
:: to put multiple statements on one line — saves program memory and speeds the interpreter since each line has overhead. Example: 100 X=1 :: Y=2 :: Z=X+Y
CALL SPRITE is the primary sprite creation and positioning command in both TI BASIC and TI Extended BASIC. It instructs the TMS9918A Video Display Processor to display a hardware sprite on screen — a moving graphical object rendered entirely by the VDP chip, completely independent of the character background, with no CPU intervention required once set in motion.
The TI-99/4A VDP supports up to 32 hardware sprites simultaneously (#1 through #32). Each sprite is an 8×8 pixel (or 16×16 with magnification) bitmap that can be freely positioned anywhere on the 256×192 pixel screen, colored independently, and given automatic motion. Hardware collision detection between sprites is built into the VDP.
CALL MOTION is set, the VDP moves sprites automatically every VBLANK (60 times/sec)
without any CPU cycles — freeing the TMS9900 for game logic, input, and sound.
Sprites are identified by a number prefixed with #, from #1 to #32.
Each sprite number is an independent hardware object. You can have up to 32 on screen at once,
but the VDP enforces a maximum of 4 sprites per horizontal scanline — the 5th sprite
on any given scanline is silently skipped (not drawn). This causes the classic "sprite flicker"
in busy games.
| Value | Meaning | Notes |
|---|---|---|
#1 | Sprite slot 1 (highest priority) | Drawn on top of all other sprites. Player character typically uses #1. |
#2–#31 | Sprite slots 2–31 | Drawn in order; lower numbers appear on top of higher numbers. |
#32 | Sprite slot 32 (lowest priority) | Drawn beneath all other sprites. Backgrounds / decorative elements. |
#ALL | All sprites | Valid only in CALL DELSPRITE — removes all 32 sprites at once. |
The charcode tells the VDP which 8×8 pixel bitmap to use as the sprite's visual shape.
The sprite uses the same pattern table as screen characters — so you define a sprite's appearance
with CALL CHAR, then reference it by its character code number.
Any character code from 32 to 143 can be redefined with CALL CHAR
and used as a sprite pattern. Codes 32–95 are the standard ASCII printable characters (space,
letters, numbers, symbols) — you can redefine these freely. Codes 96–143 are entirely user-definable.
Each character is an 8×8 pixel grid = 8 bytes = 16 hex digits.
Each hex digit pair is one row of 8 pixels. A 1 bit = pixel ON (sprite color),
a 0 bit = pixel OFF (transparent).
| Shape | Code | Pattern String | Visual |
|---|---|---|---|
| Solid square | 96 | "FFFFFFFFFFFFFFFF" | ████████ (all 8 rows full) |
| Hollow square | 97 | "FF818181818181FF" | Border only, hollow center |
| Filled circle | 98 | "3C7EFFFFFFFF7E3C" | Rounded blob shape |
| Small dot | 99 | "0000183C3C180000" | 4×4 dot in center |
| Cross / plus | 100 | "1818FF18181818FF" | + shape |
| Arrow right | 101 | "0810204040201008" | > pointing right |
| Arrow left | 102 | "1008040202040810" | < pointing left |
| Space invader | 103 | "24DBE7E7E7240000" | Classic alien shape |
| Heart | 104 | "006699FFFF7E3C18" | Heart outline shape |
| Spaceship | 105 | "183C7EFFDB7E1818" | Diamond with details |
| Bullet (horiz) | 106 | "000000FF00000000" | Thin horizontal bar |
| Bullet (vert) | 107 | "1010101010101010" | Thin vertical line |
| Explosion 1 | 108 | "A554A554A554A554" | Checkerboard burst |
| Explosion 2 | 109 | "5AA55AA55AA55AA5" | Alternate checkerboard |
With CALL MAGNIFY(2) in XB, each sprite is doubled to 16×16 pixels but still uses an
8×8 bitmap (just scaled up). For true 16×16 sprites with full resolution, use
two consecutive even charcode numbers: the VDP combines chars N and N+2 (and N+1 and N+3
for the bottom half) into a 16×16 composite. The charcode must be an even number and
divisible by 4 for 16×16 mode (hardware behavior of TMS9918A).
The color parameter sets the sprite's foreground color — the color of every 1-bit
pixel in the sprite bitmap. 0-bit pixels are always transparent (the background
shows through). Each sprite has exactly one color; multi-color sprites require
overlapping multiple sprites.
Colors are specified as integers 1 through 16, matching the TMS9918A's fixed 16-color palette. Color 1 (transparent) makes the sprite completely invisible — useful for "hit detection only" sprites.
| Value | Color Name | Hex (approx.) | Use Case |
|---|---|---|---|
| 1 | Transparent | — | Invisible sprite (collision only) |
| 2 | Medium Green | #21C842 | Grass, aliens, foliage |
| 3 | Light Green | #5EDC78 | Highlights, plants |
| 4 | Dark Blue | #5455ED | Player ship, water |
| 5 | Light Blue | #7D76FC | Sky objects, ice |
| 6 | Dark Red | #D4524D | Enemies, danger |
| 7 | Cyan | #42EBF5 | Bullets, energy beams |
| 8 | Medium Red | #FC5554 | Explosions, fire |
| 9 | Light Red | #FF7978 | Hearts, health |
| 10 | Dark Yellow | #D4C154 | Gold, stars |
| 11 | Light Yellow | #E6CE80 | Highlights, coins |
| 12 | Dark Green | #21B03B | Ground, trees |
| 13 | Magenta | #C95BB5 | Power-ups, portals |
| 14 | Gray | #CCCCCC | Rocks, metal objects |
| 15 | Black | #000000 | Outlines, shadows |
| 16 | White | #FFFFFF | Player, bullets, UI |
Since each sprite has only one color, multi-color objects require layering two or more sprites
on top of each other at the same position, each with a different bitmap (showing different portions)
and a different color. Move them together with synchronized CALL MOTION or CALL LOCATE.
Row and column set the sprite's top-left corner position in pixel coordinates.
The TI-99/4A screen is 256 pixels wide and 192 pixels tall.
Unlike CALL HCHAR which uses character cell coordinates (1–24 rows, 1–32 columns),
sprite positions are raw pixels starting at 1 (top-left).
| Coordinate | Range | Notes |
|---|---|---|
| row (vertical) | 1 – 256 | 1 = top of screen. 192 = last visible row for 8×8 sprite. Values 193–256 place sprite partially or fully below screen. Value 209 (0xD0) is the sentinel that hides sprite when scanned by VDP. |
| column (horizontal) | 1 – 256 | 1 = leftmost pixel. 249 = last visible column for 8×8 sprite. Values beyond 256 wrap (TMS9918A uses 8-bit column register). |
| Char Row | Pixel Row | Char Col | Pixel Col |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 9 | 2 | 9 |
| 3 | 17 | 4 | 25 |
| 6 | 41 | 8 | 57 |
| 12 | 89 | 16 | 121 |
| 13 | 97 | 17 | 129 |
| 18 | 137 | 24 | 185 |
| 24 | 185 | 32 | 249 |
CALL LOCATE(#n, 200, 1) to park a sprite invisibly below the screen.
CALL MOTION(#n, vrow, vcol) sets a sprite's velocity. The VDP automatically
updates the sprite's position every VBLANK (~60 times per second NTSC, ~50 PAL) by adding
vrow to the row and vcol to the column. Velocity is in
1/60th-pixel units per frame — or equivalently, pixels per second when vrow/vcol
represent pixel-per-second values that the VDP divides over frames.
| vrow value | Effect |
|---|---|
0 | No vertical movement |
-n (negative) | Moves UP n units per second |
+n (positive) | Moves DOWN n units per second |
| vcol value | Effect |
|---|---|
0 | No horizontal movement |
-n (negative) | Moves LEFT n units per second |
+n (positive) | Moves RIGHT n units per second |
CALL LOCATE(#n, row, col) instantly moves a sprite to the given pixel coordinates,
overriding any MOTION velocity without stopping it. The sprite continues moving from the new position
at the same velocity. Use for teleportation, wrapping, and respawning.
CALL POSITION(#n, rowvar, colvar) reads the current pixel row and column of sprite #n
into two numeric variables. Essential for collision logic, boundary detection, and AI movement.
CALL COINC(#a, #b, tolerance, resultvar) checks whether sprites #a and #b are within
tolerance pixels of each other. The result is -1 (true) if they overlap
within tolerance, or 0 (false) if not.
The tolerance is the maximum allowed distance between their top-left corners.
| Tolerance | Meaning |
|---|---|
| 1–4 | Very precise — sprites must nearly perfectly overlap. Good for bullets vs targets. |
| 5–8 | Normal collision — good for most 8×8 sprite game objects. |
| 9–16 | Loose collision — good for larger objects or forgiving hit boxes. |
| 17+ | Wide proximity detection — use for "nearby" triggers, not true collision. |
CALL DISTANCE(#a, #b, var)
returns the actual pixel distance between sprite centers as a number, allowing circular
collision detection with any threshold. More flexible but slightly slower than COINC.
CALL PATTERN(#n, charcode) changes which character bitmap sprite #n displays
without altering its position, color, or motion. This is the primary animation mechanism —
cycling through a sequence of pre-defined character patterns creates the illusion of movement.
CALL DELSPRITE(#n[, #m, ...]) removes one or more sprites from the screen.
The sprite's slot is freed — position, motion, and pattern are all cleared.
Use #ALL to remove all 32 sprites instantly.
CALL MAGNIFY(size) sets the global sprite magnification level for ALL sprites.
It writes directly to VDP register R1 bits 0–1. Cannot be set per-sprite — it affects all
sprites simultaneously.
| Size Value | Sprite Pixel Size | Bitmap Resolution | Notes |
|---|---|---|---|
1 | 8 × 8 px | 8 × 8 (1:1) | Default. Normal size. Sharpest. |
2 | 16 × 16 px | 8 × 8 scaled ×2 | Double size, blocky. Each pixel becomes 2×2. |
3 | 32 × 32 px | 8 × 8 scaled ×4 | Very large, very blocky. Bosses/large objects. |
4 | 64 × 64 px | 8 × 8 scaled ×8 | Huge. Nearly full screen. Background elements. |
This example demonstrates every CALL SPRITE parameter and related function in a complete mini-game skeleton: player-controlled ship, enemy with motion, bullet firing, collision detection, animation, and boundary wrapping.