Site hosted by Angelfire.com: Build your free website today!

Beginning TI-82 Assembly Programming, Part II
By: Doug Torrance

In this column:


The newest shell: CrASH
Some more text from last column
GET_KEY and cp
jr and jp
call and ret

The newest shell: CrASH
Since the writing of the last column, a new shell has been introduced entitled CrASH. Programming it is nearly identical to programming for Ash. However, it is slightly different. The format for a CrASH .asm file is as follows:
#include crash82.inc
.db "Program name here",0

Program code goes here


As you can see, you don't need .org or .end as in programming for Ash and OS-82. Other than that, there are few differences. We will go into those later. Just like the other shells, to compile CrASH programs, you need tasm plus the files that come in the CrASH .zip file (crasm.bat and crashprg.exe).
Some more text from last column
As you recall, when you set 3,(IY+05), text will be written with white text on a black background. However, another bit determines just how much of that black background you see. set 1,(IY+05) will make it so the row of pixels directly underneath the text is also black, centering the text on the black background since there is already a row of black pixels directly above the text. If you do not set this bit, this row of pixels will not be black, and the bottom of your text will look chopped off.
GET_KEY and cp
If you have done any TI-Basic programming, you should be familiar with the getKey function. There are several commands you can use, actually, to duplicate this in assembly. The first is call GET_KEY. This returns the value of the key pressed into the A register. The values are different from those in TI-Basic, however. Here is a table of each key and its hexadecimal value:

+-------------+-------------+-------------+
| 00 NO KEY | 16 TAN | 28 x,T,theta|
| 01 DOWN | 17 VARS | 29 ON * |
| 02 LEFT | 19 . | 2A STO> |
| 03 RIGTH | 1A 2 | 2B LN |
| 04 UP | 1B 5 | 2C LOG |
| 09 ENTER | 1C 8 | 2D X^2 |
| 0A + | 1D ( | 2E X^-1 |
| 0B - | 1E COS | 2F MATH |
| 0C * | 1F PRGM | 30 ALPHA |
| 0D / | 20 STAT | 31 GRAPH |
| 0E ^ | 21 0 | 32 TRACE |
| 0F CLEAR | 22 1 | 33 ZOOM |
| 11 (-) | 23 4 | 34 WINDOW |
| 12 3 | 24 7 | 35 Y= |
| 13 6 | 25 , | 36 2nd |
| 14 9 | 26 SIN | 37 MODE |
| 15 ) | 27 MATRIX | 38 DEL |
+-------------+-------------+-------------+

(taken from 82-VARS.TXT by Mattias Lindqvist and Dines Justesen)
The other method is ROM_CALL(KEY_HAND) (the Ash name) or ROM_CALL(_GETKEY) (the OS-82 name). This pauses the calculator until a key is pressed, and then stores the value into the A register. With KEY_HAND, the contrast can be changed, the calculator can be turned off, and there will be automatic power down just like in normal operation. Also, with the graphlink software, you can send an image of the screen to your computer. This is handy for making screenshots of assembly games. However, in Ash, if you turn the calculator off during this time, when it is turned back on again, your calculator will crash. This is due to the relocation method that Ash uses. Therefore, it's not really recommended to use KEY_HAND with Ash. Because OS-82 does not use relocation, it is fine to use _GETKEY (the OS-82 name). CrASH does use relocation, but it has a built-in feature that will return a program to its old position before calling KEY_HAND. Instead of using ROM_CALL(KEY_HAND), use call CR_KHAND. The values stored in the A register are completely different from the ones used in GET_KEY. Also, when you can press [2nd] and [ALPHA], their respective cursors will appear in the upper left hand corner of the screen. If one of these cursors is lit, a different value will be stored into the A register. The following three tables give the decimal values that are stored into the A register when 1) neither [2nd] or [ALPHA] has been pressed, 2) [2nd] has been pressed, and 3) [ALPHA] has been pressed.


