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