As many may know already, I got started in computing when I was a kid with the TI-99/4A home computer. ("Home computer" being a term for anything that wasn't a PC.) In order to "find my roots", so to speak, I've recently taken it upon myself to learn Assembly programming on this thing, seeing as though I missed out on this opportunity when I was younger.
Since all the reference documentation for the TI is decades old, there wasn't a single tutorial for the obligatory "Hello World", so I had to make do with what I could find. By stitching together bits and pieces from various reference manuals and tutorials, I was able to cobble together my first ever Assembly program on my beloved first computer! (Only took me about 30 minutes!)
Holding my breath, I invoke the assembler. A few seconds and grinds of the massive floppy drive later, I see:
Next, I load up the object file and execute it, and I am presented with the following screen:
Success! I pat myself on the back for taking the first step toward accomplishing this little personal challenge.
Here's another screenshot with a bit of code. Working in a 40x24 character screen sure feels restrictive!
For those who are interested, I've included below a source listing and an explanation of the program. There are some pretty interesting facts about the TI which make themselves apparent in the code, as I'll explain after the listing.
(Comments added after the fact.)
One of the most important things about the TI is that it doesn't actually have very many hardware registers. All your working registers are held in RAM, in a space known as the "Scratch Pad". Because of this, tracing through jumps and function calls can sometimes be confusing, but to over simplify it, any time the TI executes a "context switch", it allocates a new space of 32 contiguous bytes to use as the new scratch pad, and stores the return address in R11 of the new scratch pad. In my code above, when we enter the program, R11 holds the value of the next instruction address that the OS expects us to return to after completion of our program. So I save this in a dedicated word of memory to restore later. Then, I use the 32 bytes of memory I allocated toward the beginning of the listing as a fresh new set of registers to work with.
Another interesting point is how you call routines in the computer's ROM. The OS provides a reference table with the names of many routines, from reading and writing to video memory, to floating point calculations, to I/O instructions for interfacing with any expansion peripherals that might be installed. You must list all the routines you wish to call so the assembler can look up these addresses for you and assemble them into the object code.
It's a neat architecture, and fun to play with. I'm really looking forward to getting my hands even more dirty with TI Assembly in the near future! If you all are interested in seeing what comes out of this endeavor, be sure to check back with this blog!
Since all the reference documentation for the TI is decades old, there wasn't a single tutorial for the obligatory "Hello World", so I had to make do with what I could find. By stitching together bits and pieces from various reference manuals and tutorials, I was able to cobble together my first ever Assembly program on my beloved first computer! (Only took me about 30 minutes!)
Holding my breath, I invoke the assembler. A few seconds and grinds of the massive floppy drive later, I see:
0000 ERRORSappear on my screen. Wow! First try! I wasn't expecting that.
Next, I load up the object file and execute it, and I am presented with the following screen:
Success! I pat myself on the back for taking the first step toward accomplishing this little personal challenge.
Here's another screenshot with a bit of code. Working in a 40x24 character screen sure feels restrictive!
For those who are interested, I've included below a source listing and an explanation of the program. There are some pretty interesting facts about the TI which make themselves apparent in the code, as I'll explain after the listing.
(Comments added after the fact.)
DEF HELLO defines the entry point to the program REF VMBW,KSCAN looks up the addresses of ROM routines from the OS's symbol table MSGTXT TEXT 'HELLO WORLD!' allocates space for a text message MSGLEN EQU 12 equates the symbol 'MSGLEN' to the number 12 SCRLIN BSS 32 allocates a Block Starting with Symbol 'SCRLIN' of 32 bytes in length WRKSPC BSS 32 allocates another block of 32 bytes for the workspace registers. STATUS EQU >837C equates the location of the status register with the symbol 'STATUS' SAVRTN DATA >0000 allocates a word of memory for the return pointer KEYADR EQU >8374 locations in memory of the results of the Key SCAN routine. KEYVAL EQU >8375 (I ended up not using these, but they're needed if you wish to determine which key was pressed.) ANYKEY BYTE >20 Allocates a byte of memory to hold a bit mask which will tell us if any key was pressed later. HELLO MOV R11,@SAVRTN ENTRY POINT! Save the old return pointer for safe keeping LWPI WRKSPC load the workspace pointer, pointing to our allocated block of memory LI R1,SCRLIN load some values into the registers LI R2,32 CLRWRD CLR *R1 clear the word pointed to by R1 INCT R1 increment r1 by two DECT R2 decrement r2 by two JNE CLRWRD jump to CLRWRD if r2's new value is not zero LI R0,0 load some more values into the registers LI R1,SCRLIN LI R2,32 LI R3,24 CLRLIN BLWP @VMBW call the OS Video Multi Byte Write routine to write a line of null chars to the screen (Blanking that line) AI R0,32 add 32 (the length of a screen row) to R0, effectively moving to the next line. DEC R3 decrement R3 JNE CLRLIN jump to CLRLIN if r3's new value is not zero LI R0,4*32+5 set print position to row 5, column 6 LI R1,MSGTXT load some more registers LI R2,MSGLEN BLWP @VMBW print the message 'HELLO WORLD!' to the screen KEYSCN CLR @STATUS clear the status register BLWP @KSCAN call the OS's Key SCAN routine CB @ANYKEY,@STATUS use our bit mask on the status register to see if the OS reports any key has been pressed JNE KEYSCN if no key has been pressed, jump to KEYSCN and scan again MOV @SAVRTN,R11 Cleanup. Restore the return pointer to R11 CLR @STATUS Clear the status register (Lets the OS know everything went ok.) RT Return control of the computer to the OS END HELLO End program, indicate to the loader program that HELLO should be automatically entered upon loadingI should point out that in TI Assembly, hex numbers are prefixed with the '>' symbol, as in the line: STATUS EQU >837C
One of the most important things about the TI is that it doesn't actually have very many hardware registers. All your working registers are held in RAM, in a space known as the "Scratch Pad". Because of this, tracing through jumps and function calls can sometimes be confusing, but to over simplify it, any time the TI executes a "context switch", it allocates a new space of 32 contiguous bytes to use as the new scratch pad, and stores the return address in R11 of the new scratch pad. In my code above, when we enter the program, R11 holds the value of the next instruction address that the OS expects us to return to after completion of our program. So I save this in a dedicated word of memory to restore later. Then, I use the 32 bytes of memory I allocated toward the beginning of the listing as a fresh new set of registers to work with.
Another interesting point is how you call routines in the computer's ROM. The OS provides a reference table with the names of many routines, from reading and writing to video memory, to floating point calculations, to I/O instructions for interfacing with any expansion peripherals that might be installed. You must list all the routines you wish to call so the assembler can look up these addresses for you and assemble them into the object code.
It's a neat architecture, and fun to play with. I'm really looking forward to getting my hands even more dirty with TI Assembly in the near future! If you all are interested in seeing what comes out of this endeavor, be sure to check back with this blog!












nice one