Welcome to the second lesson of the Ash School! Today you'll learn
a little about registers and how to store bytes, words and strings in the
memory. You'll also learn some common instructions so you will understand
the "Hello world!" program you wrote in the last lesson a bit better.
Contents
Registers
In most programming languages (in fact, all other languages I know) you
use variables to store information like bytes, integers, strings and such.
But, in assembly there is no such thing as a variable. So how do you do?
Inside each processor there are a few very quick ways to store information,
the registers. On a PC the most frequently used registers are called ax, bx,
cx and dx and on an Amiga, I think, they're called d0, d1, d2 and so on.
What are they called on a Z80, which is the CPU on a TI-82?
The common registers are called A, B, C, D, E, H and L (There are
other registers, though they aren't important right now). Pretty easy? They
are 8-bits registers, which means that you'll fit a byte in them. Now, how
do you do if you want to store a word for example? You "simply" put the
registers together two and two. There are three register pairs, called BC,
DE and HL. As you probably can figure out, the BC register uses both the B
and C register. Important also is that you can't combine the registers in
another way, ie registers such as BD, HA and so isn't possible. Also
important is that if you store something in the B register, it will be
destoryed when storing something later in the BC register, because they
are the same (sort of).
Since the registers are few you'll need to know how to store things in
the memory. This method is a bit slower, so numbers you often use you should
store in the registers more often. All calculations require that at least one
of the numbers you want to add or sub is stored in a register. Anyway, since
the RAM memory is 32k, how do you know where in the memory it's safe to store
things? You don't want to write to some location in the memory where other
programs are stored because then you might destroy them.
Lucky for us is that there are a few places in the memory that
aren't used by the TI when where not in "basic-mode". One of those places
are the text memory, which starts at address $808F. There you'll fit 8x16
(the text screen size) = 128 bytes. The reason you can use the text memory
as storage is that when writing something to the screen, you write it directly
to the LCD memory, not via the text memory. You don't
have to understand this really, you just need to know that you're able to
store things in the text memory. Other places to store things in is the second
text memory (128 bytes, starts at $8BDF), the graph memory (756 bytes, starts
at $88B8). and the APD backup buffer (756 bytes, starts at $8228). First you
should use the text memory, then the second one, then the APD backup buffer
and last the graph mem. Of course, if you need to store big arrays, it's perhaps
necessary to use the APD backup buffer or the graph mem.
What if you have many variables? How do you do then? 128+128+756+756
= 1768 bytes isn't that much. Then you have to store it directly in
the program. The disadvantage of this is that the program gets bigger
(since you allocate space in the program).
OK, that was a big explanation of this. Now, how do you use it practically?
First you need to now how to explain that you mean a memory address instead
of a word. The way to to this is that you put ( ) around the address, for
example ($80DF). Then the compiler will now that you mean the address
$80DF, not the word $80DF.
LD, the most common instruction
As you saw in the previous lesson, ld is used very often. What does it do?
Not so hard to guess actually. LD stands for Load, which means that you
store the second argument in the first argument. Example: ld a,53 stores
53 in the A register. Now there are a couple of different ways to do this.
A list below show you some ways to use ld.
- ld d,63
- ld bc,$7A3B
- ld ($80DF),a
- ld a,($80F5)
- ld b,(hl)
- ld (de),a
- ld ($8641),hl
You can probably guess what these instructions will do. As you see
I always use the hexadecimal form when I use address location. You should
do that, because that is standard. Another thing you may need to know
about when storing words is that they are stored "backwards". For example
ld bc,$7A3B will put $7A in the C register and $3B in the B registers, not
the other way round. Always when you deal with words the LSB (least significant
byte) is stored first. The last example, ld ($8641),hl, will store L in
($8641) and H in ($8642).
Sadly, there are some rules you must follow when using ld. Some things
that aren't allowed are listed below.
- You are not allowed to store a 16-bit registers in a 8-bit registers
and vice versa.
- You can't load a 16-bit reg with another 16-bit reg. Ld bc,hl isn't
allowed with other words. You have to ld b,h and ld c,l to do that.
- When you use a direct memory allocation, ($80F5) for example, the other
register must be A or a 16-bit register. Ld b,($80F5) is not allowed but
ld bc,($80F5) is allowed. Note that (hl) is not a direct memory
allocation.
- An important thing to know is that (hl) acts exactly like other
8-bit registers (with the exception that the A reg is a bit special). You
could say that it's an 8th register. (which, in fact, it is if you look
at how the opcodes are made) When I say "only registers", I always mean
that (hl) is also possible. Because of that, hl is very often used as a memory
pointer.
I think that covered the registers we've been through so far. When the
other registers are introduced I'll explain how to access them.
Some more instructions
There are a few other instructions that are easy to use that you should
now.
Add
Add does an addition. Simple? Yes, except that there are limitations of
course. The first arg must be the A reg (if you want to make a
8-bit addition) or HL (16-bit addition). If the first arg is A, then
the second arg can be a direct value or a 8-bit register (remember that
(hl) acts like an 8-bit register), not a memory location! If the
first arg is HL, the the second arg must be a 16-bit register! Add
hl,hl is also possible. The answer is always stored in the first
register. Some example of correct additions are listed below.
- add a,7
- add a,(hl)
- add a,c
- add hl,bc
Inc and Dec
Inc and dec are very useful when you want to make small additions or
subtractions. They only have one arg, which is a register, 8-bit or 16-bit.
Worth notifying is that if you want to add a 8-bit reg (excluding A) with
3, it's smarter to Inc that register three times, because if you for example
want to add the C reg with 3, you have to ld a,c \ add a,3 \ ld c,a. That
takes four byte (you'll see that if you look at the opcodes of the instructions.
Very good to have a book with opcodes when programming) but inc c \ inc c \
inc c only takes three bytes. When it comes to 16-bit reg, it's even more
useful to use Inc, especially when you want to add bc with de for example.
Dec works exactly like Inc, but since Sub (see below) is even more limited
than Add, you can use many Dec in a row and still it takes lesser bytes
than a sub would have done.
Sub
As I said, subtraction is even more limited than addition. That's because
you must sub from the A reg. Because of that, you only have one arg,
which can be a 8-bit reg or a byte. But what do you do if you want to make
a 16-bit sub? Well, there is a trick you can use. There are a 16-bit sub,
but it requires that you clears the Carry flag (more about flags in the
next lesson). If you want to sub hl with de, you do like this: or a \ sbc
hl,dr. Or a is the easiest way to clear the carry. What or really does
will be explained in a later lesson.
Back to the "Hello world!" program
Let us first look at the program again.
#include "ti82.h"
#include "keys.inc"
.org START_ADDR
.db "My first program!",0
ROM_CALL(CLEARLCD)
ld hl,0
ld ($800C),hl
ld hl,Text
ROM_CALL(D_ZT_STR)
WaitKey:
call GET_KEY
cp G_MODE
jr nz,WaitKey
ret
Text:
.db "Hello World!",0
.end
You should understand the beginning. The reason why we store $0000
in ($800C) is because that ($800C) holds the current cursor position.
Since you store a word, both ($800C) and ($800D) will be 0. That's good
since ($800C) contains the x-pos and ($800D) contains the y-pos. 0,0 is
the top left corner on the screen.
ROM_CALL(D_ZT_STR) display a zero terminated string with start at (hl).
Then we must first load hl with the correct address. The string we want to
display are stored last in the program (good place for storing constants).
Ld hl,Text loads hl with the address of the string.
The last part may be a bit cryptic, but it waits until the mode key
is pressed. More about those instructions in the next lesson.