Normal function
---------------
+-------------+-------------+-------------+-------------+
| RIGTH 1 | STAT 55 | ) 135 | 7 150 |
| LEFT 2 | GRAPH 69 | STO> 139 | 8 151 |
| UP 3 | MODE 70 | , 140 | 9 152 |
| DOWN 4 | WINDOW 73 | (-) 141 | x,T,the 181 |
| ENTER 5 | Y= 74 | . 142 | X^-1 183 |
| CLEAR 6 | TRACE 93 | 0 143 | SIN 184 |
| DEL 7 | + 129 | 1 144 | COS 186 |
| PRGM 45 | - 130 | 2 145 | TAN 188 |
| ZOOM 46 | * 131 | 3 146 | X^2 190 |
| MATH 49 | / 132 | 4 147 | LN 192 |
| VARS 52 | ^ 133 | 5 148 | LOG 194 |
| MATRIX 54 | ( 134 | 6 149 | |
+-------------+-------------+-------------+-------------+

2nd function
------------
+-------------+-------------+-------------+-------------+
| RIGTH 12 | STAT 58 | ) 238 | 7 253 |
| LEFT 11 | GRAPH 75 | STO> 9 | 8 252 |
| UP | MODE 64 | , 153 | 9 251 |
| DOWN | WINDOW 76 | (-) 198 | x,T,the 65 |
| ENTER 10 | Y= 48 | . 199 | X^-1 244 |
| CLEAR 6 | TRACE 59 | 0 143 | SIN 185 |
| DEL 8 | + 53 | 1 245 | COS 187 |
| PRGM 47 | - 137 | 2 246 | TAN 189 |
| ZOOM 46 | * 136 | 3 247 | X^2 191 |
| MATH 50 | / 132 | 4 248 | LN 193 |
| VARS 56 | ^ 182 | 5 249 | LOG 195 |
| MATRIX 57 | ( 237 | 6 250 | |
+-------------+-------------+-------------+-------------+

Alpha function
--------------
+-------------+-------------+-------------+-------------+
| RIGTH 1 | STAT 55 | ) 166 | 7 169 |
| LEFT 2 | GRAPH 69 | STO> 178 | 8 170 |
| UP 3 | MODE 70 | , 164 | 9 171 |
| DOWN 4 | WINDOW 73 | (-) 203 | x,T,the 181 |
| ENTER 5 | Y= 74 | . 142 | X^-1 158 |
| CLEAR 6 | TRACE 93 | 0 154 | SIN 159 |
| DEL 7 | + 204 | 1 179 | COS 160 |
| PRGM 157 | - 177 | 2 180 | TAN 161 |
| ZOOM 46 | * 172 | 3 205 | X^2 163 |
| MATH 155 | / 167 | 4 174 | LN 173 |
| VARS 52 | ^ 162 | 5 175 | LOG 168 |
| MATRIX 156 | ( 165 | 6 176 | |
+-------------+-------------+-------------+-------------+

(Also from 82-VARS.TXT)
Actually, there is also another method of detecting keys, and that is by actually communicating with the keyboard port. We'll get into that in a later column.

Okay, that's just great. Now we know what the value of the A register is after we've pressed a key using call GET_KEY or ROM_CALL(KEY_HAND/_GETKEY). What do we now to check and see what's in the A register? Use the cp command. cp will compare the A register with a value or another 8-bit register, and if they are the same, it will set the zero flag. We'll get into exactly what the zero flag is in another column, but right now we'll learn how to use it.

So, let's say that we're using call GET_KEY, and we want to see if [ENTER] has been pressed. Looking at the table above, we see that the hexadecimal value that GET_KEY stores into the A register is $09. So we want to compare the A register with $09:


call GET_KEY
cp $09

If [ENTER] has been pressed, the zero flag will be set. Now to learn how to use the zero flag . . .

jr and jp
jr and jp are jumps, which are very similar to Goto in TI-Basic. They jump to a label somewhere in your program. It is very simple to define a label. You may recall that all the commands and such are spaced in from the left hand margin. Labels, on the other hand, are right on the left hand margin like this:

Label:
asm code here

jr is small and fast, but can only go so far. jp takes up more memory, is slower, but can go really far. So, when in doubt, use jr. However, when compiling your program, if you get an error in tasm saying "Range of relative branch exceeded," you need to change one of your jr's to a jp.

So let's say you want to jump to a label named "Label". All you have to do is jr Label, or if Label is a long way away from where you are, jp Label.

Now, here's where the zero flag comes in. You can also make your jumps conditional. If you use

jr z,Label

you will only jump to Label if the zero flag is set.

jr nz,Label

will jump to Label only if the zero flag is not set.

OS-82 is a little different when it comes to jp. Instead, it uses JUMP_(). So if you wanted to jump to Label and it's to far away to use jr, you would do JUMP_(Label). The conditional jumps are

JUMP_Z()
JUMP_NZ().
call and ret
call is very similar to running a subroutine program from another TI-Basic program, and ret is the same as Return in TI-Basic.
So let's say you have this:

call Subroutine
...
code
...

Subroutine:
...
code
...
ret

When the TI-82 encounter call Subroutine, it will jump to the code at "Subroutine" and run it until it encounters ret. When this occurs, it will jump right back to right after call Subroutine.
Just like the jumps, there are conditional calls and rets: i.e.

call z,Subroutine
call nz,Subroutine
ret z
ret nz.
This is why we have to put ret at the end of an assembly program to return to the shell. The shell used call to run the program, and in order to return to the shell, we have to use ret.
Again, in OS-82, we use CALL_() instead of call. However, we only use this when calling another part of the program. If you call something in the ROM, like GET_KEY, it stays the same (call GET_KEY). The conditionals are

CALL_Z()
CALL_NZ().
Another little note about call. You probably noticed above that it is call GET_KEY and ROM_CALL(KEY_HAND). This is because GET_KEY exists at the same point in all of the different TI-82 ROM versions, so it can be called directly. However, KEY_HAND's location varies depending on your ROM version. For this reason, the shell has to determine which version you have and then jump to the proper location. This is why you have to use ROM_CALL for most, but not all, ROM functions.

Knowing all of this, we can examine the loop used in the last column.


Loop:
call GET_KEY
cp $09
ret z
jr Loop

Okay, we've called GET_KEY, so the A register now contains the value of the last key pressed (according to the first table shown above). cp $09 compares the A register with $09, which is the value that corresponds with the [ENTER] key. So if [ENTER] was indeed pressed, the zero flag would be set. ret z is a conditional return, so only if the zero flag is set (i.e. it was [ENTER] that was pressed), the program will return to the shell. And the final command, jr Loop, will jump you back to Loop, where it all starts over again.

Here are the Hello World programs from the last columns, plus a new one written for CrASH, which demonstrate this loop:

Ash
#include "ti82.h"
.org START_ADDR
.db "Hello",0
ROM_CALL(CLEARLCD)
ld hl,$0000
ld (CURSOR_ROW),hl
ld hl,String
ROM_CALL(D_ZT_STR)
Loop:
call GET_KEY
cp $09
ret z
jr Loop
String:
.db "Hello",0
.end
.end

OS-82
#include "ti-82.h"
.org 0
.db "Hello",0
ROM_CALL(CLEARLCD)
ld hl,$0000
ld (CURSOR_ROW),hl
ld hl,String
ld de,(PROGRAM_ADDR)
add hl,de
ROM_CALL(D_ZT_STR)
Loop:
call GET_KEY
cp $09
ret z
jr Loop
String:
.db "Hello",0
.end
.end


CrASH
#include "crash82.inc"
.db "Hello",0
ROM_CALL(CLEARLCD)
ld hl,$0000
ld (CURSOR_ROW),hl
ld hl,String
ROM_CALL(D_ZT_STR)
Loop:
call GET_KEY
cp $09
ret z
jr Loop
String:
.db "Hello",0

Next column: More commands