Beginning TI-82 Assembly Programming, Part II

by Doug Torrance

updated for Ash v3.0 and CrASH v1.6

In this column:

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 when you're using the smaller menu font.
set 1,(IY+05)
This 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. This only applies when you use ROM_CALL(D_ZM_STR). It looks the same when you use ROM_CALL(D_ZT_STR) whether you set this bit or not. It is important that you reset this bit at some point before you exit the program.
res 1,(IY+05)
If you don't do this, there is a possiblity that when other program are run, the calculator will crash. This was a fatal bug in an earlier version of my game Blockbuster. If someone played ZTetris after Blockbuster, their calculator crashed, because I had forgotten to reset the first bit in (IY+05).

GET_KEY and cp

If you have done any TI-Basic programming, you should be familiar with the getKey function. There are three different methods 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, its hexadecimal value, and the alias you can use in your program that is defined in either ti82.h or crash82.inc. Most of them are the same between the two shells, but there are some differences, which are specified.

G_NONE$00NO KEY
G_DOWN$01DOWN
G_LEFT$02LEFT
G_RIGTH
G_RIGHT (Crash only)
$03RIGHT
G_UP$04UP
G_ENTER$09ENTER
G_PLUS$0A+
G_MINUS$0B-
G_TIMES$0C*
G_DIV$0D/
G_POWER (Ash only)
G_CARROT (Crash only)
$0E^
G_CLEAR$0FCLEAR
G_NEG$11(-)
G_3$123
G_6$136
G_9$149
G_RPARAN (Ash only)
G_PARENR (Crash only)
$15)
G_TAN$16TAN
G_VARS$17VARS
G_PERIODE (Ash only)
G_PERIOD (Crash only)
$19.
G_2$1A2
G_5$1B5
G_8$1C8
G_LPARAN (Ash only)
G_PARENL (Crash only)
$1D(
G_COS$1ECOS
G_PRGM$1FPRGM
G_STAT$20STAT
G_0$210
G_1$221
G_4$234
G_7$247
G_COMMA$25,
G_SIN$26SIN
G_MATRIX$27MATRIX
G_XTO$28x,T,theta|
G_ON$29ON
G_STO$2ASTO>
G_LN$2BLN
G_LOG$2CLOG
G_SQR$2DX^2
G_INVERSE (Ash only)
G_INVE (Crash only)
$2EX^-1
G_MATH$2FMATH
G_ALPHA$30ALPHA
G_GRAPH$31GRAPH
G_TRACE$32TRACE
G_ZOOM$33ZOOM
G_WINDOW$34WINDOW
G_YEDIT$35Y=
G_2nd$362nd
G_MODE$37MODE
G_DEL$38DEL

The second method should only be done in Crash. While call GET_KEY merely loads the most recent keypress into the a register, this next function actually waits for you to press a key. And while it is waiting, you can adjust the contrast, press 2nd and ALPHA and see the appropriate cursors light up, and turn off the calculator. If you use this function in Ash and turn of the calculator, your 82 will crash. Ash calls this function ROM_CALL(KEY_HAND). In earlier versions of Ash, before they added relocation, it worked just fine. But with the relocation feature, which was with version 3.0, KEY_HAND became extremely impractical. One of the nice features about Crash is that it fixes this problem. Instead of calling the ROM directly, you call a function in Crash itself which first derelocates the program, then calls the ROM function. So if the calc is turned off (or automatically powers down, which also happens when this function is used), it will be safe from crashing because the program was no longer relocated. To call this function in Crash, use this code.

call CR_KHAND
This also stores a value in the a register. However, it is completely different than the one we would get if we used GET_KEY. And since CR_KHAND reads 2nd and alpha functions as well as just the regular functions of a key, there are three times as many possibilities as GET_KEY for what we end up having in the a register. Following is a table of all these different possibilties, with the name of the key, the function, the decimal value found in the a register, and the alias in crash82.inc. Remember, only use this function in Crash.

K_RIGTH1 RIGHT RIGHT
K_RIGHT1 RIGHT RIGHT
K_LEFT2 LEFT LEFT
K_UP 3 UP UP
K_DOWN4 DOWN DOWN
K_ENTER5 ENTER ENTER
K_CLEAR 6 CLEAR CLEAR
K_DEL7 DEL DEL
K_INS8 DEL INS
K_RCL 9 STO> RCL
K_ENTRY10 ENTER ENTRY
K_BOL 11 LEFT BOL
K_EOL 12 RIGHT EOL
K_PRGM 45 PRGM PRGM
K_ZOOM46 ZOOM ZOOM
K_DRAW47 PRGM DRAW
K_PLOT48 Y= STAT PLOT
K_MATH49 MATH MATH
K_TEST50 MATH TEST
K_VARS52 VARS VARS
K_MEM53 + MEM
K_MATRIX54 MATRIX MATRIX
K_STAT55 STAT STAT
K_Y-VARS56 VARS Y-VARS
K_ANGLE57 MATRIX ANGLE
K_LIST58 STAT LIST
K_CALC59 TRACE CALC
K_QUIT64 MODE QUIT
K_LINK65 X,T,theta LINK
K_GRAPH69 GRAPH GRAPH
K_MODE70 MODE MODE
K_WINDOW73 WINDOW WINDOW
K_YEDIT74 Y= Y=
K_TABLE75 GRAPH TABLE
K_TBLSET76 WINDOW TblSet
K_TRACE93 TRACE TRACE
K_PLUS129 + +
K_MINUS130 - -
K_TIMES131 * *
K_DIV132 / /
K_POWER133 ^ ^
K_LPAREN134 ( (
K_RPAREN135 ) )
K_LBRACK136 * [
K_RBRACK137 - ]
K_STO139 STO> STO>
K_COMMA140 , ,
K_NEG141 (-) (-)
K_PERIOD142 . .
K_0143 0 0
K_1144 11
K_2 145 22
K_3 146 33
K_4 147 44
K_5 148 55
K_6 149 6 6
K_7 150 77
K_8 151 8 8
K_9 152 9 9
K_EE 153 , EE
K_SEMI 154 0 ;
K_A 155 MATH A
K_B 156 MATRIX B
K_C 157 PRGM C
K_D 158 X^-1 D
K_E 159 SIN E
K_F 160 COS F
K_G 161 TAN G
K_H 162 ^ H
K_I 163 X^2 I
K_J 164 , J
K_K 165 ( K
K_L 166 ) L
K_M 167 / M
K_N 168 LOG N
K_O 169 7 O
K_P 170 8 P
K_Q 171 9 Q
K_R 172 * R
K_S 173 LN S
K_T 174 4 T
K_U 175 5 U
K_V 176 6 V
K_W 177 - W
K_X 178 STO> X
K_Y 179 1 Y
K_Z 180 2 Z
K_XTO 181 X,T,theta X,T,theta
K_PI 182 ^ Pi
K_INVERSE 183 X^-1 X^-1
K_SIN 184 SIN SIN
K_ASIN 185 SIN SIN^-1
K_COS 186 COS COS
K_ACOS 187 COS COS^-1
K_TAN 188 TAN TAN
K_ATAN 189 TAN TAN^-1
K_SQR 190 X^2 X^2
K_SQRT 191 X^2 SQRT
K_LN 192 LN LN
K_EX 193 LN e^X
K_LOG 194 LOG LOG
K_TENP 195 LOG 10^X
K_ANS 198 (-) ANS
K_COLON 199 . :
K_QUEST 203 (-) ?
K_QUOTE 204 + "
K_THETA 205 3 THETA
K_LBRACE 237 ( {
K_RBRACE 238 ) }
K_ABS 244 X^-1 ABS
K_L1 245 1 L1
K_L2 246 2 L2
K_L3 247 3 L3
K_L4 248 4 L4
K_L5 249 5 L5
K_L6 250 6 L6
K_n 251 9 n
K_Vn1 252 8 Vn-1
K_Un1 253 7 Un-1

Phew, that's a lot, huh? Try formatting it into HTML, too! :P Anyways, the third method of reading keypresses is to read the keyboard port directly, which is a little advanced for us right now. After a few more lessons, we'll get to that.

Okay, so let's look at a little snippet of our code from the Hello program we wrote in the last column.

Loop:
	call GET_KEY
	cp G_ENTER
	ret z
	jr Loop
Okay, there's our new friend GET_KEY! So what we did was we read the most recent keypress and store that into the a register. We then use the cp command to compare a with G_ENTER, which is the alias for $09 given in ti82.h and crash82.inc. If Enter was the most recent key pressed, then the value of a would indeed be $09. With the cp command, if a is equal to the value we're comparing it to, the zero flag is set. What? The rest of this code is a little hard to understand without learning some more stuff . . .

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". Depending on far away Label is, you'll either use

	jr Label
or
	jp Label
Now, here's where that zero flag I mentioned when we were looking at the code from our Hello program 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. To jump to Label if the zero flag is not set, use
	jr nz,Label

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 encounters 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.

Another little note about call. You probably noticed above that it is call GET_KEY and ROM_CALL(D_ZT_STR). 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, D_ZT_STR'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 finally completely understand the loop used in the last column.

Loop:
        call GET_KEY
        cp G_ENTER
        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). So if [ENTER] was pressed, the zero flag would be set. Ret z is a conditional return, so the program will return to the shell only if the zero flag is set (i.e. [ENTER] was pressed). And the final command, jr Loop, will jump you back to Loop, where it all starts over again.

Well, both of these tutorials are now updated for Ash v3.0 and Crash v1.6. The next column will deal with some new commands.