Beginning TI-82 Assembly Programming, Part I

by Doug Torrance

updated for Ash v3.0 and CrASH v1.6
In this column:

I wrote two articles quite awhile ago concerning programming in TI-82 assembly language. They dealt mainly with programming for Ash v3.0, still in widespread use today, and OS-82 v1.1, which has since fallen out of use. It has been replaced by CrASH, now in version 1.6, which is much more compatible with Ash, and boasts many new features. So I've decided to update these two articles for these two shells, and hopefully continue the series.

Starting out

You will need the following:

Okay, so you open up your text editor. You're staring at a nice empty screen. Let's start with Ash. It's the older shell, it's first alphabetically, so it makes sense. The first lines in Ash programs will always be:

	#include "ti82.h"
	#include "keys.inc"
	.org START_ADDR
	.db "Description",0
Okay, so what's this stuff mean? Well, both ti82.h and keys.inc are files which come in the Ash zip file. They make life much easier for programmers. Without ti82.h, we would have to remember the actual addresses of certain locations in the calculator's memory. Instead, ti82.h assigns these addresses nice, easy to remember names. Keys.inc is not essential, just handy for dealing with reading keypresses. We'll get more into that in the next lesson. START_ADDR is one of those addresses that ti82.h assigns a name for. It is actually $9104 in the TI-82's memory, which happens to be where the program starts after Ash relocates it. Eh? What's this relocate stuff? It's something else that makes our lives much easier as programmers. When you run a program from Ash, it moves the program to START_ADDR, so the program knows where it is. In earlier versions of Ash and in OS-82, you had to do extra calculations to find out where it was. The .db "Description",0 is simply the description that is displayed in the Ash menu before you run a program. So if you used "Hello",0 as your description (like our first program which we'll get to in a little bit), you'd see the following:

"Description",0 ends in a zero so the calculator knows when the string is over when it is writing it.

Okay, so know you know how an Ash program starts. Crash programs start quite a bit differently, actually. But the differences between the shells basically stop there. They are extremely similar. So here's how the same program would start off in Crash:

	#include crash82.inc
	.db "Description",0
Wow's it's shorter! Yup, crash82.inc includes all the information in keys.inc, so you don't have to include that. Also, the .org START_ADDR is already done for you in crash82.inc. So that gets rid of a step too. So basically, it's exactly the same as the first four lines of an Ash program, only that crash82.inc takes care of three of them.

And for OS-82 programs:

#include "ti-82.h"
.org 0
.db "Description",0

Binary, Decimal, and Hexadecimal

When writing assembly language, you end up working with a 3 different bases of numbers, base 2, our old friend base 10, and base 16.

Base 2, called binary, is made of only 0's and 1's. So if you count up, you get 0, 1, 10, 11, 100, 101, 110, 111, etc. We notate binary numbers in our source code by typing a % before the number, such as %10101100. Binary numbers are the basis of computers, because they represent only two things, on and off. So everything is broken down into binary.

Base 10, or decimal, is what we know and love from our everyday lives. Shouldn't be too confusing. Also, we don't have to put anything in front of the number in our code. Nice and normal.

Base 16, or hexadecimal, is a very handy way of working with bytes. A byte is comprised of 8 bits, each of which can be represented by a binary number. But it can also be represented by only 2 hexadecimal numbers. Hexadecimal numbers are what we work much of the time. When you count in hexadecimal, you count 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, 10, 11, etc. Hexadecimal numbers are notated with a $ before them, such as $4c09

A handy way of converting these numbers is using Windows Calculator in scientific mode.

Displaying Text

Okay, there's two basic fonts on your TI-82. There's the standard home screen font and there's the menu font, which is used on the graph screen. If you're familiar at all with TI-BASIC, the home screen font is what you get with the Disp and Output( commands, and the menu font is what you get with the Text( command.

Let's work with the larger font first. To write text onto the screen, we need to call a function located in the calculator's ROM. Since this function is located in different locations on different ROM versions of the TI-82, we use ROM_CALL() to call it. This tells the shell to figure out which ROM version the calculator is and jump to the appropriate place so that we can get our text. For writing the large font, we use ROM_CALL(D_ZT_STR). Okay, that's cool, but how do we tell the calculator where to write the text and what text to write? Well, there's a place in the calculator's RAM that stores the row of the cursor and also the column of the cursor. The row is located at $800c and the column at $800d. They have been given the names CURSOR_ROW and CURSOR_COL by ti82.h and crash82.inc. Easy enough.

So, how do you store values into CURSOR_ROW and CURSOR_COL? This involves registers. They are kind of like variables. There are two main kinds of register, 8-bit and 16-bit. The 8-bit ones store one byte (8 bits) of information, and the 16-bit registers store one word (2 bytes). CURSOR_ROW and CURSOR_COL are both one byte long, so it is easiest to input into them simultaneously by using a 16-bit register and writing both bytes at once. So we use hl, which is the 16-bit register we'll work with the most. Here's our code:

	ld hl,$0a04
	ld (CURSOR_ROW),hl
This will set the cursor to be in the 4th row and the 10th column. I know, it's backwards. When hl writes itself into (CURSOR_ROW), it reverses the order of the bytes. If you write the bytes in individually, it won't be reversed:
	ld a,4
	ld (CURSOR_ROW),a
	ld a,10
	ld (CURSOR_COL),a
Weird, huh? Well, you might be wondering what that "a" is. It's the most common of the 8-bit registers. Since we're only working with one byte at a time, we use a instead of hl. Also, you're probably wondering why it's (CURSOR_ROW) and not just CURSOR_ROW without the parantheses. Well, when something is enclosed in (), that means that we're looking at what is stored in that memory address. If there's no (), just CURSOR_ROW, we're looking at the memory address itself, not what's stored in it. So after we've run the code above, (CURSOR_ROW) would be $0a04, and CURSOR_ROW would be $800c, which is what CURSOR_ROW is defined as in ti82.h and crash82.inc. Also, you can use (CURSOR_POS) instead of (CURSOR_ROW). They're defined as exactly the same thing in ti82.h and crash82.inc.

Okay. We've got that out of the way. One last thing to do before we can finally write the text on the screen, we need to know what text to write! So at the bottom of the program, we're going to define our text.

String:
	.db "Hello",0
Looks familiar, huh? Just like our program description at the top of the page! Another zero-terminated string, so that the calculator knows when the string is over and can stop writing it. We've put the label String right in front of it. Notice that the label is on the far left of the screen and the data is indented. This is true with the entire program. We're going to put all the labels flushed to the left and all our code at least one space in. Now how do we tell the program that this is the string we want? We do this:
	ld hl,Hello
This loads the address of Hello into hl. ROM_CALL(D_ZT_STR) needs to have the address of the string in hl to work. Now we can put everything together:
	ld hl,$0a04
	ld (CURSOR_ROW)
	ld hl,String
	ROM_CALL(D_ZT_STR)

String:
	.db "Hello",0
This should make some sense. First we're telling the calculator where we want the cursor. Then we're telling it what string we want to display. Then finally, we call the ROM function that writes the text onto the screen. Pretty nifty!

Okay, here's two programs that write "Hello" in the upper lefthand corner of the screen, one for Ash and one for Crash:

Ash
	#include ti82.h
	#include keys.inc
	.org START_ADDR
	.db "Hello",0
	
	ROM_CALL(CLEARLCD)
	ld hl,0
	ld (CURSOR_ROW),hl
	ld hl,String
	ROM_CALL(D_ZT_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0

	.end
Crash
	#include crash82.inc
	.db "Hello",0

	ROM_CALL(CLEARLCD)
	ld hl,0
	ld (CURSOR_ROW),hl
	ld hl,String
	ROM_CALL(D_ZT_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0
Okay, besides the differences mentioned at the top, you might be able to catch the one other difference between Crash and Ash. Crash doesn't need the .end statement at the end of the program. That's another thing that's included in crash82.inc and not in ti82.h.

You also probably see some commands you're not familiar with. ROM_CALL(CLEARLCD) is really self-explanatory, it clears the screen. We will get to the Loop section in the next column.

Alright, we can display text in the larger font. Now what about the smaller font? Instead of ROM_CALL(D_ZT_STR), we use ROM_CALL(D_ZM_STR). And instead of CURSOR_ROW and CURSOR_COL, we use CURSOR_X and CURSOR_Y, which correspond to $8215 and $8216 in the calculator's memory. Unlike CURSOR_ROW and CURSOR_COL, which refer which space the cursor is at on the 8 x 16 homescreen, CURSOR_X and CURSOR_Y refer to the actual pixels, so they can range from 0 to 95 in the x direction and 0 to 63 in the y direction. Otherwise, it's identical. So here's our two programs, rewritten to use the smaller font:

Ash
	#include ti82.h
	#include keys.inc
	.org START_ADDR
	.db "Hello",0
	
	ROM_CALL(CLEARLCD)
	ld hl,0
	ld (CURSOR_X),hl
	ld hl,String
	ROM_CALL(D_ZM_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0

	.end
Crash
	#include crash82.inc
	.db "Hello",0

	ROM_CALL(CLEARLCD)
	ld hl,0
	ld (CURSOR_X),hl
	ld hl,String
	ROM_CALL(D_ZM_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0
Okay, let's say we want to show white text on a black background. Then we do the following before we write the text:
	set 3,(IY+05)
The set command makes the 3rd bit of the specified byte, in this case (IY+05), which is a memory address on the 82, equal to 1. To make it equal to 0, thus making the text black on white again, you do the following:
	res 3,(IY+05)
Our final two programs, showing the "Hello" in the large font, white on black.
Ash
	#include ti82.h
	#include keys.inc
	.org START_ADDR
	.db "Hello",0
	
	ROM_CALL(CLEARLCD)
	set 3,(IY+05)
	ld hl,0
	ld (CURSOR_ROW),hl
	ld hl,String
	ROM_CALL(D_ZT_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0

	.end
Crash
	#include crash82.inc
	.db "Hello",0

	ROM_CALL(CLEARLCD)
	set 3,(IY+05)
	ld hl,0
	ld (CURSOR_ROW),hl
	ld hl,String
	ROM_CALL(D_ZT_STR)

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop

String:
	.db "Hello",0
Okay, I think we're finally done with learning how to write stuff on the screen! Now to see your program work!

Running your program

Okay, if you haven't downloaded tasm, Ash, and Crash from the top of the page, do so now. It's easiest if you unzip everything into the same directory. Save your source code as hello.asm. Then if it's an Ash program, go to MS-DOS and type "asm hello", or if it's a Crash program, type "crasm hello". These batch files run tasm and then another program that turns what tasm has assembled into a .82p files. Make sure you have the appropriate shell on your calculator, and send this file to your calc! You've done it! Your first assembly program!

Next column

Getkeys, jumps, and calls.