------ ASM Tutorial 1 ------- For: What you need to know By: Andy_S Original Date: 9/27/97 Last Update: 12/22/97 Version: 1.1
There are several things you should know or do before starting in assembly. First, you should get the two files ti83asm.inc and tokens.inc. These are include files, you will use them in almost every assembly program you write. After you get these, look over the beginning of ti83asm.inc so you can get a general idea of some romcalls. Romcalls are calls to already-made routines located in ROM, that you can use. Another good file to look at is the Z80 Hardware Reference (called z80href.txt). This file gives you an introduction to the registers (which are a kind of permanent variables) and instructions (which are the commands you program). You probably don't understand what it all means now, but it will help you later, when you start learning about the different registers and commands. Second, you should know the way to set up to start an assembly program. All programs start with
.NOLIST #define equ .equ #define EQU .equ #define end .end #include "ti83asm.inc" #include "tokens.inc" .LIST .org 9327h
The defines make the include files work, and the include files let you use the built in calculator routines. The .org 9327h tells the program where to start. All TI-83 programs will start at that place. At the end of the program goes
.endand if you get an error with that, try putting
because the compiler needs to know where the code finishes. The next thing you'll need to do is get a compiler. For the PC, you can download a program called TASM, which will compile your programs. After you compile a program, you need to put it into the TI-83 format, and there is a nice program called 83lnk which will do that for you. (If you are using a Mac, the compiler is called CAZ, but I've heard it doesn't work.)
To compile a program with TASM, first download the zip file and unzip it to one directory. Then you probably want to make a batch file to run everything. So, start up a DOS Command prompt, and change to the directory that you put TASM in. After that, type 'edit asm.bat' (no quotes). You should get a simple editor. Type in this file:
tasm -i -80 %1.asm
and then save and close the file. To compile something after that, all you have to do is type (again no quotes) 'asm progname'. Make sure your file has an extension of .asm, but don't type it as part of the command. After you run that, it will tell you whether there were any compile errors. If not, run 83lnk, by typing:
83lnk xxxxxx.obj xxxxxx.83p XXXXXX
where xxxxxx is your filename (no .asm) and XXXXXX is the name you want it to have on the calculator (it MUST be in capital letters remember!). Then send the 83p file to your calculator and try it out.
Learn how to use the compiler by testing it with a complete source code. When that works, you'll be ready to start your assembly programming.
------ ASM Tutorial 2 ------- For: all about registers By: Andy_S Original Date: 12/23/97 Version: 1.0
Registers are used to hold values. They are like permanent variables, because you can't get rid of them, but they are more than just that. There are really 8 main registers. They are: a, b, c, d, e, f, h, and l. Unfortunately, you can't always just pick the letter you like best at that moment and use that register (but sometimes you can!). Some Z80 instructions require input from certain ones and return values to certain ones.
The register 'a' is the most important. It is also called the accumulator. When you use the command cp (which does a compare), it checks whether your value matches the one in the accumulator.
The registers b, c, d, e, h, and l are by themselves 8 bit registers. This means they can each hold one byte of information (which is any number from 0 to 255). However, if you need more than that, some can be combined to make 16 bit registers (from 0 to 65536). The ones that you can combine are b and c, d and e, and h and l. When you combine them you get the new registers named bc, de, and hl. They are now can be read as one value or the values of each separately. In other words, if you put 2 into b, and 3 into c, then bc is equal to 515. To explain why, you must understand a little about binary.
Binary is a way of writing numbers using only 0 and 1. To read a binary number, you have to look at it like this:
Number you're trying to read: 00100110
Imagine this in your head: 76543210
Now all you have to do is go (usually from the right) and see whether there is a one or zero in each place. If there is a one, take 2 to the power of the number under it that you're imagining. Then add all those answers together. The number above for example would be:
2^1 + 2^2 + 2^5 = 2 + 4 + 32 = 38
and so it is the number 38. Now back to the 2 and 3 example... The number 2 in binary is 00000010 and the number 3 is 00000011. so if you put those next to each other you get
and then you read it the same as before except keep going from 7 to 8 to 9 and so on when you're imagining your numbers. If you do this, you get 515. But notice if you look at them individually, they are still 2 and 3. The 16 bit registers are good for holding memory addresses.
Instead of listing millions of examples you wouldn't understand yet, from now on, it will be noted if a command uses a specific register.
------ ASM Tutorial 3 ------- For: displaying text on the home screen By: Andy_S Original Date: 9/26/97 Last Update: 12/22/97 Version: 1.1
Ok, to make a program to display text on the screen... First, put all the #includes and #defines that you should already know before reading this, and of course
Next, you probably want to clear the screen, so you should put
as the first line in your program, unless you *don't* want to clear the screen, in which case you ignore it. Notice that the line with the .org is aligned on the left and the call isn't. Basically, any line that start with a '.' should have no space in front of it, and all other lines except labels (we'll get to them in a minute) should have some space before them (a tab works fine).
Positioning the text is the next important thing. You want to say where on the screen its going to go. You have a choice of eight rows and sixteen columns. To confuse you some, the numbers go from 0 to 7 for rows and 0 to 15 for columns, instead of going from 1 to 8 and 1 to 16, because row 0 is really row 1 and so on from there. The calculator stores the position of the cursor in 2 bytes called CURROW and CURCOL. The position you want for the row (which is how far down it is) goes in the first, the column goes in the second. However, luckily, they are one right after the other in memory. This means you only have to use one!
For an example, say you want to display text 3 rows down and 4 columns across. To do this you want a 2 (remember how you have to subtract 1) in CURROW and a 3 in CURCOL. To do this you need to use the register hl. So
ld hl,0203h ld (CURROW),hl
would be the next part of the program. This tells the calculator to load the value 0203h (the h means hexidecimal) into hl. Then it loads hl into CURROW. Since hl can hold two bytes, it puts the first in CURROW and the second in the byte after it, which happens to be CURCOL.
After this you need to say what to display. So you load the address of your string, at this point we'll say your string is called "Str". Notice the string is only *called* Str, the string itself is not "Str". (Str itself is actually a label; we're very close to finding out just what a label is.) To load the string's address into hl, you put
as the next line. Easy enough? After that all you have to do is display it. To make it easy, there is just one call you need to do that.
To end the program, you need a line that says
which tells the calculator to go back to regular mode. The only thing left after that is the string itself. To set that up, you need a line like this
Str: .db "assembly",0
in your code. The "Str:" part is a label that says this string will be called "Str". (So *that's* what a label is! Don't forget there is no space before one.) The .db tells the calculator that you are about to need some room for a string. The "assembly" is the string, but can be replaced by whatever you want. The ",0" at the end tells _puts where to stop displaying. Finally, remember that you need
at the bottom so the assembler knows where to stop. Then compile your code. It's done!
Assuming you followed all that, your full source should be:
.NOLIST #define equ .equ #define EQU .equ #define end .end #include "ti83asm.inc" #include "tokens.inc" .LIST .org 9327h call _clrlcdfull ; if you wanted a cleared screen ld hl,0203h ld (CURROW),hl ld hl,Str call _puts ret Str: .db "assembly",0 .end
------ ASM Tutorial 4 ------- For: the commands cp, call, and jp By: Andy_S Original Date: 9/26/97 Last Update: 12/22/97 Version: 1.0
Some people are probably wondering about the commands call and jp, and especially about the letters sometimes seen after them. Usually these letters are z or nz. To make it easier, z stands for zero and n for not. What all that means is another story. So here goes...
If you've programmed in any other language, you probably know about if-then statements. What an if-then statement usually says is if a variable is equal (or not equal) to a value, then "do something". That sounds complex, but an example will help.
if (x = 3) then goto Start
That line (which is not assembly, don't put it in your code!) says: if x is equal to 3 you go to Start. If x is *not* equal to 3, then the program will not go to Start. The assembly equivalent to this is:
ld a,(x) cp 3 jp z,start
which seems weird because one line turned into three. It will make sense however when explained. First, x is loaded into a (also known as the accumulator). The cp 3 line means "compare 3 with a" (notice it always compares your value with what's in the accumulator!) so it checks whether x = 3. The jp z,start means "jump, if zero, to start". This is how the zero fits in: the way comparisons are made is to subtract. So, for example, lets say x *is* equal to 3. The cp statement would then do (without changing values) basically 3 minus 3. This is obviously equal to 0. If x was equal to 4, it would be 4 minus 3. This would not be zero. So, to check whether x is not equal to 3, you could do
ld a,(x) cp 3 jp nz,start
which says "jump if not zero to start". In other words, use z if you want "equal to" and nz if you want "not equal to". Call works the same way, so if you want to make a call, rather than a jump, you would use call z, or call nz. Of course, to make a call you need to know more about what a label is for, and to load from a variable like x you need to know how to create variables. But you will learn that in another lesson.
------ ASM Tutorial 5 ------- For: getkey input By: Andy_S Original Date: 9/29/97 Last Update: 12/22/97 Version: 1.1
Almost every program needs some kind of user input so input is a good thing to know how to do. There are three ways of reading a key.
The first method is to use the romcall _getkey. This method waits for a keypress and returns the value in the accumulator (that is, a). The value returned is a hex value; to find these values, read the table (KEY CODES) TI has on its webpage (http://www.ti.com/calc/docs/83asmkey.htm) on returned values for input. The way you would use _getkey is this:
call _getkey cp 05h jr z,label
where label is where you want the program to go if key 05h is pressed (key 05h happens to be the Enter key), and of course there would be more to the program to say what to do if a different key was pressed. Using this routine causes the calculator to wait for a keypress so it is not helpful if you need to repeat something while waiting for a key. This method would mainly be used for a pause, when the key pressed is not important.
The second method is to use the romcall _getk. This routine does not wait for a keypress. It also returns the value to op2 (op1 and op2 are special locations in memory that many romcalls use as places to return a value) rather than the accumulator. A common way of using this routine is
keyloop: call _getk call _op2toop1 call _convop1 or a jr z,keyloop ret
The _op2toop1 moves the returned value to op1, and the _convop1 puts it in the accumulator, and then you can just do a cp like before. The line "or a" is a way of saying "cp 0", but it takes less memory (You check for zero because _getk returns zero if no key was pressed). The next to last statement repeats the loop if no key was pressed. The values returned with _getk are the same as the getkey command in TI-BASIC. Read the user's manual if you don't know how to find these values.
The loop presented here is not much better than _getkey, because it also waits for a keypress. However, if you take out the lines
jr z,keyloop the routine will check for a key, and return the value in the accumulator, including 0 if no key was pressed, without looping. You can then go on with the rest of your loop, and call the routine again after it is done. This will allow you to keep looking for keypresses and still let the rest of the program to proceed.
The third method, known as direct input, is more complicated and will be discussed in a later tutorial.
------ ASM Tutorial 6 ------- For: displaying text on the graph and home screens By: Andy_S Original Date: 9/30/97 Last Update: 12/23/97 Version: 1.1
Ok, you figured out how to display a string on the home screen (see ASM Tutorial 1 if you didn't). Now you want to know how to display it on the graph screen using the smaller text. The first difference is instead of using CURROW and CURCOL, you use PENCOL and PENROW. Here's the confusing part. This time the column goes first and the row sceond. Again, you only have to use the first one. For example put
ld hl,0929h ld (PENCOL),hl to display text at 10 pixels across and 30 down (remember the first number is 0). Each row and column is one pixel now. Then you have the romcall to actually display the text. This call is call _vputs
Remember, there needs to be a string in hl for _vputs. There are also other methods of displaying text. For example, if you want to display just one character, instead of _vputs, you can use _vputmap. The way you use this is to load the character you want into the accumulator (for example, 68 is the letter 'd') and call _vputmap. Like this
ld a,65 call _vputmap
That will display 'd' at (PENCOL, PENROW). The same method is available for homescreen text using the romcall _putc. This will be displayed at (CURROW, CURCOL). If you want just one blank space on the graph screen, you can use the romcall _vputblank. This just moves the cursor over two pixels.
Another way to display text, instead of having a string with ",0" at the end, there is a romcall for both types of text to have a fixed length. The way you do this on the homescreen is _putps. The length is the first byte, like
ld hl,str call _putps ret str: .db 10, "abcdefghij"
With the graph screen text, it is not the first byte of the string, but the value in the register b, and the romcall is _vputsn.
ld hl,str ld b,10 call _vputsn ret str: .db "abcdefghij"
Of course you have to still tell the program where you want it to display the text. This concludes the main ways of displaying text.