https://bumbershootsoft.wordpress.com/2025/02/15/the-8-bit-eras-weird-uncle-the-ti-99-4a/ Bumbershoot Software An Umbrella Site for Michael Martin's software experiments Menu Skip to content * Home * Platform Guides * Projects * Techniques The 8-Bit Era's Weird Uncle: The TI-99/4A 2 Replies It's been awhile since I've looked at an architecture that's completely new to me, and I haven't done any since creating my new scheme for dedicated platform guides. I've now gotten enough material together that I can kick off an exploration of a new platform for me: the Texas Instruments TI-99/4A. For this new introduction, I'll be splitting it into three parts, two of which go live today: * This article, which will be a breezy tour of interacting with the system as a casual user of the time might. This tracks my tours of the ZX81, the Atari 800, and the Amiga 500. * The new TI-99/4A platform guide, as it presently exists. This page will retain my most up-to-date instructions for setting up emulation and cross-development, along with some simple test programs and an index of relevant articles. * Next week's article, which will use the tools and resources in the platform guide to see how we can realize this week's work as cartridge software running without support from BASIC. Some History The TI-99/4 series was released over about the same timeframe as the Atari 800 and the Commodore VIC-20, and its capabilities were comfortably intermediate between the two. Entering when it did, it ended up in a price war that ultimately locked TI into a strategy of selling the base unit at a loss, then making it up on margins on their software. In the event, they ultimately locked out all third-party software for the platform and then didn't release all that much first-party software either. TI crashes out of the home computer market in 1984 and never returns. This would normally result in just being another forgotten also-ran like the Timex Sinclair 2068 (a ZX Spectrum variant for the US that was basically incompatible with all ZX Spectrum software), but it's got three things going for it as a curiosity to revisit: * Despite being a debacle for TI, it was a debacle because it lost them a ton of money, not because it wasn't widely available and recognized. It was common enough that it still got program listings in computer magazines long after it was discontinued. * The architecture of the machine is absolutely wild. We'll see more of that next week, but for now, we'll just note that internal pressures at TI resulted in a project that started out both as a game console in the Atari 2600 vein and as a very compact implementation of their TI-990 minicomputer line. This dual nature is clearly visible in the architecture, once we get down into it. * Its game-console lineage turns out to be really important and influential. While the computer as a whole was an also-ran, its graphics and sound chips were sold separately for a long time afterwards and were very widely used. The TI-99's sound chip, the SN76489, was not only in the ColecoVision and the 8-bit Sega consoles, it made it all the way to the Sega Genesis (where I actually programmed some songs into it) and also found its way into the BBC Micro and the IBM PCjr. Its graphics chip, the TMS9918A, or its immediate successors were used in early Sega consoles (even the Sega Master System, which counts as "late" here, includes a full compatibility mode), the ColecoVision console, and the MSX hardware standard that was extremely popular outside of the US. The chip's design was also widely influential, and its approach to sprites--in particular, a focus on providing a lot of very small sprites instead of a modest number of larger ones--ended up informing Nintendo's approach to their own consoles as well. While the NES's own graphics chip is clearly distinct from the TI, it's also very clearly taking its design cues from it rather than from the Atari 800's player-missile system or the Commodore 64's sparse but highly configurable movable object blocks. Many of those graphics and sound features were conveniently exposed via BASIC, so today we'll play around with the BASIC systems on the TI-99/4A and see what kind of stuff we can do with its tile graphics and its sprites. Starting Up BASIC I'll be using Harmless Lion's Classic99 emulator for this tour; it's got licensed versions of TI's console ROMs as well as much of the rest of their library, and it's also got good support for disk drives and debugging. If you want to follow along but do not wish to install anything, the online JS99'er emulator is every bit as grab-and-go. As we power on, we are first greeted by the boot screen: The TI-99 4/A boot screen. Under the Texas Instruments logo, we see the text "Texas Instruments Home Computer Ready - Press Any key to begin" and some color bars. Much like the Chicago font of the old monochrome Macs, this screen is something that I had at best glancing acquaintance with as a child, but nevertheless it produces completely unreasonable levels of nostalgia for me. Pushing a key to get past it takes us to the cartridge menu, which, with no cartridge installed, just offers the in-ROM BASIC: The TI-99 launch menu. The only option is "Press 1 for TI BASIC". If we did have a game cartridge in there, we might have other options: The TI-99 launch menu, but now there are four entries: "1 for TI BASIC, 2 for Hunt the Wumpus, and 3 and 4 for the French and German editions. We are not here to play games, though, and if we were it would definitely be TI Invaders and not Hunt the Wumpus. We select TI BASIC and are greeted with the main prompt: A "TI BASIC READY" prompt. From here, we can type in a program and run it. Most system-specific operations in TI BASIC are phrased of the form CALL FN for some FN. This includes even really simple things like clearing the screen ( CALL CLEAR), but we may also just redefine any character's pattern at will with CALL CHAR and set colors with CALL COLOR. Our first program will be more exciting than a simple "Hello, world": 10 CALL CHAR(97,"030F1F3F7F7FFFFF") 20 CALL CHAR(98,"C0F0F8F8F0E0C080") 30 CALL CHAR(99,"FFFE7C7830000000") 40 CALL CHAR(104,"80C0603018083800") 50 PRINT "abcdefghijklmnopqrstuvwxyz" 60 PRINT "ch BUMBERSHOOT SOFTWARE" 70 CALL COLOR(9,5,1) 80 CALL COLOR(10,11,1) 90 GOTO 90 (This, as well as the other BASIC programs in this article, can be copy-pasted into Classic99 with the "Paste" option under the edit menu, or into JS99'er with Ctrl-V.) Our first four lines here define four new characters at ASCII points 97-99 and 104. Those correspond to the lower-case letters a, b, c, and h. The second arguments are simple hex dumps, one byte wide, 8 bytes tall, for a standard 8x8 character cell. Lines 50 and 60 print out all the lower-case letters, and then write ch beneath the ab to arrange these four letters into a square graphic in its own right. We then see two CALL COLOR commands, which we'll see in action shortly. Finally, the program goes into an infinite loop, waiting for the user to hit the CLEAR function (Fctn-4 on a real system, F4 in Classic99, and Alt-4 in JS99'er or Classic99). We get a cute little logo! The program above, as entered into TI BASIC and run. The letters A, B, C, and H are replaced with bits of a graphic that, assembled, shows a small cartoon umbrella. ... we also get the screen turning green, which a thing that TI BASIC does while a program is running unless we change the background color ourselves with CALL SCREEN. Once we CLEAR... The program above, displayed in the usual character set. ...the interpreter informs us BREAKPOINT AT 90 and all the graphics return to their normal small-caps in black on cyan. This is actually why we need that infinite loop at the end; if we delete that line (enter the command 90 on its own line to do so) we'll see the graphics appear and then vanish just as they're finished. But the thing to really notice here is the way the letters have changed colors. Letters a through g are blue, and then letters h through o are dark yellow. On pretty much every system I've looked at before this where multicolor text is possible at all, from the ZX Spectrum through the Commodore 64 and the NES to DOS machines, the colors of text are stored in a parallel matrix that applies color assignments to particular locations on the screen. (For scrolling screens like the NES, it's applied to locations on the broader map of which the screen is a window.) Here on the TI, colors belong to the characters themselves, and they are assigned eight at a time. The CALL COLOR command takes three arguments; the first is a block of character codes, followed by color numbers for foreground and background. Our backgrounds here are "transparent", so they inherit the green of the running system. A Nicer Display Now that we kind of know what we're doing, let's make a cooler display out of it: A black box on a green screen, with an umbrella logo and the caption "Bumbershoot Software: 16 more than your TI-83!" For the most part, this is just a question of defining and printing out more characters. Getting the colors to work right, though, poses a few challenges and a few opportunities. * For the solid black background and white text, we can set the background for our custom graphics and the letters we use. * Since we want the full screen background to stay the same, we'll need to leave color group 1 alone. * We need both a space and an exclamation point for our message and its box, though. We'll have to copy the exclamation point graphic somewhere in the letter space at a point we don't use. * We've got lots of punctuation we don't use already, so we won't need new color spaces for that; we can use character 94 (^) for the exclamation point just fine. * Instead of defining a blank character, we can also just define color group 11 (the lower-case letters p through w) to be black on black. I put in a couple more bells and whistles too, and this gives us the final program: 10 CALL CLEAR 20 CALL CHAR(94,"0010101010100010") 30 CALL CHAR(97,"030F1F3F7F7FFFFF") 40 CALL CHAR(98,"C0F0F8F8F0E0C080") 50 CALL CHAR(99,"FFFE7C7830000000") 60 CALL CHAR(104,"80C0603018083800") 70 FOR I=2 TO 8 80 CALL COLOR(I,16,2) 90 NEXT I 100 CALL COLOR(9,5,2) 110 CALL COLOR(10,11,2) 120 CALL COLOR(11,2,2) 130 PRINT "pppppppppppppppppppppppppppp" 140 PRINT "pabppBUMBERSHOOTpSOFTWAREppp" 150 PRINT "pch16pMOREpTHANpYOURpTI-83^p" 160 PRINT "pppppppppppppppppppppppppppp" 170 FOR I=1 TO 10 180 PRINT 190 NEXT I 200 CALL KEY(3,K,S) 210 IF S=0 THEN 200 220 CALL CLEAR There's two other things going on here that we haven't seen before. The first is that it turns out that normal TI BASIC will only ever PRINT things at the bottom of the screen, so to get our banner into the middle of the screen we have to print out a bunch of blank lines afterwards. The second is that instead of forcing a breakpoint we might as well scan the keyboard normally and shut down cleanly when a key is pressed. That's CALL KEY's job. If we want to go further with exploring the hardware, though, we've hit the limits of the built-in BASIC. Fortunately, there is an expansion cartridge that provides quite a few more capabilities. On to Extended BASIC The Extended BASIC cartridge is available under the "Cartridge - Apps" menu options in Classic99, and directly under the "Software..." tab in JS99'er. It runs much faster, can make decent use of memory expansion hardware, and generally offers more capabilities. One of those is the ability to read character patterns instead of just writing them, so we can confirm that we did indeed get the exclamation point graphic right: CALL CHARPAT(33,E$) PRINT E$ It also lets us conveniently write text or put character prompts anywhere on the screen with DISPLAY AT and ACCEPT AT, but even so there's still no notion of a print cursor that can be placed anywhere on the screen; these are more like blitting strings to video memory. Preexisting commands are also extended: multiple calls to CALL COLOR may be combined into one by concatenating their arguments, and we can put multiple statements on one BASIC line. Two colons are used to separate statements instead of the trdaitional one, but the colon is used as part of other commands inline, so it more or less works out. All of this lets us express the same program more compactly: 10 CALL CLEAR :: CALL SCREEN(3) 20 CALL CHAR(94,"0010101010100010") 30 CALL CHAR(97,"030F1F3F7F7FFFFF") 40 CALL CHAR(98,"C0F0F8F8F0E0C080") 50 CALL CHAR(99,"FFFE7C7830000000") 60 CALL CHAR(104,"80C0603018083800") 70 FOR I=2 TO 8 :: CALL COLOR(I,16,2) :: NEXT I 80 CALL COLOR(9,5,2,10,11,2,11,2,2) 90 DISPLAY AT(11,1):"pppppppppppppppppppppppppppp" 100 DISPLAY AT(12,1):"pabppBUMBERSHOOTpSOFTWAREppp" 110 DISPLAY AT(13,1):"pch16pMOREpTHANpYOURpTI-83^p" 120 DISPLAY AT(14,1):"pppppppppppppppppppppppppppp" 130 CALL KEY(3,K,S) :: IF S=0 THEN 130 140 CALL CLEAR The one place where backwards compatibility breaks is that the screen doesn't turn green on its own anymore, so we have to use CALL SCREEN to specifically request that. Sprites Extended BASIC also includes a collection of new commands centered around CALL SPRITE, which will give us access to the sprite hardware. This ends up looking looking quite a lot like a predecessor to the NES sprite system because, as we noted, it kind of is; but coming out as it did when the Atari 800 did, it also bears the marks of that earlier era. * Sprites aren't unique objects like the Atari's "players"; they use the same kinds pattern tables as the custom text characters, and sprites are generally one character wide and tall. This matches the NES's relation of sprites to its own pattern tables. BASIC obliges us to use the same pattern tables for both text and sprites, but the underlying VDP graphics chip can target them independently. * Sprites can be made double-sized, both in the sense of "built out of 4 characters in a 2x2 grid instead of just one" and "each pixel of the defining patterns takes up a 2x2 pixel rectangle on the screen", or both at once. This is comparable to the NES's 8x16 sprite mode but can produce larger images. * There are 32 sprites available at once, and any given scanline can have four of them. This is exactly half the numbers the NES offers, though it evens up if you compare 16x16 sprites to the NES's 8x16 ones used in pairs. * There is pixel-perfect collision detection--however, it's only capable of reporting whether any sprites have collided and cannot tell you which ones did. This has no analogue on other systems, which tend to either be more fine-grained or lack this collision detection capability entirely. TI Extended BASIC provides some custom routines that produce finer-grained results in software. Double-sized sprites are a 2x2 character grid, which is great for our little umbrella graphic, but they also have to be only one color, which is unfortunate. We can just use two sprites, though. The four-sprites-per-scanline limit means that we can only render two umbrellas on the screen together, but that will do for now. The sprites also are presented column major (so, read the screen it would like like "ac" over "bd" instead of "ab" over "cd" like we did in our original case) so we also need to juggle the characters around a bit alongside making space for the handles. All the same, there's not a lot of difference between this and our first tests: 10 CALL CLEAR :: CALL SCREEN(3) 20 CALL CHAR(96,"030F1F3F7F7FFFFF") 30 CALL CHAR(97,"FFFE7C7830000000") 40 CALL CHAR(98,"C0F0F8F8F0E0C080") 50 CALL CHAR(99,"0000000000000000") 60 CALL CHAR(100,"0000000000000000") 70 CALL CHAR(101,"0000000000000000") 80 CALL CHAR(102,"0000000000000000") 90 CALL CHAR(103,"80C0603018083800") 100 CALL MAGNIFY(4) 110 CALL SPRITE(#1,96,5,60,80,#2,100,11,60,80) 120 CALL SPRITE(#3,96,2,160,140,#4,100,15,160,140) 130 CALL KEY(3,K,S) :: IF S=0 THEN 130 140 CALL CLEAR This creates four sprites, each with its own color, and displays it on the screen. CALL MAGNIFY picks one of the four combinations of single/double size and single/double magnification, and CALL SPRITE assigns character, color, and X- and Y- coordinates. The display gives us some bigger umbrellas: Two pixel umbrellas, twice as large as our first ones, and in different colors. These are sprites, not the custom characters of earlier designs. We can even add some extra arguments to CALL SPRITE to set a velocity, and the sprites will move smoothly in that direction without further minding by BASIC. That's very handy since BASIC is pretty slow and won't be able to animate sprites reasonably on their own. 110 CALL SPRITE(#1,96,5,112,80.-40,20,#2,100,11,112,80,-40,20) 120 CALL SPRITE(#3,96,2,112,80,10,-20,#4,100,15,112,80,10,-20) Unfortunately, synchronizing sprites like we have here to make a single multicolor object doesn't work so well, because even Extended BASIC is slow enough that they fall seriously out of sync: The previous two umbrellas, in different locations on the screen because they are moving. The handles started moving at different times, so they appear to be in the wrong places. The period books I'd been reading about the TI suggested that sprite motion was actually provided by the graphics chip itself somehow, but I'm skeptical; everything I've seen about how to use it in machine language suggests that this is a being provided by some kind of system ROM routine, not the chip itself. What's Next BASIC gave us a better tour of the core graphics capabilities here than I was expecting, to be honest. Next week we'll dig into how cartridge software worked on the TI-99/4A and accomplish similar goals without relying on BASIC. The architecture of the system as a whole and the onboard system ROMs in particular are pretty strange, so it'll be a more twisted path than we'd expect. By the time we're done, though, we should have seen a worked example of why everything we do in the Platform Guide is there. Share this: * Twitter * Facebook * Like Loading... Related This entry was posted in retrocoding, ti on February 15, 2025 by Michael Martin. Post navigation - Reversing the Xorshift* PRNG 2 thoughts on "The 8-Bit Era's Weird Uncle: The TI-99/4A" 1. [dec23]arcadeshopper February 15, 2025 at 3:58 pm [The following link is to a TI BASIC program that loads and runs a machine code program that shows off the system's full bitmap graphics capability. The commenting system mangled it, but at the time of edit the link here is to a working program. I think I've managed to fix this in post, but WP and BASIC do not get along and this isn't the first time it's burned me. It's genuinely a neat program though; thanks, arcadeshopper! -MM] https://forums.atariage.com/applications/core/interface/file/ attachment.php?id=524302 100 FOR I=1 TO 128 110 READ J 120 A$=A$&CHR$(J) 130 NEXT I 140 FOR I=1 TO 102 150 READ J 160 Z$=Z$&CHR$(J) 170 NEXT I 180 DATA 0,0,2,224,131,160,198,12,4,217,194,58,194,90,162,213,215,224,131,183,197,198,215,203,208,30,216,30 190 DATA 131,161,2,2,0,100,16,11,0,0,0,0,0,200,0,0,0,0,53,103,0,0,0,0,192,59,192,155,96,128,2,11,131 200 DATA 0,192,75,2,12,220,94,215,224,131,161,5,194,215,192,16,0,4,140,6,2,22,253,4,91,152,32,131,115,131 210 DATA 179,22,251,16,209,131,62,192,59,131,126,131,114,131,196,131,48,0,6,0,104,140,0,136,0,140,2,0,0,0 220 DATA 0,131,160,77,0,39,227,2,3,131,138,130,72,21,43,2,1,131,168,192,8,2,40,0,4,5,194,6,147,192,5,6 230 DATA 147,2,134,154,32,22,242,5,133,4,202,4,199,2,42,255,250,5,133,192,5,6,1,4,198,7,2,6,147,2,38 240 DATA 255,223,23,228,192,10,10,6,225,198,2,138,255,250,19,239,7,2,192,4,5,132,2,12,215,113,6,160 250 DATA 131,142,6,1,10,135,5,202,16,230,6,160,131,126,27,2,27,106 260 OPEN #1:A$ 23296 REM U!!%Y)0'"/#$S!)#AV,)%I05V`)#AI.A&PT)-I0!V`)1!.@SQ(,89)/D"9%7`.@"!A-9!.@SQ108119$&PX8Q2!!V`)1!.>""A17 23371 REM `1;AAXY<;BO1Y)%#A!;#`Y-$B$;&%9@9D!"9`1"!9!4$"=0"1Q8&Q15&R-'%"=9##1#!!AI!9!)(!.-##!#D"K#$@BO3'`9#!(A! 23446 REM I=12!Q+(!1!2!A5%I=3C"B%$!IA!Q"%#"1;C"K*$%1-#C1%!%1)&![*$II52!Q++!-!2!A5&II5#!9/O!A)!"A)-VX%'I)//!C!! 23521 REM #!+!?I!7VQ)!.`D)!).U"K#$@BPY('A#!9.UJ'!!%M!2!I![A"9%"K#$@BV'(<)#!9/O!A)!"A;AAYI%R!)&!!("B=$(#I/AS'(* 23596 REM &A%(""5#"15&"W)+&1)&"A5)Q+#$RB9(!A)>;[A#A]!4`=#AA]!+QA)")!#!1B1"Q)()!I.W"K#$@BRK(.1#!9/ZQQI+8/-+!ET` 23671 REM "[-*Q!E#1!!(9Q$8U1),A!$8T!M,]N\!#>@2!GR!!.@-!CTA!.>,#I$8U;!+V]Q#<%!!EJY7"A<-V]Q'T.@-VW#$>I$!&A1'I)._ 23746 REM (.9>2-%%%1/C27%)%-WCBK%(%-I$!!!#!Q!!!!)!"1!#!Y/K"-1#$!!E--!#$!!'"Q)W!A6#Y1,-QA)A`Q!9]Q)'A]A+'"=-!A?$ 23821 REM RM!7&AE(&M!8%Q)%VR!%R?#$Q"!"".9#+O]!&A%%'M%%&A1'I)._'`A=;.@AA^1%Y)07V_#$V2$-!A)!#A4%"A17`A9#&PQ#!9/G 23896 REM Q##$Q!)A(7M4`=#!%1-#1A!(%!)#9P`YT%)#A9/O&P,)!)0!!A![?!)#!!9'I)/+!A!!1!;AAVI#!!"A"K#$7A;AAXY