Register A (called the accumulator) is most versatile of all Z80 registers. It is used as a sort of general purpose register because of its aptitude towards nearly every kind of situation. The other registers of immediate importance are H, L, D, E, B and C. H and L stand for the high and low registers of the HL register pair. HL is the most versatile of register pairs, in part because of its ability to manipulate indirect addresses.
The most common of all Z80 instructions are the load
instructions, with the syntax: ld register(s), operand where the
operand is either a value or another register whose value is loaded into
the first register. The instruction <ld a,$20> loads the accumulator
with $20 and <ld b,a> loads B with the value in A. If A was $20
before execution of <ld b,a>, now both A and B contain $20. Register
pairs are similar, except that you can’t load register pairs with other
register pairs, or with any registers period. <ld hl,$d2fe>,
<ld ix,$2434>, <ld iy,$8230>, and <ld bc,0> are perfectly valid
instructions. (Note: instructions for the registers SP, PC, I and
R will not be discussed until they are introduced in a later section.)
The HL register pair will function as a pointer for indirect addresses;
for instance: <ld a,(hl)> or <ld h,(hl)> loads the specified
register with the value addressed by HL. You are also allowed to
load into (hl) -- <ld (hl),$88>, for example. So if HL is $c350,
then the byte of external memory at $c350 will be $88 after the execution
of <ld (hl),$88>.
IX and IY are called the index registers,
and are used primarily for indirect addressing. The high registers
of IX and IY are IXH and IYH; the low registers are IXL and IYL.
You generally don’t want to separate the IX and IY register pairs (and
to do so you must use special instructions which are covered in the undocumented
instructions section). Nearly every instruction for HL can be edited
to use IX or IY in place of HL. IX and IY will always take up one
extra byte than HL, though, because the opcodes for these instructions
are the same as the opcode for HL except that IX has a $dd and IY has a
$fd preceding the HL opcode. <ld hl,$8686> has the opcode 218686,
<ld ix,$8686> has the opcode DD218686. IX’s and IY’s indexing
capabilities also take up a second byte if the instructions are used to
indirect address. When you type <ld (ix),a>, you are really saying
<ld (ix+0),a> (<ld (ix-0),a> if you're a pessimist); you can offset
IX with any value from -128 to +127. If you want to load A into an
address 120 bytes below the address IX points to, then your code would
be <ld (ix-120),a>.
One last thing I need mention concerning the use
of indirect addressing: you can not load bytes everywhere to the 64k of
memory. $0000 to $7fff is Read-Only Memory (ROM), meaning
that it is immutable (while it can be read from normally, nothing can be
written to it). $8000 to $ffff is Random Access Memory (RAM),
meaning that you can use it to store information, such as variables, tables,
graphics, sounds and code and is the area of memory on which you can employ
indirect addressing instructions.
Here’s something you can compile and test out on
your calculator, even if you don’t understand it (your anxiety is very
understandable):
#include “asm86.h”
#include “ti86asm.inc”
.org _asm_exec_ram ;.org $d748 (in ti86asm.inc)
call _clrLCD
;clear screen
call _homeup
;move cursor to top
ld hl,string
call _puts
;put the string “Hey, what’s going on?” to cursor position
ret
;end program
string:
.db “Hey, what’s going on?”,0
;comments are written after semicolons
;comments will be ignored by the compiler
.end
.end
I’ll first try to explain both the string and the
uncanny orgasm up at the top. Each instruction is separated from
the edge of the window by some amount of white space. Any
text that is not preceded by white space and is followed by a colon is
called a label. A label is not compiled, so it takes up no
memory; it’s purpose is to help the programmer find an offset in his /
her program without having to calculate the address. When loading
a register pair with a label, you are actually loading it with the address
of the following byte. So, <ld hl,string> is actually the same
as <ld hl,$d756> -- it’s loading HL with a pointer to the string.
$d756 is a rather suspicious number; here’s where
it came from: when a program is compiled, a compiler will calculate the
addresses of all labels according to the initial offset of the program
itself in memory (the compiler must know where in memory our program is
in order to calculate label offsets correctly when the program references
itself). .org (organize) tells the compiler around what address it
needs to arrange the program’s labels. In this case, the labels need
to be offset from _asm_exec_ram.
Let’s try that again: the string begins on
the 14th byte of this program, so in order that the string is located when
this program is on the calculator, we need to know what address it is 14
bytes away from. Because this program starts at _asm_exec_ram (_asm_exec_ram
is an equate defined in ti86asm.inc with a numeric value of $d748 -- the
address where all programs are executed on the calc), the compiler must
understand that HL wants to point to an address 14 bytes subsequent of
$d748 -- the address $d756.
This program also uses the call instruction. These instructions are magic, or at least for now they are ;-)