Cracking Code ZX Spectrum
Cracking Code ZX Spectrum
THE CODE
on the
SINCLAIR ZX
SPECTRUM
?efS°' , &&
J'SteB*.
Pan/Personal Computer News
Computer Library
John Wilson
Introduction 5
1 Chips, Registers and Numbers 7
2 Number Crunching 29
3 Rotating and Shifting 41
4 Making Comparisons and Checking Bits 51
5 Operating Logically 59
6 Block Manipulation 64
7 A Spectrum Monitor 72
8 Program Production 84
9 Using the rom Routines 95
10 Screen and Attribute Handling 121
11 Interrupts on the Spectrum 140
12 Machine Code Miscellany 156
Appendices: 1 Z80 Instructions listed by Mnemonic 207
2 Z80 Instructions listed by Opcode 224
3 Flag operation table 241
4 Spectrum Monitor-Assembler Listing 243
Index 265
Introduction
LET X=X+20*2+1
means set the variable x equal to the correct value of x, plus twenty
times two, plus one. In machine code programming, however, we
have to give more precise instructions at a low level, and specify
each individual operation needed to perform the calculation.
The example above could be broken down to the sequence:
When run, the program very slowly fills the screen with ink. Now try
running an equivalent machine code program:
1 CLEAR 31999
10 FOR x=32000 TO 32014
20 READ a: POKE x,a
30 NEXT x
35 RANDOMIZE USR 32000
40 DATA 33,0,64,1,0,24,54,255,
35,11,120,177,32,248,201
All of you should know that the Spectrum (or any other computer
for that matter) stores data in terms of 'bytes'. A byte is an 8 bit
binary number which can have a decimal value of 0 to 255. In a 48K
Chips, registers and numbers 9
In the binary system we use only two digits, these being 0 and 1. In
order to represent large numbers therefore we can only write in a
series of these two digits.
Remember that the Z80 chip represents information (numbers) in
groups of 8 bits. Each of these bits may be 'off' (i.e. digit 0) or 'on'
(i.e. digit 1). The bits in a byte are numbered 0 to 7, starting from the
right.
In the binary system numbers are broken down in powers of two
(that's why it is also known as the base two system). That is to say we
break them down as factors of units (bit 0), two's (bit 1), four's (bit
2), eight (bit 3), sixteen (bit 4), thirty-two (bit 5), sixty-four (bit 6) and
one hundred and twenty eight (bit 7).
Take for example the binary number 00011001, this represents the
decimal number:
10 Chips, registers and numbers
0*128 (0*2 t 7)
+0*64 (0*2 T 6)
+0*32 (0*2 f 5)
+1*16 (1 *2 14)
+1*8 (1*2 t 3)
+0*4 (0*2 f 2)
+0*2 (0*2 f 1)
+1*1 (1*2 t 0)
25 decimal
65536
OR 64K (1K=1024 bytes)
Chips, registers and numbers 11
The Spectrum uses 16K of this for its basic rom, which is why the
maximum amount of ram in a standard Spectrum is 48K.
When 16 bit values are stored in memory, something strange
happens. Since 16 bit values are made from two bytes and only one
byte can be held in one memory location, it follows that a 16 bit
value must occupy two bytes in memory. The way that they are
stored is that the least significant byte (lsb), which is the right hand
group of 8 bits, is stored in the first address and the most significant
byte (msb), which is the left hand group of 8 bits, is stored in the next
address. It would appear that this was a strange way for the chip
designers to build the Z80 but the reason is that the Z80, like other
common microprocessor chips, has evolved from simpler chips that
did not have any 16 bit operations. These older chips only used 8
bits to address memory and so they could only address a maximum
of 256 bytes (1/4 K). When the newer chips were designed the extra
work involved in storing 16 bit values for addresses etc. was sim¬
plified. This was done by storing the old 8 bit address (the LSB)
followed by the rest of the new 16 bit address. This does cause some
problems for novice machine code programmers but soon you will
understand. The following diagram should simplify the explanation.
Address-^- 10110101
Address+1 10100010
1111111101001011
adding one + 1
1111111101001100
Since -180 is outside the range of -128 to +127 this value could not
be held in a single byte.
Another number system we need to know before we go any
further is the base 16 or hexadecimal system. Base 16 refers to the
fact that this number system has 16 digits:
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
Using the same principle as found in the base two and base 10
systems we break down the number in a hexadecimal system into
multiples of 16. Take, for example, the hex number 20 (h or h after a
Chips, registers and numbers 13
DB 02h,04h
DEFS 100
EQU Equate
This instruction is used to give values to labels. The format is a label,
followed by the EQU, followed by a number:
The above would give the label PLOT the value 22E5 hex.
; Comment
In most assemblers the ; is used in the same manner as the basic rem
to indicate a useful remark or comment. This is very useful because
without helpful comments assemble code is harder to understand
than basic because the operations are less immediately obvious.
Another feature of machine code assemblers is the facility to refer
to memory addresses by means of labels. Instead of entering an
instruction which says 'Jump to Address 31000', we can set a label at
the address 31000. We could assign the label the name 'Fred', for
example, and then give an instruction 'Jump to Address Fred7. This
can greatly simplify our program structure and also enables mean¬
ingful label names to be assigned to sections of code.
16 Chips, registers and numbers
The effect of this program is to remove all the ink from the screen
How it does so is not important currently because it is serving only
to demonstrate how you can get machine code to work without
buying an assembler program.
The org is not a part of the machine code but it shows where in
memory the machine code must be stored. This is the address into
which we will start to poke the data.
To obtain the data for each of the mnemonics above you will need
to look them up in Appendix 2. As an example, the entry for LD HL,
4000H will read:
In order to get the hex for LD HL,4000h the 4000h must be converted
into two bytes and reversed in order (due to the lsb/msb storage
convention explained earlier).
So, LD HL,4000h will assemble to 21 00 40 in hex or 33 0 64 in
decimal. Since we will be using a basic program to poke the code you
will need to calculate the decimal values to be placed in the data
statement. I have calculated the example for you but try to follow
through the procedure to make sure you understand the principles
involved.
Chips, registers and numbers 17
Now to enter this machine code program the following basic pro¬
gram could be used:
Registers
The Z80 cpu has several registers available to the programmer. These
can be used to hold numeric values similar to basic variables but the
programmer is limited to 22 registers. Some of these registers can be
used in pairs to hold 16 bit values. The older chips such as the 6502
are unable to do this. The Z80 registers are referenced by the
names:
From these a, b, c, D, e, h, l can all hold 8 bit values and ix, iy, sp and
pc will hold 16 bit values. Registers sp, pc, f, i and r have specific
functions which will be explained later and are not used for holding
user data. In addition to these there is a second set of a, b, c, d, e, f, h
18 Chips, registers and numbers
and L registers which are usually referred to as A', B', c, d', e, f, h'
and L'. These two sets of registers cannot be used at the same time,
so in order to access the alternate set a special instruction has to be
used, 'exx' (Exchange) which flips from one register set to the other.
(Two exceptions here are the A' and F registers which are exchanged
using EXAF, AF)
As mentioned earlier, some of these 8 bit registers can be paired
off to form one sixteen bit register. The diagram below demons¬
trates how this can be done:
A F A' F'
B C B' C
D E D' E'
H L H' L'
IX
IY
SP
PC
Now let's take a more detailed look at each of the registers that we
have just introduced and their functions.
IXand IYRegisters
These are known as the Index registers. The ix and iy registers are
often used to point to tables of data and are extermely powerful
tools for accessing arrays of data by a method known as indexing.
On the Spectrum great care must be taken before using the iy index
register in your own machine code programs. A number of rom
routines require that iy contains the value 5C3Ah (23610 decimal)
otherwise they will not work correctly. The Interrupt routine also
requires this value to be in iy. Therefore if you must use iy in your
machine code, disable the interrupts and make sure that iy=5C3Ah
before calling any rom subroutines or returning to basic. Disabling
interrupts and rom subroutines are dealt with later in this book.
/ and R Registers
The i or Interrupt register is used in conjunction with a technique
known as vectored interrupt programming. This is the 280's pointer
Chips, registers and numbers 19
7 6 5 4 3 2 1 0
SZXHXPA'NC
The Flag register has 8 bits which can be either high or low (1 or 0).
Each of these bits is set if certain conditions exist, although bits 3
and 5 are not actually used. If you want to see the mnemonics for
each instruction and how the flags are affected you can find them in
Appendix 3.
Carry flag
The Carry flag indicates whether there was an overflow from bit 7 of
a register. It is mostly affected by addition, subtraction or shift
instructions. By overflow we mean that, for example, adding 250 to
250 would give a value of 500. However the maximum value that can
be held in 8 bits is 255 so the actual value left would be 244. Since the
Carry flag would be set we know that the real value is 244+256 (500).
The same applies to 16 bit values where a result would exceed 65535.
Some sample instructions that use the result of this flag are:
RET C; RETURN IF CARRY FLAG SET
jpnc, address; jump to Address if carry not set
N flag
The n flag, know as the add/subtract flag, cannot be used directly by
20 Chips, registers and numbers
PC Register
The pc, or Program Counter is a 16 bit register that holds the address
in memory of the instruction currently being executed.
The SP Register
The sp or Stack Pointer is another 16 bit register. This one points to
the current address at the top of the stack. Unlike the term queue,
which indicates that literally the first item in is the first item out, the
stack is a term used to represent data held in the reverse order, in
which the last item placed will be the first item out. (This is
sometimes known as a lifo 'Last In First Out' list).
Chips, registers and numbers 21
PUSH HL
This means 'push the hl register pair on the stack'. We could now
use the register pair for other calculations if we wanted to, knowing
that we have a copy on the stack. To retrieve data from the stack we
use the instruction:
POP HL
This means 'pop the data on top of the stack into the hl register pair'.
It is important, however, to note the order in which we push and pop
data. For example, if we use the instructions:
PUSH HL
PUSH BC
22 Chips, registers and numbers
POP BC
POP HL
POP HL
POP BC
then it would become apparent that the register pairs had been
changed over. This can be a useful way of moving data within the
chip but care must be taken when using the stack. Problems will
arise when a push or pop instruction is missing because a ret could
pop some data and RETurn to the wrong address. A large proportion
of machine code 'crashes' are caused by programmers wrongly
using the stack in this way. Remember 'Last In First Out'
Let us examine the following code:
LD HL,0
PUSH HL
RET
The first instruction tells the computer to load the hi register pair
with the number 0. The second is the push instruction which places
the hl pair onto the stack and leaves the number 0 on the top of the
stack. The last instruction is the RETurn instruction which retrieves
the last 16 bit number on the stack and places it into the program
counter. Since the top of the stack contains 0 the program will start
to run from address 0000 — Bad news if you have not SAVEd your
program! 1
3E 16 LD A,22
The above instruction means 'load the a register with the value 22
decimal'. It does precisely what it says: it puts the value 22 into the a
register. The two digits on the lefthand side of the operation are its
hexadecimal equivalent, which are POKEd into memory or typed in
using a monitor. (An assembler does automatically.) We can also
load other 8 bit registers with data.
Examples
LD A,289
This is because the number 289 takes more than 8 bits to represent
it. We can however load register pairs with 16 bit numbers.
16 bit LOADS
As we have mentioned before the Z80 chip has the facility for pairing
off registers, a feature which gives access to some powerful 16 bit
commands.
Let us recap which registers can be paired off together:
24 Chips, registers and numbers
AF AF'
BC BC'
DE DE'
HL HL'
You can see from the diagram that the registers (with the exception
of the Accumulator and Flag registers) are paired off in alphabetical
order. The ix, iy, sp and pc registers have not been included in the
diagram as these are true 16 bit registers and are not split into two
like the others.
Let us now take a look at some 16 bit load operations.
21 00 40 LD HL,16384
This means 'LoaD the hl register pair with 16384 decimal' (4000h) If
you look at the hex translation, this time there are 3 bytes to repre¬
sent the instruction. The first is the Op code for 'ld hl' and the last
two are the data. The low part of the data is the second byte and the
high part is the third byte. (Remember that the Z80 stores 16 bit
values in the opposite way to which you would write them!)
Other examples of 16 bit load operations are given below, (hh is
the high byte of a number in hex while ll is the low byte).
01 LL HH LD BC,HHLL
11 LL HH LD DE,HHLL
31 LL HH LD SP,HHLL
DD 21 LL HH LD IX,HHLL
FD 21 LL HH LD IY,HHLL
78 LD A,B
79 LD A,C
6B LD L,E
The first example reads 'LoaD the A register with the B register'. If, for
example, we had the instructions:
78 LD A,B
we would find that the A register would take the contents of the B
register, thus ending up with the value 2.
The Z80 chip does not have 16 bit instructions such as:
F9 LD SP,HL
DD F9 LD SP,IX
FD F9 LD SP,IY
3A 00 40 LD A, (16384)
The instruction above reads 'LoaD the A register with the contents
of the address 16384 (4000h). You can think of it as being similar to
the basic instruction:
LET x=PEEK(16384)
32 LL HH LD (ADDRESS),A
26 Chips, registers and numbers
3E FF LD A,255
32 00 40 LD (16384), A
the first instruction would LoaD the a register with the value 255 and
the second would put the value of this register into the address 16384.
The Accumulator is the only 8 bit register which allows us to do this
kind of addressing. There are no instructions such as:
One way to get over this problem would be to use the instruction:
78 LDA,B ; let A register=B register
32 0040 LD (16384),A ; put A register in 16384
Sixteen bit addressing in this mode is quite extensive; here are some
examples of the instructions allowed.
ED 4B LL HH LD BC,(HHLL)
ED 5B LL HH LD DE,(HHLL)
ED 6B LL HH LD HL,(HHLL) ; most assemblers use the faster form
of this instruction which is 2A LL HH
DD 2A LL HH LD IX,(HHLL)
FD 2A LL HH LD IY,(HHLL)
ED 7B LL HH LD SP,(HHLL)
These instructions are 16 bit load instructions so they read two bytes
from a given address. We could use:
2A 53 SC LD HL,(23635)
which reads 'LoaD the hl register pair with the contents of address
23635(5C53h). This would take the contents of the address 23635 and
place them in the L register (low byte first). Finally it would take the
contents of 23635+1 (i.e. 23636) and place it in the h register.
It is also possible to save the contents of registers at a given
address, as follows:
This instruction will put the value of the l register at the address
16384 and then put the value of h at the address 16385.
The two instructions above would load AA hex at location 16384 and
22 hex at the location 16385.
Now suppose we wanted to load a value into the a register from
an address which we did not directly know. The address can be
worked out from a calculation. We would address that value by a
method known as register indirect addressing. Sounds complicated,
doesn't it? Don't worry, it's all very easy. All this means is that
instead of giving an address directly to load from we have that
address pointed to by a register pair, as you will see.
7E LD A,(HL)
The instruction above reads: 'LoaD the a register with the contents
pointed by the address in the hl register pair'. If hl contained 16384
then the contents of that address would be put in the a register.
It is also possible to save using register indirect addressing, as
follows:
77 LD (HL),A
12 LD (DE),A
36 22 LD (HL),22h ;load 22h at the address in HL
DD RR NN LD r,(IX+nn)
FD RR NN LD r,(IY+nn)
DD RR NN LD (IX+nn),r
FD RR NN LD (IY+nn),r
DD 36 NN dd LD (IX+nn),d
FD 36 NN dd LD (lY-fnn),d
28 Chips, registers and numbers
DD 21 00 60 LD IX,6000h
DD 4E 05 LD C,(IX+05)
DD 36 00 03 LD (IX+00),03
address data
6000 00
6001 02
6002 04
6003 05
6005 06
6006 07
After executing the first line the ix register is pointing to the portion
of RAM/ROM at the address 6000 hex. When the second instruction
is executed, the offset value 05 is added to the value of the ix regis¬
ter, which equals 6005 hex, and the contents of this location are put
into the c register. Thus, the c register will contain the value 06.
Note that the address in the ix register is not changed in any way.
After executing this instruction it merely accesses the contents of
that address. The last instruction:
LD (IX+00),03
So far we have looked at the way the Z80 stores data and how it can
transfer values and control from one address to another. In this
chapter we come to the actual number crunching instructions used
in addition and subtraction. As was pointed out before, the main
advantage of the Z80 chip over other 8 bit microprocessors is that it
can handle 16 bit numbers directly, making addition and subtraction
operations that much easier. To begin with let's take a look at the 8
bit arithmetic instructions.
The two simplest number crunching instructions are dec, 'Decre¬
ment register' and inc, 'iNcrement register'. These two instructions
respectively subtract or add 1 to the value in a specified register. We
are allowed to use single registers a,b,c,d,e,l and H, with these
instructions, so the range of possible commands is:
DEC A INC A
DEC B INC B
DEC C INC C
DEC D INC D
DEC E INC E
DEC H INC H
DEC L INC L
ADD A,r
which means 'Add to the Accumulator the value in register r', where
rcan be any register of a,b,c,d,e,h on.
If we wanted to add numbers directly to the Accumulator we
could use the instruction:
ADD A,N
30 Number crunching
where n is any 8 bit number. So for example, add a,5 would add 5 to
the Accumulator.
We can also use the add instruction in conjunction with some¬
thing known as indirect addressing. The hl register pair contains an
address where the actual number which we wish to add to the
Accumulator is stored:
ADD A,(HL)
LD A,8
LD HL,6000H
ADD A,(HL)
Address Contents
6000h 02
6001h 03
6002h 06
6003h 07
LD A,0
LD IX,6000H
ADD A,(IX+0)
ADD A,(IX+3)
The first and second instructions are simple enough. These set the a
register to zero and the ix register to the address 6000 hex.
Number crunching 31
ADD A,(lX+0)
The instruction above adds the index to the address in the ix regis¬
ter. This new address is then used to point to the data which we wish
to use. Since our index is zero, the address calculated is
6000h+0h=6000h. Therefore the contents are taken from this
address and added to the accumulator, leaving it with a value of 2
after the first add instruction. The second addition is similar but uses
the index 3, which means that the data to be added is stored at the
address 6000h+03h=6003h and has the value 07. When this is added
to the Accumulator the final result will be 9.
Subtraction works on the same registers as the add instruction,
the mnemonic being sub. Again every operation is done on the a
register but the actual format of the mnemonic is slightly different as
it does not actually mention the a register. The operand follows the
sub instruction directly. For example, to add the b register to the a
register we would write:
ADD A,B
SUB B
ADC A,2
the answer left in the a register would be 5+2+1 =8. On the other
hand, if the Carry flag were to be reset we would return with the
answer 7 as with the normal addition.
When it comes to subtraction, we subtract the state of the Carry
flag from the Accumulator. So, if we had 5 in the a register and the
Carry flag was set, the instruction:
SBC A,3
INC BC DEC BC
INC DE DEC DE
INC HL DEC HL
INC IX DEC IX
INC IY DEC IY
INC SP DEC SP
16 bit addition is quite versatile on the Z80. It allows the user to add
(with or without Carry) other 16 bit registers to the hl, ix or iy register
pair. Subtraction, however, is limited to subtracting the registers bc,
de, hl and sp from the hl pair and we only have the use of the
Subtract with Carry instruction.
16 Bit subtraction.
SBC HL,BC
SBC HL,DE
SBC HL,HL
SBC HL,SP
LD HL,0432H
LD BC,0536H
ADD HL,BC
The first and last instructions ex de,hl mean 'exchange the de and hl
registers'. What they actually do is simply to swop the contents of
the de pair for the contents of the hl pair.
As we mentioned earlier, the Add with Carry instruction adc takes
into account the state of the Carry flag. For example, if the Carry flag
were set and we used the instruction :
LD HL,0432H
LD BC,0536H
ADC HL,BC
AND A
C3 00 60 JP 6000H
The above example reads 'jump to the address 6000 hex' and it loads
the program counter with 6000 hex from where it will continue to
execute the machine code.
We can also specify the address to jump to by the register pairs
hl,ix and iy. For example, if we had the instruction :
JP (HL)
This would in effect load the program counter with the hl register
pair. So if the hl pair contained 1601 hex the program would jump to
the address 1601 hex.
In order to implement the equivalent of the basic statement 'if
condition then goto' we have to use something known as condi¬
tional jump instructions. There are eight conditions which can be
identified, all of which are indicated by bits set in the flags register
(F-register). Below we give all the conditional jump statements that
are allowed:
Jump relative
There is another range of jump instructions available on the Z80
known as the jump relative command. This instruction allows us to
specify an offset instead of an absolute address. The offset is a one
byte number and allows us to jump backwards by up to 128 bytes
and forwards up to 127 bytes, counted from the first byte after the
instruction. This is because by using signed integer representation
(see chapter 1) a byte can hold values between +127 and -128. The
actual instruction is written as follows:
28 dd JR dd
18 03 JR 03
00 NOP
00 NOP
00 NOP
3E 04 LD A,4
the code would load the jump past the two nop (No operation)
instructions to the instruction which loads the Accumulator with the
value 4. The displacement 02 is added to the location after the jump
instruction. Since the jump relative instruction is two bytes long the
actual address to which the program is transferred is the address of
the jump relative instruction plus the displacement plush
JR Here
NOP
NOP
Here LD A,4
options. However, these are limited to the testing of the carry and
the zero flags:
DJNZ
The djnz 'DECrement jump on non zero' is an extremely powerful
instruction. It allows the programmer to effect a loop a specified
number of times around a portion of code, very much like the
'for... next' statements in basic. Take a look at the following
machine code program:
LD B,20H
LD HL,5800H
LD A,2
LOOP LD (HU,A
INC HL
DJNZ LOOP
RET
The first instruction ld b,28h loads the b register with the number 20
hex (32 decimal). The B register is used as a loop counter for djnz.
We then load the hl register with the two byte number 5800 hex.
This is the start of the attribute file:
LD HL,5800H
The Accumulator is LOADed with the value 2, the colour code for red
ink, black paper, bright 0 and flash 0. The next three instructions form
the main part of the loop:
LOOP LD (HL),A
INC HL
DJNZ LOOP
38 Number crunching
INC HL
This means 'increment the hl register pair by one' and adds one to
the hl pair so that it points to the next address in the attribute file.
Finally we have:
DJNZ LOOP
RET
The d)nz instruction will subtract one from the b register. If the value
after this subtraction is not zero then it will jump relative to the
address specified. If it is zero then it will go on to the next instruc¬
tion which is a RETurn.
As you can see, djnz is an extremely powerful instruction. It is very
much like having two instructions in one - a subtraction on the b
register and a jump relative on non zero.
Bearing in mind that the djnz instruction uses relative and not
absolute addressing we can only use it if the portion of code we are
looping around is no longer than 128 bytes.
CD LL HH CALL HHLL
Number crunching 39
hh is the high byte of the address and ll is the low byte. It is possible
for calls to be nested, which means that one subroutine may call
another subroutine. If fact the number of nested calls allowed is
limited only by the amount of memory left to the programmer. A
subroutine may also call itself, a function known as recursion which
is too abstruse for us to pursue here in any depth.
Like jump, the call and RETurn instructions also have conditional
counterparts. We can call a subroutine or RETurn from a subroutine
depending on the conditions set in the Flags register:
This is a bit like typing new in basic, so is not very useful unless you
wish to return to basic from a machine code program and protect the
routine from prying eyes.
RST 08H
DB 08
This routine updates the system variable LAST-K and can be used to
ascertain which keys are depressed. It is called 50 times a second by
BAS|c. It is also sometimes known as the Mode 1 maskable interrupt
routine. We II be hearing more about this routine when we get to
Chapter 11, which deals with interrupts and their uses.
3 Rotating and shifting
Rotating
in F register
RL Rotate left
This instruction rotates the register left through all the nine bits,
wrapping around the carry bit value to bit 0.
The effect of this instruction is to take the sequence of bits in the
byte, add the Carry flag value as bit 8, and then shift all bits one
place to the left. The Carry flag value then goes into bit 0. Thus if we
had (1)01010101 before an rl instruction, we would end up with
(0)10101011. This would produce a result which is the original value
multiplied by two, plus the value of the Carry flag.
flag
RLA RL (HL)
RL A RL (IX+d)
RL B RL (lY+d)
RL C
RL D
RL E
RL H
RL L
Rotating and shifting 43
RR Rotate Right
The Rotate Right instruction has the opposite effect to that of the rl
Rotate left instruction. Bit 0 of the register or byte is rotated to the
right through the Carry, while the old Carry is rotated down to bit 7.
RRA RR (HL)
RR A RR (IX+d)
RR B RR (lY+d)
RR C
RR D
RR E
RR H
RR L
44 Rotating and shifting
Shifting
c Bits
7 6 5 4 3 2 1 0
)
)
)
i
)
Bits 7 6 5 4 3 2 1 0 Bits 7 6 5 4 3 2 1 0
As you can see from the diagram the bottom four bits (bits 3-0) of
the location pointed by the hl register pair are shifted to the top four
bits positions (7-4). The original top four bits are placed in the lower
half of the accumulator with the original contents placed in the
bottom four bits of the ram location. If, for example, we had the hl
pair containing 6000 hex, this byte holding CB hex, and the accumu¬
lator containing 2A:
46 Rotating and shifting
RLD (HL)
We would find that the contents of location 6000h and the accumu¬
lator would be changed to:
The instruction rrd (hl) has the opposite effect, as shown in the
diagram below:
*
' 1 - ~“
Bits 7 0 Bits 7 6 5 4 3 2 1 0
As already indicated, the shift instructions are very useful for multi¬
plying and dividing by powers of two. If, for example, the Accumula¬
tor contained the value three and we had the instructions:
SLA A
SLA A
LD A,128
SLA A
the bit pattern for 128 is 1000000. Therefore when the sla a instruc¬
tion is carried out, the top bit of the Accumulator would be shifted
into the Carry flag. Zero remains in the A register leaving the Carry
flag and Zero flag set. It is easy to write small routines to multiply
Rotating and shifting 47
MULTI 0
SLA A
LD B,A
multiply the Accumulator by two and save the result in the b register.
Remember, the instruction
LD B,A
has no effect on the Accumulator but copies its contents into the b
register. Therefore at this point we have double the original number
in both the a and the b registers.
SLA A
SLA A
ADD A,B
adds the contents of the B register, which contains twice our original
number, to the a register. This leaves the desired answer.
This method of multiplication would only work for numbers in the
range of 0 to 25. Any larger number would result in a number greater
than 255 which we are unable to fit into an eight bit byte. To perform
multiplication on two byte numbers, using shifts, we have to take
into account that a Carry may occur from the lower half of a register.
This must be shifted to the high part. Therefore to multiply the hl
register pair by two we use the instructions:
48 Rotating and shifting
SLA L
RL H ;2*HL
LD E,L
LD D,H ;Save in DE.ie DE=2*HL
SLA L
RL H ;4*HL
SLA L
RL H ;8*HL
ADD HL,DE ;HL=8*HL+2*HL=10*HL
This piece of machine code is much faster and more concise to use
than the previous example. However, this is not always the case.
Suppose we wanted to multiply the hl pair by 128. The first thing that
you thought of was probably to use the series of add instructions.
A lot of instructions!
If we take a look at the bit pattern of a two byte number when we
multiply be 128 we might be able to use shift and rotate instructions
to our advantage. Let's look at the bit pattern we must get in order to
multiply a two byte number by 128:
Rotating and shifting 49
J.J J J J J
T 1 .
bis bi4 b-13 b-12 bn b10 bg b'« b7 b6 [>5 b4 b3 b>2 bi bo
The top seven bits of the low byte need to be shifted into the bottom
seven bits of the high byte. Bit 0 of the low byte will be shifted up to
bit seven and bit seven of the high byte is lost.
If we represent the bit patterns by having hn representing bit n of
the high byte and In to represent bit n of the low byte then before
we perform the multiplication we have the pattern:
h7 h6 h5 h4 h3 h2 hi h0 high byte
17 16 15 14 13 12 II 10 low byte
After multiplying a two byte number by 128 we end up with the bit
pattern:
h0 17 16 15 14 13 12 II high byte
10 0000000 low byte
Notice that the first seven bits of the low byte will always be set to
zero. Looking at the pattern we can see that we can get the new high
byte pattern by shifting the old low byte to the left one. Before we
do this we can put h0 into the Carry flag using the instruction:
SRL H
Now we have bit 0 of the h register i.e. h0 in the Carry flag. We can
now get the pattern we need for our new high byte in the low byte l
register by using the instruction.
RR L
This puts the Carry (containing the old value h0) into bit seven of the
low byte. All the other bits are shifted to the right forcing the carry
into the topmost bit. We now have the pattern we want for the H
50 Rotating and shifting
LD H,L
Finally, we set bit seven of the low byte to the contents of the Carry
which is the bit 10. We do this by first setting the l register to zero
and then rotating the Carry through to bit seven:
LD L,0
RR L
So our code for multiplying the hl register pair by 128 looks like this:
MULT128: SRL H
RR L
LD H,L
LD L,0
RR L
The code is much smaller and faster to use than the series of add
instructions. This portion of code is as much as 50% faster than the
equivalent shown earlier. Arithmetic using bit manipulation is a little
difficult to grasp at first but its implications are enormous. Screen
addresses, for example, can be calculated much faster, giving games
of infinite quality. Therefore it's very worthwhile to take some time
to learn. Meanwhile, I'll end the chapter by giving you a routine to
divide the hl register pair by 128 and let you find out how it works.
DIV128: SLA L
RL H
LD L,H
LD H,0
RL H
4 Making comparisons
and checking bits
CP 128
CP B
JR Z,EQUAL
JR C,BGREAT
LESS A:
The first instruction 'Compare the B register with the A register' sub¬
tracts the b register from the A register. The actual result is not
updated to the accumulator and only affects the flags.
If the a register was equal to the b register then the Zero flag
would be set, causing the program to jump to the address labelled
equal. If the they were not equal, then the program would carry on
to the next instruction:
JR C,BGREAT
If the Carry flag was set this would indicate that the B register was
greater than the Accumulator, causing a branch to the label bgreat. If
no branch occurred this would mean that the b register was less than
the a register, causing the program to arrive at the label lessa.
CP n CP (HL)
CP A CP (IX+dd)
CP B CP (lY+dd)
CP C
CP D
CP E
CP H
CP L
There are other bit instructions in the Z80 set which allow us to set,
reset or test individual bits in a byte.
The SET instruction
The 'set' instruction allows us to set a particular bit in a byte. We can
test individual bits in a register or a ram location. The format of the
set instruction can be any of the following forms:
Making comparisons and checking bits 53
SET n,r
SET n,(HL)
SET n,(IX+dd)
SET n,(IY+dd)
SET 4,A
Address Contents
6000H 22h
6002h 00h
6002h 08h
Then the instruction set 4, (iy+2) would have the following effect:
address contents
6000H 22h
6001H 00H
6002H 18h
BIT 7,A
then the instruction would reset the Zero flag as bit seven is set to 1.
If, however, we used the instruction:
BIT 0,A
with the same contents in the accumulator the Zero flag would be
set, as bit 0 is zero.
The bit instruction is very useful because it does not corrupt
anything we are testing. Similar to the compare instructions, it affects
the bits in the flag registers only.
scans the keys 0 to 6 on the top row of the keyboard. The other
addresses and the keys they scan are given below:
So if we wanted to scan for the bottom row of keys from space to the
letter b we would use the basic instruction:
The IN instruction
In machine code to read a port we use the instruction:
IN A,(port)
The value port is the port address which is a one byte number in the
range 0-255. This port is read and the value is returned in the
Accumulator. How do we use this instruction to scan the various
lines on the keyboard? Well, if you look closely at the address which
you scan in basic to read a particular row you will notice that the low
bytes of each address are all FE hex,254 decimal. The port address
and the high bytes all differ from each other.
To read a set of keys in machine code we first load the Accumula¬
tor with the high byte of the line we wish to read and then execute
the instruction:
IN A,(0FEH)
Now, if we wanted to test if the key 0 was pressed we could use the
instruction:
bit
56 Making comparisons and checking bits
This would set the Zero flag if the key was pressed or reset the flag if
it was not pressed. °
It is an easy matter to read a set of keys by using the compare
instruction:
This portion of code sets the Zero flag if all the keys Q,W and E are
pressed.
There is another form of the in instruction which allows us to
specify the port by the value in the c register. The register in which
the value is read can also be chosen from the set a,b,c,d,e,h on.
The code:
LD C,0FEH
LD A,0FBH
IN E,(C)
reads the line Q to t and places the value read in the e register.
The OUT instruction
The 'out' instruction is often used to generate sound and output to
the cassette system. The port which controls the small pisa speaker
is at the address 0FEh,254 decimal. This also has the ability to change
the screen border colour. The values of output to this port is in the
format shown below:
Speaker Border
7 6 1 0
Unused Microphone
PORT 254
The first three bits (bits 0-2) of the byte are used for the border
colour. Bit 3 is used to control the ear and mic sockets so that data
Making comparisons and checking bits 57
can be sent and read to and from a cassette unit. Bit 4 is used to
pulse the (so-called!) speaker in the Spectrum.
Sound generation on the Spectrum is a simple matter of pulsing
bit 4 of port 254 high and then low for a short period of time. We
would set bit 4 (i.e. to 1) and hold it high for a short time and then
set it low (0) and hold it at this level for the same period of time. The
delay we have between 'flipping' bit 4 of the port determines the
frequency or note we get from the speaker. A long delay produces a
low frequency and a short delay a high frequency. To output the
value in the Accumulator to a port we use the mnemonic:
OUT (addr),A
This simply reads 'ouTput the value in the Accumulator to the port
address' So to turn the speaker on we would use the instructions:
LD A,16
OUT (0FEH),A
Notice how we first load the Accumulator with 16. All this does is to
set bit 4 high, which when sent to the port turns the speaker on. The
following program demonstrates how the out instruction can work
to generate sound. Both the assembler mnemonic listing and a basic
listing have been given, with which the machine code can be
loaded. Line 20 of the basic program changes the low bytes of the
values for the duration and the frequency.
Assembler Listing
ORG 28000D
JP NOISE ;JUMP TO MAKE A NOISE
SOUND;
LD A, 10H ;MASK SPEAKER
;SO BIT 4 IS HIGH
OUT (0FEH), A ;TURN ON SPEAKER
RET
58 Making comparisons and checking bits
DELAY:
LD B,D jTRANFER DE TO
jBC REGISTER PAIR
LD C, E )IE. PLACE DELAY IN
;BC REGISTER
LOOP:
DEC BC ; DECREMENT BC REGISTER PAIR
LD A, B jAND REPEAT
OR C
JR NZ,LOOP {UNTIL BC PAIR IS ZERO
RET {RETURN AFTER FINISHING
j DELAY
NOISE:
LD DE,DELA {GET DELAY
LD HL, DURAT {GET DURATION
BUZZ: CALL SOUND {MAKE A SOUND USING
{DELAY AND DURATION
DEC DE {SUBTRACT ONE OFF DELAY
DEC HL {SUBTRACT ONE OFF DURATION
LD A, L
OR H
JR NZ, BUZZ {REPEAT SOUND UNTIL
{DURATION IS ZERO.
RET
END
AND Operations
This diagram has two switches, labeled a and b, a power supply and a
light bulb. If we take the state of a closed switch as representing 1
and the open state as 0, then in order to switch the light on we have
to have both switches on, i.e. then are both set to 1. We can
represent the possible combination of the switches and their effec¬
tive result on the light bulb by a truth table. The final result is 1 if the
light bulb lights and 0 if it doesn't.
60 Operating logically
As you can see Switch a and switch b have to be closed to make the
light bulb light up.
When we do a logical operation in machine code all the opera¬
tions act on the Accumulator. The and operator is useful for picking
up bits which we want to examine. This is known as 'masking'. If we
had the code:
LD A,01001010B
AND 00000111B
Since the data is in binary we require the 'b' suffix after the numeric
value. The second operation is the and function. It takes each bit of
the data and and's it with the corresponding bit in the accumulator:
01001010
AND 00000111
00000010=2 decimal
OR Operations
As you can see either one of the switches can be on to set the light
on. Now looking at the possible combinations of switching in this
circuit we get the following truth table:
or TRUTH TABLE
A B A ORB
0 0 0
0 1 1
1 0 1
1 1 1
LD A,10101101B
OR 11100000B
The above code would set the top three bits in the Accumulator.
10101101
OR 11100000
11101101=237 decimal
62 Operating logically
OR instruction set
OR n OR (HL)
OR A OR (IX+dd)
OR B OR (lY+dd)
OR C
OR D
OR E
OR H
OR L
XOR Operations
The xor 'exclusive or' operator is a little more difficult to explain
with the aid of a circuit diagram but is just as easy to understand. It is
similar to the or operator but either, not both bits, may be high to
give a high output
A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0
LD A/I
TOC: XOR 1
LD (LIGHT),A
The load instruction turns the light on or off according to the result
in a, so first time round this would turn the light off. We now leave
the light in this state for a period of time using a djnz instruction
which counts from zero to 255 then back to zero again:
JR TOG
CPIR
CPDR
CPI
CPD
CPIR
The a register is LOADed with the value which we are searching for
(the 'key'). The hl register pair is LOADed with the address of the start
of the block we wish to search, and the bc register pair is set up to
contain the number of bytes we want to search through. The cpir
instruction is used to automatically go through all the data, com¬
paring the contents at each address until it either finds the key it is
searching for or until it has exhausted the search. This is signified by
the bc register containing 0.
If the key is found then the Zero flag is set and the hl register pair
points to the next address after the key. So the cpir can be thought
of as three instructions INC hl, cp (hl) and dec bc.
Take a look at the following example:
Block manipulation 65
FOUND: DEC HL
The first line contains the assembler psuedo operator defm 'DEFine
Message' which tells the assembler to place the string "abcdefgh" in
memory when the program is being assembled. As you can see the
hl is LOADed with the start of the string and bc the number of bytes
we wish to search through. The Accumulator contains the key "g",
which we want to seek. The cpir instruction will find this key (as it is
contained within the string) and cause the program to jump to the
address at the label found. At this point we subtract the hl register
by one to point to the actual address where the key is.
CPDR
The cpdr instruction is similar to the cpir instruction but the hl regis¬
ter pair points to the end of the block of data and the search is made
backwards. This time when a key is found the Zero flag is set as
before, but the hl register pair will point to one less than the address
where the key was found.
FOUND: INC HL
As you can see, our code for searching for a key is similar to the last
subroutine searchforwd. However we start off with LOADing the hl
register pair with the address of the end of the string:
LD HL,STRING+8
66 Block manipulation
FOUND: INC HL
CPIandCPD
These are known as semi-automatic instructions. If we use a cpi
instruction then it will compare the a register with the contents of
the hl register. The hl register pair will be incremented and the
contents oftheBC pair will be decremented. Flags will be set accord¬
ing to the result of the comparison and the subtraction of the bc
pair. The two most significant flags to test are the po flag (Parity Odd)
and the Zero flag. If the key is found then the Zero flag is set. On the
other hand if the search is exhausted and the key is not found then
the pat flag is set. These instructions are useful when we are search¬
ing through non-continuous data. For example, if we wanted to
search though a string and the data we are seeking occurs every
three bytes of the string, then we could use the following code:
LD HL,STRING ; SET OF START OF STRING
LD BC,LENGTH ;SET LENGTH
LD A,KEY ; KEY TO SEARCH FOR
LOOK: CPI ;COMPARE (HL) WITH A REG
; DECREMENT BC
;AND INCREMENT HL
JP Z,FOUND ; FOUND KEY
JP PO,NOTFOUND ;EXHAUSTED SEARCH,NOT FOUND
INC HL ;SKIP PASS
INC HL ; UNWANTED DATA
JR LOOK ;KEEP SEARCHING
The routine will exit to the memory address specified by the label
found if the key is in the string or to that given by label notfound if
the key is not in the string. Notice that there are only two inc hl
instructions, not three. This is because the cpi instruction has
already incremented the hl pair.
Block transfer
LDIR
The ldir instruction is used to move data which is held in a continu¬
ous sequence of memory locations. The hl registers are set up to
point to the start of the data block, the de pair is set up to point to
the start of the destination and the bc register the number of bytes
to move. When executing the ldir instruction the contents of the
location pointed by the hl pair is copied to the location pointed to
by the de pair. Both the hl and the de register pairs are incremented
to point to the new data and destination locations while the bc regis¬
ter pair is decremented. This transfer continues until the bc register
pair reaches zero, then the Z80 goes onto the next instruction.
The hl register points to the start of the dummy screen which is C000
hex and the de is set up to point to the start of screen. The bc register
is set up to contain 1B00 hex or 6912 decimal, the number of bytes
Block manipulation 69
The routine will move at least one byte as the test for a zero byte is
made after the ldi instruction. The transfer is complete if the parity
odd flag is set, indicating that we did not encounter a zero byte in
the dummy screen and the bc register reached zero. It will exit when
we reach the first zero byte. The and A is used here to test for zero. A
more obvious method would be CP0 (compare with zero) but and a is
more efficient as it is faster to execute and uses less memory space.
The and A will leave the contents of a unchanged since any bit that is
AND'ed with itself will remain unchanged (see the Truth Table for and
in chapter 5). The and instruction will set the flags according to the
eventual contents of a so this is an easy way of setting flags, ora
would be equally suitable for this purpose but xor a would clear the
A register to zero.
Miscellaneous instructions
The next (and last) batch of five instructions that will be explained in
this book all operate on the Accumulator or flag register, so they
have been grouped
70 Block manipulation
CPL
INC A
This piece of code would leave the result 53 hex in the a register (not
4D hex as with normal addition).
The way that daa works is that after an arithmetic operation it
checks whether the low nybble is in the range 0 to 9. If this is not the
case, it will add 6 to the low nybble, which causes the high nybble to
be incremented. Then the high nybble is checked. If it exceeds 9
then 6 is added to the high nybble, which will overflow into the
Carry flag.
In the example the Accumulator will hold the value 4Dh before
daa is executed.
Here is the flow of logic for daa in this case:
4+1=5
This chapter presents a program which will allow you to write, run
and debug machine code programs. It can be entered into vour
Spectrum using the basic machine code loader given as Listing 1
below. After keying in and SAVEing the basic program on tape, begin
the program by entering the start address at which the machine
code program will start to be built up. Then input the hexadecimal
data which makes up the program as given in the hexadecimal listing
(Listing 2). If at any time you wish to correct a mistake there are edit
facilities to help you (see below). Typing $$ when prompted for a
hexadecimal byte allows you to change the address at which the
next piece of data is to be placed. To quit the program, type in a
double hash ## when prompted for hexadecimal data. After this has
been done the program allows you to store the machine code using
the save command. &
For anyone interested in the way in which the monitor was written
the corresponding assembly mnemonic listing has been included in
Appendix 3. At the moment don't worry about understanding how it
works, just type in the data. Once it is up and working you can use it
to enter the other machine code programs which are given. In this
book each functional program, other than illustrative examples, has
two listings. One is in mnemonic form which is easier to read and
follow. This can be used by those of you that have full assembler
programs available. The second listing is the hexadecimal equiva¬
lent. This is the portion of memory of your Spectrum which holds
the program. It is displayed as a hexadecimal dump and can be
reproduced by keying in the appropriate value for each memory
location using the hex monitor. You could, of course, use the basic
monitor to input the machine code listings. However, as you will see
this is not as powerful as its machine code counterpart.
The monitor program has been assembled at the address 25500
This allows us to write machine code programs higher up in mem¬
ory, giving more free space in which to run both basic and machine
programs. The monitor offers the machine code programmer eleven
functions. These are: Dump, Edit, Fill, Goto, Hunt, Identify, Load
Move, Print, Register and Save.
A Spectrum monitor 73
The monitor should then welcome you with a '>' prompt, inviting
the input of one of the eleven commands.
D address (Hexadecimal dump)
Type in'd' followed by a two byte hexadecimal number. The moni¬
tor then displays the contents of memory from the given address in
a hexadecimal format. The routine will keep dumping the memory
contents until a key is input other than a carriage return. For
example:
>D 0100
>E C000
C000 FF 3E
C001 00 2A
C002 00 C9
C003 00 <ENTER>
Fstart address end addres byte (fill memory with byte value)
To fill a block of memory with a given byte, given the start address,
end address and byte value to where the byte is to be filled. For
example:
This will fill the memory from 4000 hex to 5800 hex with the byte 2A
hex.
>G C000
The above example will cause the monitor to execute from address
C000 and will return back to the monitor after a ret instruction is
met.
This second example will cause the monitor to execute from the
address C000 with a breakpoint at C004. If the code flows through
this address then it will return to the monitor displaying the Break¬
point address and the contents of the registers. Such an example
could be as follows:
*C004
AF BC DE HL IX
2A3E 22 AA 0000 DEF0 DDFE
0000 0000 2232 2312
The above example will search for the byte 2A hex. From the address
0000 to 0100 hex. When the search is exhausted the monitor returns
with the prompt.
>1 SPECMON
The example above will set the identifier to the string 'specmon'.
L start address,'number of bytes (Load file)
The load command will wait for the filename given by the identifier
(see the Y command) and once found on the tape will start to load it
at the given address. The second parameter (also a two byte
number) specifies the number of bytes to load. The load command
will read in each file of the tape and display its header to the screen.
>1 SPECMON
>L C000 0100
Waiting for SPECMON
The commands above will load the file 'specmon' to the address C000
hex. The number of bytes to be LOADed is 256 (i.e. 0100 hex). To exit
from the load command at any time press both the ccaps shift> and
the <break> keys.
M start address, end address, destination address (move Block)
This command will move blocks of data to a given address. You need
to specify the start address, end address and the destination
address.
The above example will move data from the address C000 up to DB00
hex to the address 4000 hex.
P address (printascii)
The print command is similar to the dump command except that it
displays the contents of the memory in ASCII code form rather than
hexadecimal. For example,
>P 0690
will print the ASCII contents from the address 0690 hex. To continue
the listing press <enter>, otherwise press any other key.
Ror R ror R 'r (register modify)
The register command allows you to examine or modify any register
r, where r can be any of the following:
>R ENTER
AF BC DE HL IX
FF3E 0000 0000 F22A 0000
0000 0000 0000 002A
The second value under each register pair is the contents of the
alternative register set. The iy register is not shown as this is used by
the basic system.
To modify a register, you can simply type in the register pair you
wish to modify, after typing in the command 'r'. The monitor will
then display the current value of the register pair, waiting for an
input of the new value. To modify the alternative set, type a ' before
entering the register pair. For example:
This will change the contents of hl register pair to the value 2FFF
hex, and the alternate de register pair to 2A33 hex.
A Spectrum monitor 77
>1 FRED
>S 4000 1B00
Press any key when ready
These will save the 6912 bytes from the address 16384 to the tape.
The file will be SAVEd as 'fred'.
The listings follow below.
10 CLEAR 25499
20 CLS : GO SUB 160
30 LET a=x
40 GO SUB 320: PRINT x$;":";
50 FOR z = l TO B: GO SUB 90: IF
3$="$$" THEN GO TO 10
60 LET a=a+l: NEXT z
70 PRINT
80 LET x=a: GO TO 40
90 INPUT "hex :"; LINE a*: IF
LEN a$<>2 THEN GO TO 90
100 IF a$=,,$*" THEN RETURN
110 IF a*^"##" THEN STOP
120 GO SUB 250
130 IF e=l THEN GO TO 90
140 POKE a,x: PRINT a$;"
150 RETURN
160 INPUT "addr:"; LINE b$: IF
LEN b$<>4 THEN GO TO 160
170 GO SUB 250
180 IF e=l THEN GO TO 160
190 LET x=t*256+x: LET a=x
200 RETURN
210 REM two byte hex input
220 LET a$=b$(l TO 2)
78 A Spectrum monitor
639C C3 36 64 3E FF CD C2 04
63A4 C9 37 3E FF CD 56 05 C9
63AC 11 11 00 DD 21 06 69 AF
63B4 37 CD 56 05 3A 11 69 4F
63BC 3E 24 32 11 69 CD CD 68
63C4 11 07 69 CD 2C 64 3E 20
63CC CD r>r>
A. iL 64 EB 71 23 23 23
63D4 7E CD 92 66 2B 7E CD 92
A Spectrum monitor 79
63DC 66 2B 3E 20 CD 22 64 7E
63E4 CD 92 66 2B 7E CD 92 66
63EC CD CD 68 C9 11 11 00 DD
63F4 21 06 69 AF CD C2 04 C9
63FC E5 C5 D5 CD A7 64 3A 3B
6404 5C CB 6F 28 F9 CB AF 32
640C 3B 5C D1 Cl El C9 CD FC
6414 63 3A 08 5C CD 22 64 C9
641C 3E 02 CD 01 16 C9 F5 F5
6424 AF 32 8C 5C FI D7 FI C9
642C 1A FE 24 C8 CD *■>*? 64 13
6434 18 F6 31 6E 69 CD 1C 64
643C 11 B0 65 CD 2C 64 11 44
6444 65 CD 2C 64 CD CD 68 31
644C 6E 69 3E 08 32 6A 5C 21
6454 6F 69 36 4B 23 36 64 2B
649C 66 83 66 5A 66 83 66 E2
64A4 67 B5 65 01 00 80 11 00
64AC 40 21 00 40 ED B0 C9 CD
64B4 EE 64 CD 8D 66 3E 20 CD
64BC 22 64 7E CD 92 66 3E 20
64C4 CD 22 64 E5 CD B1 66 El
64CC 77 23 CD CD 68 18 E0 CD
64D4 D6 66 E5 3E 20 CD 22 64
64DC CD D6 66 E5 D1 El C9 CD
64E4 D3 64 E5 D5 CD EE 64 D1
64EC El C9 3E 20 CD 22 64 CD
64F4 D6 66 E5 Cl CD CD 68 C9
64FC 3E 20 CD 22 64 CD D3 64
6504 22 40 65 7B B2 CA AE 66
650C ED 53 42 65 11 7D 65 CD
6514 2C 64 11 17 69 CD 2C 64
80 A Spectrum monitor
651C CD AC 63 11 07 69 21 17
6524 69 06 0A 1A CB AF 4E CB
652C A9 B9 20 EC 23 13 10 F3
6534 ED 5B 42 65 DD 2A 40 65
653C CD A5 63 C9 00 00 00
6544 0D 2A 53 42 55 47 2A 20
654C 28 43 29 20 4A 6F 68 6E
6554 20 57 69 6C 73 6F 6E 20
655C 31 39 38 34 2E 0D 24 0D
6564 50 72 65 73 73 20 61 6E
656C 79 20 6B 65 79 20 77 68
6574 65 6E 20 72 65 61 64 79
657C 24 0D 57 61 69 74 69 6E
6584 67 20 66 6F 72 20 24 0D
658C 52 4F 55 54 49 4E 45 20
6594 4E 4F 54 20 49 4D 50 4C
659C 45 4D 45 4E 54 45 44 24
65A4 0D 2A 2A 45 52 52 4F 52
65AC 2A 2A 0D 24 16 01 01 0D
65B4 24 3E 20 CD 22 64 CD D3
65BC 64 22 13 69 7B B2 CA AE
65C4 66 ED 53 11 69 11 63 65
65CC CD 2C 64 CD A7 64 CD A7
65D4 64 CD A7 64 CD 12 64 3E
65DC 03 11 06 69 21 17 69 12
65E4 13 01 0A 00 ED B0 CD F0
65EC 63 CD A7 64 CD A7 64 CD
65F4 A7 64 DD 2A 13 69 ED SB
65FC 11 69 CD 9F 63 C9 E5 CD
6604 92 66 3E 20 CD 22 64 El
660C C9 06 08 7E CD 02 66 23
6614 10 F9 CD CD 68 C9 3E 20
661C CD 22 64 CD EE 64 0E 08
6624 CD 8D 66 3E 20 CD 22 64
662C CD 22 64 CD 0D 66 0D 20
6634 EF CD CD 68 CD CD 68 CD
663C 12 64 FE 0D 28 E0 C9 06
6644 15 7E FE 20 38 04 FE 80
664C 38 02 3E 2E CD 22 64 23
6654 10 EF CD CD 68 C9 3E 20
A Spectrum monitor 81
665C CD 22 64 CD EE 64 0E 08
6664 CD 8D 66 3E 20 CD 22 64
666C CD 22 64 CD 43 66 0D 20
6674 EF CD CD 68 CD CD 68 CD
667C 12 64 FE 0D 28 E0 C9 00
6684 D5 11 8B 65 CD 2C 64 D1
668C C9 7C CD 92 66 7D 5F CB
6694 3F CB 3F CB 3F CB 3F CD
669C A1 66 7B E6 0F C6 30 FE
66A4 3A FA AA 66 C6 07 CD oo
aV> XU
66 AC 64 C9 C3 4B 64 CD 12 64
66B4 CD C8 66 5F CD 12 64 CD
66BC C8 66 CB 23 CB 23 CB 23
66C4 CB 23 B3 C9 A7 DE 30 FE
66CC 0A D8 A7 DE 07 FE 10 30
66D4 D9 C9 CD 81 66 F5 CD B1
66DC 66 6F FI 67 C9 3E 20 CD
66E4 22 64 CD D3 64 3E 20 CD
66EC 22 64 E5 EB A7 ED 52 DA
66F4 AE 66 CA AE 66 E5 Cl El
66 FC E5 D1 13 E5 D5 CD B1 66
6704 D1 El 77 ED B0 CD CD 68
670C C9 D1 21 4A 67 E5 D5 CD
6714 64 67 C9 3E 20 CD 22 64
671C CD D6 66 E5 3E 20 CD 22
6724 64 CD 12 64 FE 0D 28 El
672C FE 2C C2 AE 66 CD D6 66
6734 E5 11 01 69 01 03 00 ED
673C B0 El 36 CD 23 36 7E 23
6744 36 67 CD 64 67 C9 ED 73
674C 04 69 31 01 69 08 D9 E5
6754 D5 C5 F5 D9 08 DD E5 E5
675C D5 C5 F5 ED 7B 04 69 C9
6764 ED 73 04 69 31 EF 68 FI
676C Cl D1 El DD El 08 D9 FI
6774 Cl D1 El 08 D9 ED 7B 04
677C 69 C9 CD 4A 67 El 2B 2B
6784 2B CD CD 68 3E 2A CD 22
678C 64 CD 8D 66 EB 21 01 69
6794 01 03 00 ED B0 CD B4 67
82 A Spectrum monitor
67? C C9 06 04 5E 23 56 E5 EB
67A4 CD 8D 66 3E 20 CD O'?
A*. Am* 64
67AC CD 22 64 El 23 10 EC C9
67 B4 CD CD 68 11 D3 68 CD 2C
67BC 64 21 EF 68 CD 9D 67 CD
67C4 CD 67 CD CD 68 CD 9D 67
67 CC C9 7E F5 23 7E CD 92 66
67D4 FI CD 92 66 23 3E 20 CD
67DC 22 64 CD 22 64 C9 3E 20
67E4 CD O'?
a_ 64 CD 12 64 FE 27
67EC 20 05 CD 12 64 C6 08 21
67F4 2A 68 01 09 00 ED B1 C2
67FC B4 67 2B 11 2A 68 A7 ED
6804 52 11 EF 68 CB 25 1? 23
680C 3E 20 CD 22 64 7E CD 92
6814 66 2B 7E CD 92 66 23 3E
681C 20 CD o'? 64 CD B1 66 77
6824 2B CD B1 66 77 C9 41 42
682C 44 48 58 49 4A 4C 50 3E
6834 20 CD 22 64 CD E3 64 E5
683C A7 ED 52 30 04 El EB 18
6844 F6 EB C5 D5 Cl D1 FI E5
684C A7 ED 52 El 38 03 ED B0
6854 C9 09 2B EB 09 2B EB ED
685C B8 C9 3E 20 CD T1
A- A.. 64 CD
6864 12 64 FE 00 C8 FE 41 DA
686C AE 66 21 17 69 06 0A 0E
6874 20 71 23 10 FC 21 17 6?
687C 06 09 77 05 C8 23 CD 12
6884 64 FE 0D C8 FE 41 DA AE
688C 66 18 EF 3E 20 CD 22 64
6894 CD D3 64 E5 EB A7 ED 52
689C DA AE 66 CA AE 66 E5 Cl
68A4 El 3E 20 CD 22 64 E5 D5
68AC CD B1 66 D1 El BE F5 20
68B4 0D CD CD 68 CD 8D 66 CD
68BC 12 64 FE 0D 20 09 23 0B
68C4 78 B1 28 03 FI 18 E6 FI
68CC C9 3E 0D CD oo
aL. j! 64 C9 41
68D4 46 20 20 20 20 42 43 20
A Spectrum monitor 83
68DC 20 20 20 44 45 20 20 20
68E4 20 48 4C 20 20 20 20 49
68EC 58 0D 24 00 00 00 00 00
68F4 00 00 00 00 00 00 00 00
68FC 00 00 00 00 00 00 00 00
6904 00 00 00 00 00 00 00 00
690C 00 00 00 00 00 00 00 00
6914 00 00 00 00 00 00 00 00
691C 00 00 00 00 00 0D 24 00
6924 00 00 00 00 00 00 00 00
692C 00 00 00 00 00 00 00 00
6934 00 00 00 00 00 00 00 00
693C 00 00 00 00 00 00 00 00
6944 00 00 00 00 00 00 00 00
694C 00 00 00 00 00 00 00 00
6954 00 00 00 00 00 00 00 00
8 Program production
ORG 16384
4000 3E 21 LD A,33
4002 C9 RET
•END
The first instruction 'org' (ORicin) is not a Z80 instruction but is used
to tell the assembler at what address to place the first instruction in
memory. Our example shows that the origin is set at 16384 decimal
so that the first instruction will be assembled at 16384 or4000 hex.
The assembled listing shows mnemonics to the right of the listing
and the address and hexadecimal Op codes to the left. Why are
hexadecimal values displayed and not decimals? Well why not?
Hexadecimal number are used for convenience sake only. They use
fewer digits to represent the decimal numbers 0-255 and are easier
to read . . . well they should be easier to read after a bit of practice!
The first address is 4000 hex which is 16384 decimal. At this
address the Op code for ld a, (load the a register) is placed. The
Program production 85
content of the address 4001 hex (16385) is the data byte 33, This is
shown by its hexadecimal equivalent 21 hex. Since the first instruc¬
tion was two bytes long the next instruction will be placed at the
address 4002 hex or 16386 decimal. This is the RETurn instruction
which is only one byte long and has the value C9 hex or 201 decimal.
The last instruction 'end' is also not aZ80 instruction. This is used by
most assemblers to signify that there are no more instructions to
assemble. Now armed with this information we can now go through a
short assembled listing looking at each instruction and the effect it
has. Also listed is a hexadecimal dump of the program which can be
used with the basic monitor or machine code monitor listed in this
book. Instruction will be given on how to input the machine code
using the basic monitor. First of all let's go through the overall effect
of the program.
The machine code routine enables you to enhance your programs
by having a scrolling attribute border along the edges of the screen. A
coloured border is produced along the screen by POKEingthe attribute
file with random paper colours. Then the routine begins to move the
whole border in a clockwise direction. The whole program comes in
two parts: a basic listing (listing 1) and the machine code (listing 2).
The basic program does the easy work. It draws a random coloured
border along the Spectrum screen. Line 10 sets the variable x to the
address of the start of the attribute file 22528 and this is used to place a
random line of paper colours along the top of the screen. Lines 20 to 40
produce the top border by POKEing a random paper colour (The paper
colour is produced by multiplying a random number from 0 to 7 by 8).
The lines 50 to 100 produce two coloured borders along the side of
the screen and finally, lines 110 to 140 produce the border for the
bottom of the screen. After the border is produced the machine code
routine is executed by the basic line 150 "randomize usr 30000". The usr
command is used by basic to call a machine code routine in memory.
The address following the usr instruction is the address to where basic
will jump. It executes our machine routine which will shift the whole
border clockwise by one attribute. After the machine code routine
has been executed and returns back to basic (by using a ret instruc¬
tion) the line following the basic call will be executed. Line 160 is used
to slow down the scroll by pausing for 1 second. Line 170 jumps back
to 150 to call the machine code routine again and again until the user
breaks out by pressing <capsshift> and <breakspace> together.
Type in the basic program and save it by typing save "demo". DO
NOT run it at this point, as we have not yet typed in the machine code
routine. RUNning the program will probably result in the Spectrum
crashing and losing the basic program!
86 Program production
With the basic program safely savecI on a cassette, you should now
key-in the assembler listing on page 91. To do this you will, of
course, have to load your assembler first. When this listing has been
entered into the Spectrum you should save the source code, again
onto a cassette, using the appropriate command for your assembler.
Next, clear the memory of the machine by switching it off and then
on again.
Then load up the basic 'demo'. Before re-LOADing the machine
code program type in clear 29000. This will re-set ramtop to protect
our machine code program, load the machine code routine from
the tape into the Spectrum by typing load ''democ'' code and
<enter>. After it has LOADed, run the program and we should get a
coloured border scrolling around the screen in a clockwise direc¬
tion. If the program does not scroll the border then probably you
have mis-typed the machine code routine. Re-type the routine again
using the basic monitor.
Now let's look more closely at the machine code routine and see
how it works:
ORG 30000
The first line tells the assembler where the origin of the machine
code is to be assembled. Since our program is to be placed at the
address 30000, this number is placed after the org instruction.
The next instruction is like the org instruction in that it is not a Z80
one. It is used by the assembler to produce a table of strings
(symbols) which hold one or two byte numbers. The string 'equ'
stands for EQuate and produces the string and gives it the value
following the EQuate. In this way the above line will produce the
symbol attradd with the number 5800 hex (22528 decimal).
The symbol attradd now holds the address of the attribute file.
The next two instructions are at the start of the machine code
program. These use the value attradd contained in the assembler's
symbol table. The hl register pair is LOADed with the address of 5800H
Program production 87
plus 31 decimal and the de pair is LOADed with the address 5800H + 30
decimal. The assembler will automatically calculate the two results
and place the address in the source output.
The hl register pair contains the address of the far right hand
corner of the top row of the attribute file. The de register pair holds
the address of the attribute location to the left of the top right hand
corner (it points to the left of the hl register pair).
The object of the first portion of code is to move each of the 31
attributes of the top line of the screen one character along to the
right. This is done by repeatedly replacing the attribute byte pointed
to by the de pair and placing it in the location pointed to by the hl
pair. Then make the two pointers point to the next locations.
The loop dotop, therefore, is used to scroll the top attribute line
from the left to the right:
Before going into this loop we have to save the attribute in the top
right hand corner. This will be over-written with the new attribute to
its left.
The first part of the code ld a,(hl) loads the contents of the
address pointed to by the hl pair. This is the top right hand corner of
the attribute file and places the value into the a register. The second
instruction push af pushes this attribute onto the stack, where it will
stay until we need it. This has the effect of saving the first attribute
onto the stack.
We now set up a loop counter needed to scroll the top line of the
attribute file. This is done by LOADing the b register with the value 31
. is the number of characters we have to scroll across.
decimal, which
The content of the de register pair is LOADed into the a register with
88 Program production
the instruction lda, (de). This instruction is used to force the new
byte to be scrolled to the right. This new attribute is placed to the
right by LOADing it to the address pointed to by the hl pair, per¬
formed by the instruction id (hl) ,a (load into the address pointed by
the hl pair the contents of the a register). The attribute, pointers are
then moved to the left by one attribute by subtracting one from each
of the pointers. The DEcrement instructions dec de and dec hl are
used to implement this. The last instruction of this portion of code is
the branch instruction djnz 'DEcrement and Jump if Not Zero'. This
instruction takes the b register from our column counter and sub¬
tracts one from it. If the result is not zero (if the b register does not
contain a zero) then a relative jump is made to the address dotop.
Remember, a relative jump differs from an absolute jump in that an
offset number of bytes is given to where the program must jump
instead of a two byte address. The assembler has the job of cal¬
culating this offset. It does this by working out the number of bytes
between the lable dotop and the instruction djnz dotop.
After leaving this loop the program has to deal with the coloured
borders along the lefthand side of the attribute file. Since the hl pair
has been decremented 31 times in the loop it will now point to the
start of the attribute file 5800 hex. The de pair will point to the
address 57FFh:
LD B,21
The b register is set with the row counter and the loop doleft is then
entered. This time the hl register pair points to the new attribute
and the de pair points one row up to the old attribute. Like the first
Program production 89
portion of code which dealt with the scrolling, the top row of the
contents of the new attribute address is placed into the old attribute
address. The pointers are then updated to point to the next
attributes. Since we are going down the attribute file we must add an
offset of 32 to both the de and hl register pairs. The bc register pair is
placed onto the stack, which saves the row counter from being
corrupted. The bc pair is then LOADed with 32 which is the offset
needed to point to the next row down. This is added to the hl pair so
that it now points to the next row down. It is then exchanged with the
de pair so that it too can be updated. The instruction ex de,hl (Exchange
the de pair with the hl pair) is used because there is no such
instruction as add de,bc. Therefore we swap the two pairs and update
the other pointer with a second add hl,bc instruction. To restore the
registers to their new values we have to use the Exchange instruction
once more. The row counter is then restored by the instruction popbc
(pop the top of the stack to the bc pair) which is decremented and
tested to see if we have moved 21 bytes in the djnz instruction.
After executing the above loop the de pair points to the bottom
lefthand side of the attribute file. We now need to scroll the bottom
line from the right to the left. Therefore, we need the hl pair pointing
to the attribute to the right of the de pair.
The de pair is first copied into the hl pair by the two instructions
LOADing the high part of the de pair (the D register) into the high part of
the hl pair (the h register). This is performed with the instruction ld
h,d (load into the h register the contents of the d register). Then the
low part is copied by using the instruction ld l,e (load the l register
with the contents of the e register).
90 Program production
Now that the hl pair is also pointing to the bottom left hand
corner, point it to the right of the de pair by incrementing it by one
using instruction inchl (iNcrement the hl pair by one).
The next portion of code is very similar to the dotop but this time we
are scrolling the attributes in the opposite direction. Notice that we
are incrementing the pointers instead of decrementing them.
After scrolling the bottom portion of the attributes we now deal with
the scrolling of the right hand side of the attributes.
First, we adjust the hl register pair to point to one row above the
de pointer. The instruction ld bc,-32 is the one used. The final
portion of code is similar to the loop doleft but this time we are only
scrolling 20 rows as the second row from the top has its new
attribute SAVEd on the stack.
DEC HL
; RE-ADJUST HL BACK ONE
LD BC,—32
;AND MAKE IT POINT TO ONE
ADD HL,BC
; ROW ABOVE DE
LD B,20
;THIS TIME ONLY DO 20 TIMES
;AS LAST ATTRIBUTE IS HELD ON
;THE STACK
DORIG: LD A,(HL) ;GETTHE ATTRIBUTE ABOVE
LD (DE),A ; AND PLACE IT TO THE ATTRIBUTE
; BELOW
PUSH BC ;SAVE ROW COUNTER
LD BC,—32 ;LOAD BC WITH OFFSET
ADD HL,BC ;MAKE HL POINT TO ONE ROW ABOVE
EX DE,HL ;SAVE TEMP IN THE DE PAIR
ADD HL,BC ;MAKE OLD DE POINT ONE ROW ABOVE
EX DE,HL ;AND RESTORE BACK DE AND HL
POP BC ; AS WELL AS THE ROW COUNTER
DJNZ DORIG ; REPEAT UNTIL ALL ROWS DONE
Finally, we 'pop' off the first attribute that we saved and place it to
Program production 91
the fast attribute on the second row of the screen. The RETurn
instruction then returns control back to the basic program.
END
ORG 30000
LD B, 21
92 Program production
LD H, D ;PLACE DE INTO HL
LD L, E
INC HL j HL POINTS ONE TO
;RIGHT OF DE
The rom (Read Only Memory) is a permanent program built into the
Spectrum. It handles the interpretation and execution of basic and
controls the operating system. It is contained in the first 16k of the
Spectrum's memory map and enables you to run and edit basic pro¬
grams. The ROM manages sound, graphics and communication
between the cassette port and the keyboard.
It is worthwhile looking at some of the routines contained in the
rom as these can be used by the programmer when memory space is
scarce. They can also be utilised if a complex arithmetic routine is
needed or a particular function of the Spectrum is to be used. Each
rom routine is found at a particular memory location and we need to
know the correct sequence of operations and the values that must
be placed in selected registers.
The following routine allows the user to print characters to what are
known as streams via channels. A channel is the route by which
input and output are effected to the various devices on a computer.
Examples of such devices on the Spectrum are the keyboard, the
printer and the screen. There are seven channels on the Spectrum,
the most useful of which are given below:
will 'open' that channel. Any PRiNTing done by means of the rom
routines would now send the output to the channel selected.
Therefore, if we wanted to start PRiNTing to the screen we would
open channel 2:
LD A,10
RST 10H ;PRINT AT 10,10;
LD A,16 ;INK
RST 10H
LD A/1 ; LOAD A WITH CODE FOR BLUE
RST 10H ;BLUEINK
LD A,17 ; PAPER
RST 10H
LD A,6 ; YELLOW PAPER
LD A/A'
RST 10 ; PRINT CHARACTER
LD A,2
CALL 1601H ;open channel 2
LD DE,string ;point to string
LD BC,8 ; number of chars in string.
CALL 203CH ;print string.
RET ;and return
string: DB 22,10,10,16,1,17,6/A'
Printing numbers
LD BC,400H
CALL 1A1BH ;print 1024 decimal
RET
This section of code would result in the number 970 decimal being
PRiNTed to the current channel. You may notice that the two bytes
that make up the number are stored in memory in the reverse
manner to that usually used by the Z80 (i.e. MSB, lsb). This is due to
the fine numbers being held the 'wrong way' round in memory.
SCREEN ADDRESSING
Screen addressing routines in the rom are used for PRiNTing and
PLOTting. When we plot a point using the Spectrum basic the screen
is split into a grid of 176 lines by 256 points with the co-ordinate 0,0
starting at the bottom left hand side. There are 16 remaining lines at
the bottom of the screen which are used by the basic operating
system for input, error messages, etc.
These routines may be used if required. However if you wish to
plot or print to any part of the screen, including the 16 'unusable'
lines, I suggest you use my plot routine described in chapter 10 on
the display file.
CHARADD- 0E9BH
This routine can be used to find the address on the screen for a
given character line number 1-24. The first line starts at the bottom
of the screen. The b register is LOADed with the line number of which
we want to find the address. After calling the routine the hl register
pair is RETURNed with the address of the first character line.
LD B,1
CALL 0E9BH ;find address of line 1
PIXADD - 22AAH
This routine is used to find the address of a point on the screen. The
b register is set with Y co-ordinate and the c register with the x
Co-ordinate, the address of which we wish to find. On RETURNing
from the subroutine the hl register pair contains the screen address
and the a register the bit position of the screen which is 0-7. Note
that this is inverted and refers to the bit sequence left to right, not
right to left. Therefore if a returns 0 then the leftmost bit, bit seven is
referred to and if A gives 2 then bit five (the third from the left) is
referred to, etc.
98 Using the rom routines
Assembler Listing
ORG 320000
BEEPER ESU 03B5H
PLAY:
LD IX,FRERE POINT TO START OF MUSIC
GETV: LD L,(IX+0) LOAD L WITH LOW PART
OF THE PITCH
LD H, (IX+1) LOAD H WITH HIGH PART
OF THE PITCH
INC H IF H IS 0FF HEX THEN
END OF MUSIC
RET Z SO RETURN
DEC H RESTORE HIGH PART OF PITCH
LD E,(IX+2) LOAD E WITH LOW PART
OF DURATION
LD D,(IX+3) LOAD D WITH HIGH PART
OF DURATION
PUSH IX SAVE MUSIC POINTER ON
THE STACK
CALL BEEPER CALL BEEPER ROUTINE IN
THE ROM
POP IX RESTORE IX
LD DE, 4 LOAD DE WITH A
ADD IX, DE AND POINT TO NEXT
PIECE OF MUSIC
JR GETV GET NEXT PIECE OF MUSIC
FRERE:
DEFW 66AH
DEFW 105H
DEFW 5B3H
DEFW 125H
DEFW 560H
DEFW 9BH
DEFW 5B3H
DEFW 92H
DEFW 66AH
DEFW 105H
Using the ROM routines 101
DEFW 66AH
DEFW 105H
DEFW 5B3H
DEFW 125H
DEFW 560H
DEFW 9BH
DEFW 5B3H
DEFW 92H
DEFW 66AH
DEFW 105H
DEFW 560H
DEFW 137H
DEFW 4C6H
DEFW 15DH
DEFW 43DH
DEFW 188H
DEFW 560H
DEFW 137H
DEFW 4C6H
DEFW 15DH
DEFW 43DH
DEFW 188H
DEFW 43DH
DEFW 126H
DEFW 3FFH
DEFW 67H
DEFW 43DH
DEFW 0C4H
DEFW 4C6H
DEFW 0AEH
DEFW 560H
DEFW 9BH
DEFW 5B3H
DEFW 92H
DEFW 6&AH
DEFW 105H
102 Using the rom routines
DEFW 43DH
DEFW 126H
DEFW 3FFH
DEFW 67H
DEFW 43DH
DEFW 0C4H
DEFW 4C6H
DEFW 0AEH
DEFW 560H
DEFW 9BH
DEFW 5B3H
DEFW 92H
DEFW 66AH
DEFW 105H
DEFW 66AH
DEFW 105H
DEFW 89AH
DEFW 0C4H
DEFW 66AH
DEFW 20AH
DEFW 66AH
DEFW 105H
DEFW 89AH
DEFW 0C4H
DEFW 66AH
DEFW 20AH
DEFW 0FFFFH
END
Hexadecimal Listing
7D00 DD 21 21 7D DD 6E 00 DD
7D08 66 01 24 C8 25 DD 5E 02
7D10 DD 56 03 DD E5 CD B5 03
7D18 DD El 11 04 00 DD 19 18
7D20 E3 6A 06 05 01 B3 05 25
7D28 01 60 05 9B 00 B3 05 92
7D30 00 6A 06 05 01 6A 06 05
7D38 01 B3 05 25 01 60 05 9B
Using the rom routines 103
7D40 00 B3 05 92 00 6A 06 05
7D4 8 01 60 05 37 01 C6 04 5D
7D50 01 3D 04 88 01 60 05 37
7D58 01 C6 04 5D 01 3D 04 88
7D60 01 3D 04 26 01 FF 03 67
7D68 00 3D 04 C4 00 C6 04 AE
7D70 00 60 05 9B 00 B3 05 92
7D78 00 6A 06 05 01 3D 04 26
7D80 01 FF 03 67 00 3D 04 C4
7D88 00 C6 04 AE 00 60 05 9B
7D90 00 B3 05 92 00 6A 06 05
7D9 8 01 6A 06 05 01 9A 08 C4
7DA0 00 6A 06 0A 02 6A 06 05
7DA8 01 9A 08 C4 00 6A 06 0A
7DB0 02 FF FF 05 01 6A 06 05
7DB8 01 B3 05 25 01 60 05 9B
7DC0 00 B3 05 92 00 6A 06 05
7DC8 01 60 05 37 01 C6 04 5D
7DD0 01 3D 04 88 01 60 05 37
7DD8 01 C6 04 5D 01 3D 04 88
7DE0 01 3D 04 26 01 FF 03 67
7DE8 00 3D 04 C4 00 C6 04 AE
7DF0 00 60 05 9B 00 B3 05 92
7DF8 00 6A 06 05 01 3D 04 26
SAVEBYTES:
LD DE,NBYTES ; number of bytes to save
LD IX, START ; start of block
LD A,0FFH ;savingadata block
CALL SAVEDATA ;save bytes
RET
SAVEHEADER:
LD DE,17 ;save 17 bytes length of header
LD IX,START-OF ;point to start of header info
XOR A ;signify header.A=0
CALL SAVEDATA ;save header
RET
LOADBYTES:
LD DE,NBYTES
LD IX, START
LD A,0FFH
SCF ;signify loading
CALL LOADDATA
RET
LOADHEADER:
LD DE,17
LD IX,START-of-l
XOR A
SCF
CALL LOADDATA
RET
VERIFYBYTES:
LD DE,NBYTES
LD IX,START
LD A,0FFH
AND A ; reset carry flag
CALL LOADDATA
RET
VERIFYHEADER:
LD DE,17
LD IX,START-of-F
XOR A ; reset carry flag and
CALL LOADDATA ;set A=0
RET
Using the rom routines 105
Assembler Listing
ORG 32000
JP START
OUTXNUM:
LD IX,NUMTA 1 POINT TO TABLE
LD DE,NUMB JDE POINTS TO BUFFER
DIGIT! LD C, (IX+0) J GET LOU BYTE OF BASE 10
LD B.<IX+1) {GET HIGH BYTE OF BASE 10
LD A.'B'-l }A REGISTER =30 HEX
AND A ;CLEAR CARRY
FIN:
INC A JCALCULATE NUMBER OF
SBC HL. BC ;MULTIPLES OF TENS UNTIL
JR NC, FIN I CARRY FLAG IS SET
ADD HL, BC jRESTORE NUMBER
LD (DE), A ;PLACE ASCII NUMBER
;IN BUFFER
DEC C iTEST TO SEE IF FINISED
INC DE ;BUMP BUFFER POINTER
JR Z, OUTP ;FINISHED OUTPUT NUMBER
;TO CURRENT CHANNEL
INC IX ;POINT TO NEXT
;MULTIPLE OF 10
INC IX
JR DIGIT iFIND NEXT ASCII DIGIT
OUTP; LD DE, NUMB ;NUMBER BUFFER
LD BC, 5 ;LENGTH OF STRING
CALL PRXSTRING ;PRINT IT!
RET ;AND RETURN
Using the rom routines 107
0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16
HEADER: DS 17
HEADOUT:
LD A, 0 j SET A TO ZERO
LD IX,HEADER iPOINT TO HEADER
LD DE, 17 ;17 BYTES TO
CALL SAVEBTS {SAVE
RET
108 Using the rom routines
SAVECODEs
LD A,0FFH
LD IX, MEM iPOINT TO RAM
LD DE, (LEN) »' GET LENGTH FROM HEADER
CALL SAVEBTS ;AND SAVE
RET
LOADCODE:
SCF ;SET CARRY FLAG
J TO SIGNIFY LOADING
LD A, 0FFH iA REG LOADED WITH
J TYPE OF DATA
LD IX, MEM I POINT TO START OF CODE
LD DE, (LEN) I PUT LENGTH OF CODE INTO DE
CALL LOADBTS ;DO LOADING
RET
CLS:
LD HL,4000H ;CLEAR SCREEN
LD DE,4001H
LD BC, 8*32*24-1
LD (HL), 0
LDIR
LD DE,HOME ;AND PLACE CURSOR
LD BC, 3
CALL PRXSTRING j AT HOME
RET
40ME: DB 22, 0, 0
DISPLAY:
CALL CLS ;CLEAR SCREEN
LD DE,MESS1 ;PRINT FILENAME STRING
LD BC,SIZ1
CALL PRXSTRING
LD DE, FILE
LD BC, 10 ;PRINT
CALL PRXSTRING ;FILENAME
Using the rom routines 109
LD HL,TBASIC
LD A, (TYP)
LD E, A iSAVE TYP IN E REG
SLA A ;2*TY
SLA A ;4*TYP
ADD A, E ;4*TYP+TYP=5*TYF
LD E, A
LD D, 6 ;PUT OFFSET IN DE
ADD HL, DE
j HL POINTS TO STRING
EX DE, HL
LD BC, 5 ;NUMBER OF BYTES TO PRINT
CALL PRXSTRING ; TYPE
START:
LD SP, STACK
LD A, 2 ;OPEN CHANNEL 'S'
CALL 1601H
110 Using the ROM routines
ERRORS:
LD SP,STACK
LD HL, ERRSP ;ERROR STACK
LD < HL), L0W( ERRORS)
INC HL
LD (HL),HIGH( ERRORS)
DEC HL
LD < 23613 ),HL
NEXT:
CALL WAITM j WAIT FOR HEADER MESSAGE
WANT:
LD DE, SAVES {PROMPT FOR ANSWER
LD BC, SIZ8
CALL PRXSTRING
Using the rom routines 111
SAVEMESS:
LD BC, SIZR ;PRINT MESSAGE
KEY:
DEPR:
LD A, (FLAGS) {LOOK AT STATUS OF KEYBOARD
BIT 5, A
JR Z, KEY ;WAIT FOR A KEY
;TO BE PRESSED
RES 5, A
LD (FLAGS), A
LD A, (LASTXK) 1 GET KEY VALUE
RET
DS 100 ;STACK SPACE
STACK: DB 0
ERRSP: DEFW 0 jERROR STACK SPACE
Hexadecimal Listing
7D00 C3 C3 7E 16 01 00 46 49
7D08 4C 45 4E 41 4D 45 3A 20
7D10 16 03 00 50 52 4F 47 S'?
W JL.
7D18 41 4D 20 54 59 50 45 3A
7D20 20 16 06 00 4C 45 4E 47
7D28 54 48 3A 20 16 09 00 53
7D30 54 41 52 54 3A 20 16 0A
7D38 00 20 20 20 20 4D 41 43
7D40 20 57 41 49 54 49 4E 47
7D48 20 46 4F 52 20 48 45 41
7D50 44 45 52 2E 20 16 0D 00
7D58 20 43 4F 50 59 52 49 47
7D60 48 54 20 4A 2E 4E: 20 57
7D68 49 4C 53 4F 4E 20 31 39
7D70 38 33 2E 20 42 41 53 49
7D78 43 4E 55 4D 45 52 43 48
Using the ROM routines 113
7D80 41 52 41 42 59 54 45 53
7D88 16 15 00 44 6F 20 79 6F
7D90 75 20 77 61 6E 74 20 61
7D98 20 63 6F 70 79 3F 16 15
7DA0 00 50 72 65 73 73 20 45
7DA8 4E 54 45 52 20 77 68 65
7DB0 6E 20 72 65 61 64 79 2E
7DB8 16 15 00 20 20 20 20 20
7DC0 20 20 20 20 20 20 20 20
7DC8 20 20 20 20 20 20 20 20
7DD0 20 20 10 27 E8 03 64 00
7DD8 0A 00 01 00 00 00 00 00
7DE0 00 DD 21 D2 7D 11 DC 7D
7DE8 DD 4E 00 DD 46 01 3E 2F
7DF0 A7 3C ED 42 30 FB 09 12
7DF8 0D 13 28 06 DD 23 DD 23
7E00 18 E6 11 DC 7D 01 05 00
7E08 CD 3C 20 C9 00 00 00 00
7E10 00 00 00 00 00 00 00 00
7E18 00 00 00 00 00 37 3E 00
7E20 DD 21 0C 7E 11 11 00 CD
7E28 56 05 C9 3E 00 DD 21 0C
7E30 7E 11 11 00 CD C2 04 C9
7E38 3E FF DD 21 5C 5D ED 5B
7E40 17 7E CD C2 04 C9 37 3E
7E48 FF DD 21 5C 5D ED 5B 17
7E50 7E CD 56 05 C9 21 00 40
7E58 11 01 40 01 FF 17 36 00
7E60 ED B0 11 6C 7E 01 03 00
7E68 CD 3C 20 C9 16 00 00 CD
7E70 55 7E 11 03 7D 01 0D 00
7E78 CD 3C 20 11 0D 7E 01 0A
7E80 00 CD 3C 20 11 10 7D 01
7E88 11 00 CD 3C 20 21 74 7D
7E90 3A 0C 7E 5F CB 27 CB 27
7E98 83 5F 16 00 19 EB 01 05
7EA0 00 CD 3C 20 11 21 7D 01
7EA8 0B 00 CD 3C 20 2A 17 7E
7EB0 CD El 7D 11 2C 7D 01 0A
7EB8 00 CD 3C 20 2A 19 7E CD
114 Using the rom routines
7EC0 El 7D C9 31 CF 7F 3E 02
7EC8 CD 01 16 31 CF 7F 21 D0
7ED0 7F 36 CB 23 36 7E 2B 22
7ED8 3D 5C CD 01 7F CD ID 7E
7EE0 CD 6F 7E CD 46 7E CD 0E
7EE8 7F 20 EF CD 46 7F CD 2B
7EF0 7E CD 37 7F CD 37 7F CD
7EF8 38 7E CD 0E 7F 28 EC 18
7F00 D9 CD 55 7E 11 36 7D 01
7F08 3E 00 CD 3C 20 C9 11 88
7F10 7D 01 16 00 CD 3C 20 CD
7F18 5A 7F FE 59 28 0D FE 79
7F20 28 09 FE 4E 28 04 FE 6E
7F28 20 ED A7 F5 11 B8 7D 01
7F30 1A 00 CD 3C 20 FI C9 CD
7F38 3A 7F 21 00 00 11 00 00
7F40 01 FF FF ED B0 C9 01 1A
7F48 00 11 9E 7D CD 3C 20 CD
7F50 5A 7F FE 0D 20 F9 CD 2B
7F58 7F C9 00 3A 3B 5C CB 6F
7F60 28 F8 CB AF 32 3B 5C 3A
7F68 08 5C C9 00 00 00 00 00
7F70 00 00 00 00 00 00 00 00
7F78 00 00 00 00 00 00 00 00
7F80 00 00 00 00 00 00 00 00
7F88 00 00 00 00 00 00 00 00
7F90 00 00 00 00 00 00 00 00
7F98 00 00 00 00 00 00 00 00
7FA0 00 00 00 00 00 00 00 00
7FA8 00 00 00 00 00 00 00 00
7FB0 00 00 00 00 00 00 00 00
7FB8 00 00 00 00 00 00 00 00
7FC0 00 00 00 00 00 00 00 00
7FC8 00 00 00 00 00 00 00 00
7FD0 00 00 FE 0D 20 F9 CD 2B
7FD8 7F C9 00 3A 3B 5C CB 6F
7FE0 28 F8 CB AF 32 3B 5C 3A
7FE8 08 5C C9 00 00 00 00 00
7FF0 00 00 00 00 00 00 00 00
7FF8 00 00 00 00 00 00 00 00
Using the rom routines 115
A*B+C*D/2
AB*CD2/* +
24*382/* +
For example, for the above rpn expression we go through the opera¬
tions shown below. On the lefthand side of the diagram I have given
the operations we use as we read the expression from left to right.
On the righthand side the current status of the stack is shown. The
top of the stack is the rightmost digit.
116 Using the rom routines
Operation Stack
stack 2 2
stack 4 2 4
operator * 8
stack 3 8 3
stack 8 8 3 8
stack 2 8 3 8 2
operator / 8 3 4
operator * 8 12
operator + 20
STACKA-2D28H
LD A,20
CALL 2D28H ;STACK NUMBER 20
This routine will convert the one byte number contained in the
Accumulator to its five byte floating format which is then pushed
onto the calculator stack.
There are two other routines which are similar to the last routine.
They allow us to stack a two byte integer in the bc register pair and a
five byte floating point number contained in the a,e,d,c,b registers.
The routine at the address 2D2B hex (stackbc) will convert a two byte
integer into five byte floating point format and push this on the
calculator stack. Likewise the routine at the address 2AB6 hex
(stacks) will push the floating point number in the registers a,e,d,c
and b.
To retrieve numbers from the stack we have routines which can
pop them off and convert them into one byte, two byte or the
normal five byte form. The addresses are given below:
There are two routines which are very useful to the programmer
when handling floating point numbers. The routine at the address
2DE3 hex (printfp) will take the top number on the calculator stack
and print it to the current channel selected. The second routine at
2C9B hex (asctofp) enables us to convert a number from a string to a
floating point number which is pushed to the calculator stack. Look
at the following program:
The calculator has five constants available in the rom used for cal
culating sines and cosines:
There are six memory locations used by the floating point calculator
in order to save numbers on top of the stack. The data codes C0 hex
to C5 hex are used to save the topmost number on the stack to one
of the six memory locations. The other codes E0 hex to E5 hex are
used to place numbers from one of the memory locations to the top
of the calculator stack.
Using the rom routines 119
LD BC,926H
CALL STACKBC ;STACK NUMBER
LD BC,9CH
CALL STACKBC ;STACK NUMBER
RST 28H ; START CALCULATION
DB 04 ;MULTIPLY
DB 38H ;END OF CALC.
CALL PRINTFP ; PRINT ANSWER
RET
100+50#SIN< PI*X/20)
DB 38H END OF CALCULATION
END
10 Screen and
attribute handling
0 Black
1 Blue
2 Red
3 Magenta
4 Green
5 Cyan
6 Yellow
7 White
122 Screen and attribute handling
Bit 7 6 5 4 3 2 1 0
f b p p p i i i
Where f is the flash bit, b is the bright bit, p is paper number, i is ink
number. So if, for example, we wanted the colour code for red ink
on white paper with the BRiGHTness set on we would put the value
64+8*7+2=122 in the appropriate location.
The address of the start of the attribute file is 22528 or 5800 hex. It
can be represented by a grid of 32 column by 24 rows. The start of
the file being in the top left-hand corner. If we wished to find the
address of a given pair of co-ordinates (row 0, column 0 is in the top
left-hand corner), then we could use the following piece of code:
} mmmmmm***********
; FIND ATTRIBUTE ADDRESS
j B CONTAINS THE ROW NUMBER
; C CONTAINS THE COLUMN NUMBER
; ON EXIT HL CONTAINS ADDRESS
LD L, B
LD H, 0
ADD HL, HL
ADD HL, HL
ADD HL, HL
ADD HL, HL
ADD HL, HL ;FIND ROW TIMES 32
LD B, 0
ADD HL, BC ;ADD COLUMN OFFSET
LD BC,5800H {START OF ATTRIBUTES
ADD HL, BC {ADD START OF ATTRIBUTES
RET {ADDRESS NOW IN HL
010sslllrrrccccc
ss is the section number, 0 being the top third, 1 being the middle
and 2 the bottom third.3 indicates an address in the attribute file. Ill
is the pixel line number (0-7) within a character, rrr is the line
number within a section (0-7) and ccccc is the column number
(0-31).
Using this pattern we can determine an address anywhere on the
screen, even down to one single bit. The section number is con¬
tained in the top two bits of the row. The pixel line number is also
contained in the row, this time in the middle three bits. The pixel
line number is the last three bits of the row. The column is repre¬
sented by a number 0-255. The range 0 to 255 is used because the
routine is designed to give the address and bit position of any pixel
on the screen. The data for the routine is as follows:
row ssrrrlll
col cccccbbb
where all the letters have their previous meanings and bbb is the bit
position of the pixel.
Now let's look at the routine to calculate the screen address for
any given row and column. The b register is LOADed with a row
124 Screen and attribute handling
number in the region 0-191, where row 0 is at the top of the screen.
The c register is LOADed with the column number in the range of
0-255. After executing the routine the hl pair will contain the
address on the screen, and the a register will contain the bit position
(0-7) within that address.
This routine could be used to plot points on the screen since the
plot command in basic is limited to accessing only 256 by 176 points.
To do this we call the pixadd routine then rotate the pixel to the bit
Screen and attribute handling 125
RRA
DJNZ PIX {Move pixel dot to position
;required
XOR < HL) {and place it at the address
LD (HL), A
RET
The inc H instruction is the same as adding 256 to the hl register pair
and has the effect of getting the next pixel line address below. The
offset is always 256 only if we are within a character boundary. If this
is not true we have to use the following routine below which I have
called incy:
126 Screen and attribute handling
INCY:
INC H
LD A. H
AND 7
RET NZ ;WITHIN CHAR BOUNDARY
LD A, H
SUB 8
LD H, A
LD A, L
ADD A, 32 ;NEXT CHAR LINE DOWN
;(WITHIN SECTION)
;ADD 32 DECIMAL
LD L, A
RET NC ;DEF WITHIN SECTION
;NEXT SECTION DOWN
LD A, H
ADD A, 8
LD H, A
XOR 58H ;01011080 BINARY
RET NZ ;IS THERE A WRAPAROUND
;NEEDED?
LD H, 48H
RET
screen addr screen bit pattern attr addr attr bit pattern
40 H 01000000 58H 01011000
48 H 01001000 59H 01011001
50H 01010000 5AH 01011010
As you can see this portion of code is similar to the first routine we
used to print a character to the screen. The exception is that the inc h
instruction is replaced by calling the routine incy which calculates
the address of the next pixel line down.
The next problem we have to overcome when PRiNTing a character
on the screen is to deal with its horizontal position. When we want
to print an eight bit character, at any of the 256 bits, we may
sometimes overlap between two character boundaries. This means
that if we can calculate the bit position where the object is to be
placed within one of the 32 horizontal positions on the screen we
can scroll the eight bit number which makes up one line of the
character through two bytes which we then print onto the screen.
Look at the following two diagrams. Diagram a shows a space ship
being PRiNTed within a character boundary. The data only occupies
one byte for each horizontal line. When we wish to print an eight by
eight bit object at any horizontal pixel position then we could get an
overlap onto the adjacent character position as shown in diagram B.
An overlap will occur seven in every eight horizontal bit positions.
To find a character's bit position simply get it's x co-ordinate and
mask off the bottom three bits by ANDing it with seven. Remember,
that this 'bit position' is different from the one we use to describe bit
instructions such as set,reset and bit. This time the bit position starts
from the left hand side of the byte.
8 by 8 pixel character
within boundary
Screen and attribute handling 129
If given the bit position in which a character lies, then to obtain its
two byte equivalent, get the object data to be printed and scroll it
from left to right within two bytes. This 'scrolling' from left to right
of a 16 bit number is identical to dividing the number by 2. This was
explained in the chapter on rotating and shifting. The following
piece of code divides a two byte number in the a register (the high
part) and the c register (the low part) by 2.
Notice how we clear the low byte of the number by LOADing the c
register with 0. We would of course do this scrolling until we reach
the bit position which we require. Therefore, if the B register con¬
tained the bit position we would find the two byte number by using
the code:
I B REGISTER CONTAINS THE BIT POSTION
LD A,B ;PLACE B REGISTER INTO
;THE A REGISTER
AND A JTEST FOR BIT POSTION=0
Notice that before we scroll the character we test that the bit posi¬
tion is zero. If the bit position was zero then this means that our
character is within a boundary so we deal with this at the lable
bound. If we did not do this and carried on through to scroll the data
then we would find that we would end up scrolling the data 256
times.
After we have our two new characters which make up the object
then it is simply a case of placing them on the screen. If, for
example, the hl register pair was pointing to the screen address
where we wanted to place the character then we would place the
data at hl and hl+1
To animate, simply draw the object onto the screen and to move it,
remove the object from its previous position. Then update its new
position and draw it to the screen. The following machine code
routine draws and animates nine space ships on the screen. Each
one follows a movement pattern. The object can move in any one of
four directions. Direction one indicates that the ship is moving
right, two left, four down and eight up. The movement pattern
dirtab is a table of directions which the ship follows and ends with
255 or ff hex. The ships start at different locations in the table so that
the movements are not synchronised.
Each ship has three bytes of data starting from shiptb, to represent
its x and y co-ordinates and an offset position or vector count
Screen and attribute handling 131
MOVEMENT ROUTINE
0R6 28800D
JP START }START THE PROGRAM
132 Screen and attribute handling
PUSH BC
CALL PRTCHR
POP BC
RET
LD IX,SHIP
SCROLL:
SRL A j SCROLL DATA DOWN TO BIT
iPOSTION
RR C
DJNZ SCROLL
LD A, L
ADD A, 32D J NEXT CHAR LINE DOWN
;(WITHIN SECTION)
LD L, A
RET NC ;CHAR WITHIN SECTION
LD A, H
ADD A, 8
LD H, A
XOR 88 ;01011080
RET NZ
LD H, 40H ;WRAP AROUND EFFECT
RET
DIRTAB:
CO
CO
DB 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
DB 1,1, 1, 1, 1, 1, 1, 1,1, 1,1, 1
DB 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
DB 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
DB 0FFH i END OF DIRECTION TABLE
Screen and attribute handling 135
GETDIR:
LD HL,DIRTAB ;POINT TO DIRECTION TABLE
LD E,<IY+VECTCN) ;GET SHIPS POINTER
LD D, 0
ADD HL, DE ;POINT TO DIRECTION
LD A, (HL) ;GET DIRECTION
CP 0FFH ;IS THIS THE END OF
;THE TABLE
JR NZ,MOVEIT jNO THEN MOVE SHIP!
XOR A ; SET A TO ZERO
LD <IY+VECTCN), A ;SET VECTOR COUNT TO ZERO
RET
MOVEIT:
INC (IY+VECTCN) ;INCREASE VECTOR COUNT
j FOR NEXT GO
CP 8 {GOING UP
JP Z, UPD
CP 4
JP Z,DOUND {GOING DOWN
CP 1
JP Z,RIGHTD {GOING RIGHT
CP 2
JP Z,LEFTD {GOING LEFT
RET
UPDAA:
CALL PRINOBJ {REPRINT SHIP
{AT NEW POSTION
LD (IY+XPOS), C {SAVE NEW XPOSTION
LD (IY+YPOS), B {SAVE NEW YPOSTION
RET
LEFTD:
CALL PRINOBJ
DEC C
JP UPDAA
RIGHTD:
CALL PRINOBJ
INC C
JP UPDAA
136 Screen and attribute handling
UPD:
CALL PRINQBJ
DEC B
JP UPDAA
DQWND:
CALL PRINOBJ
INC B
JP UPDAA
;SHIP TABLE
; 3 BYTES PER SHIP
; 1ST BYTE =X CO-ORD
; 2ND BYTE =Y CO-ORD
j3RD BYTE =VECTOR COUNT
SHIPTB:
DB 100, 100,8
DB 120, 80, 7
DB 55, 45, 14
DB 30, 30, 20
DB 40, 30, 1
DB 130, 130, 24
DB 140, 140, 20
DB 140, 118,2
DB 140, 150, 2
Screen and attribute handling 137
START:
DI DISABLE INT
LD 6, NUM LOAD B REGISTER WITH
NUNBER OF SHIPS
LD IY,SHIPTB IY POINTS TO START
OF SHIP TABLE
DRAW: PUSH BC SAVE SHIP COUNTER
LD B,(IY+YPOS) GET Y CO-ORD
LD C,(IY+XPOS) GET X CO-ORD
CALL PRINOBJ AND PRINT
LD DE, 3 DE CONTAINS OFFSET
ADD IY, DE POINT TO NEXT SHIP'S DATA
POP BC RESTORE COUNTER
DJNZ DRAW DRAW NEXT SHIP
MOVE:
LD IY,SHIPTB ;POINT TO SHIP TABLE
LD B, NUN [NUMBER OF SHIPS
NXT:
PUSH BC SAVE COUNTER
LD B,<IY+YPOS) GET Y CO-ORD
LD C,<IY+XPOS) GET X CO-ORD
CALL GETDIR GET DIRECTION AND MOVE
LD DE, 3 PLACE OFFSET IN DE
ADD IY, DE AND POINT TO NEXT SHIPM D
POP BC RESTORE SHIP COUNTER
DJNZ NXT MOVE NEXT SHIP
JR MDVE FOREVER AND SO ON.
END
Hexadecimal Listing
6060 C3 50 6E 16 FF 16 0F 0F
6D68 16 FF 16 C5 CD 71 6D Cl
CD AA n n
6D70 C9 OD 21 63 60 A«
6D78 5F 16 08 A7 28 1C 43 DD
6D80 7E 00 0E 00 CB 3F CB 19
6D88 10 FA AE 77 23 79 AE 77
6090 2B DD 23 CD A8 6D 15 20
6D98 E5 C9 42 00 7E 00 AE 77
138 Screen and attribute handling
6DA0 CD AS 6D DD 23 10 F4 C9
6DA8 24 7C E6 07 C0 7C D6 08
6DB0 67 7D C6 20 6F D0 7C C6
6DB8 08 67 EE 58 C0 26 40 C9
6DC0 08 08 08 08 08 08 08 08
6DC8 08 08 08 08 01 01 01 01
6DD0 01 01 01 01 01 01 01 01
6DD8 04 04 04 04 04 04 04 04
6DE0 04 04 04 04 02 02 02 02
6DE8 02 02 02 02 02 02 02 02
6DF0 FF 21 C0 60 FD 5E 02 16
6DF8 00 19 7E FE FF 20 05 AF
6E00 FD 77 02 C9 FD 34 02 FE
6E08 08 CA 34 6E FE 04 CA 3B
6E10 6E FE 01 CA 2D 6E FE 02
6E18 CA 26 6E C9 CD 6B 6D FD
6E20 71 00 FD 70 01 C9 CD 6B
6E28 6D 0D C3 1C 6E CD 6B 6D
6E30 0C C3 1C 6E CD 6B 6D 05
6E38 C3 1C 6E CD 6B 6D 04 C3
6E40 1C 6E 64 64 08 78 50 07
6E48 37 2D 0E IE IE 14 28 IE
6E50 01 82 82 18 8C 8C 14 8C
6E58 76 02 8C 96 02 F3 06 09
6E60 FD 21 42 6E C5 FD 46 01
6E68 FD 4E 00 CD 6B 6D 11 03
6E70 00 FD 19 Cl 10 EE FD 21
6E78 42 6E 06 09 C5 FD 46 01
6E80 FD 4E 00 CD FI 6D 11 03
6E88 00 FD 19 Cl 10 EE 18 E6
6E90 6E FE 01 CA 2D 6E FE 02
6E98 CA 26 6E C9 CD 6B 6D FD
6EA0 71 00 FD 70 01 C9 CD 6B
6EA8 6D 0D C3 1C 6E CD 6B 6D
6EB0 0C C3 1C 6E CD 6B 6D 05
6EB8 C3 1C 6E CD 6B 6D 04 C3
6EC0 1C 6E 64 64 08 78 50 07
6EC8 37 2D 0E IE IE 14 28 IE
6ED0 01 82 82 18 8C 8C 14 8C
6ED8 76 02 8C 96 02 F3 06 09
Screen and attribute handling 139
6EE0 FD 21 42 6E C5 FD 46 01
6EE8 FD 4E 00 CD 6B 6D 11 03
6EF0 00 FD 19 Cl 10 EE FD 21
6EF8 42 6E 06 09 C5 FD 46 01
Have you ever wished that your computer could execute more than
one program at once? Well, this chapter will explain how, in effect,
you can double the power of your Spectrum by seemingly RUNning
two programs at once!
Interrupts on the Z80 chip serve similar purposes to those on
other processors. They tell the computer that an external device,
such as a disk drive, printer, keyboard or modem requires some
attention. Take, as an example, the case where we have linked up a
printer printing out data to our computer.
There are two ways of checking whether the printer is ready to get
a character from the microprocessor. The inefficient way is to use a
loop which has a description like this:
WAIT:
IS PRINTER READY?
ANSWER=NO THEN GO TO WAIT
ANSWER=YES THEN GET NEXT CHARACTER: SEND IT
TO THE PRINTER: GO TO WAIT
As you can see the above method 'polls' the printer continually to
see if it is ready for the next character. Most of its time is spent in
this loop waiting for the printer, so a lot of CPU time is wasted!
Wouldn't it be fine if we could continue with other parts of the
program and only send characters when the printer is ready? Well
we can by using interrupts! Your Spectrum uses interrupts to get
characters from the keyboard and update the frames system
variable.
What is happening on a Spectrum is that your computer is run¬
ning Spectrum basic. Frequently, (1/50 of a second to be precise or
1/60 of a second in N. America) it remembers where it is and what
line it is running. It also recalls what address it is executing in the
rom or ram and executes a routine in rom which scans the keyboard.
After it has done this it will go back to the address it was executing
prior to interruption.
Interrupts on the Spectrum 141
mode 1 interrupts
Every time an interrupt occurs the processor pushes the current
program counter onto the stack and jumps to location 0038 hex.
To exit out of this interrupt we must use a 'ret' (return) or 'reti'
(return from interrupt) instruction.
This mode of interrupt is actually the one used by the Spectrum
during the scan for a key routine as described above.
mode 2 interrupts
This is the most powerful of the interrupts on the Z80 processor and
is sometimes known as vectored processing.
In a mode 2 interrupt the programmer can specify up to 128
interrupts for other external devices.
This mode of interrupt revolves round a table which can contain
up to 128 addresses. We can also have more than one table to deal
with other external devices.
The start of a table is always on a page boundary of a 256 byte
section of memory, i.e. 000H, 100H, 200h, C200h, etc. To tell the
processor where the vector table is we load the i (Interrupt) register
with the high byte of the page number. For example if our vector
table was at location C000 hex then we would tell the processor by
executing:
Dl ;DISABLE INTERRUPTS
1M 2 ;SET UP INTERRUPT MODE 2
LD l,C0H ; LOAD 1 REGISTER WITH C0 HEX
El ;ENABLE INTERRUPTS
Note that we only need to specify the high byte as we are dealing
with page boundaries. The second line of code tells the processor
that we want to use mode 2 interrupts. The last instruction turns on
the scanning of interrupts. If we wished to ignore any maskable
interrupts at any time we would use the instruction:
Dl ;DISABLE INTERRUPTS
IM 2
LD A,C0H
LD LA ; 1 REGISTER IS LOADED WITH HIGH
BYTE OF TABLE
El
KEYBROU:
RET
PRINROU:
RET
device to return a valid data vector with its lowest bit set to zero i.e.
always even!
ORG 0C000H
INTROU:
DI {STOP ANY MORE INTERRUPTS
PUSH HL {SAVE HL
LD HL, 5B00H {POINT TO THE ATTRIBUTE
j FILE
LD (HL), 255 {AND SHOW SOME COLOUR
POP HL {RESTORE HL
JP 0038H {JUMP BACK TO BASIC.
El ;ENABLE INTERRUPT
RET ;RETURN FROM INT
OR El
RETI
ORG 30000
RASTER: HALT
LD A, 1
OUT (0FEH), A
LD HL,500H ; ##EXPERIMENT##
;WITH THIS VALUE!
LOOP: DEC HL
LD A, L
OR H
JR NZ, LOOP
LD A, 2
OUT (0FEH), A
JR RASTER
END
7D00 76 3E 01 D3 FE 21 00 05
7D08 2B 7D B4 20 FB 3E 02 D3
7D10 FE 18 ED 00
Interrupts on the Spectrum 145
When you run the program you should get a border split into two
colours blue and red
The halt instruction on the Z80 is used to wait for an interrupt.
The computer will wait at a halt instruction until some external
device causes an interrupt. In the case of the Spectrum the ula
causes the interrupt. Therefore, the effect of the halt instruction is
to wait 1/50th of a second. Of course if we disabled all interrupts by
using the instruction di then the computer would wait for ever
unless a Non-maskable interrupt (one that can not be disabled) was
activated. In our program we use the halt instruction to link with the
raster beam to cause a split in the border colour. Objects can be
drawn when the raster is at the top or flying back thus reducing
screen flicker. A lot of game programmers use this technique when
writing fast arcade games.
Try pressing the keys when RUNning this program. Notice how the
borders go up and down. Do you know why? It is due to the
keyboard routine (which is called by the interrupt routine) taking
different lengths of time to execute depending on which keys it
finds pressed.
Every 1/50th of a second the computer reDRAWs the screen. The
screen is updated by an election beam which scans across the pixels
turning them on or off if they are set or re-set. The beam starts from
the top left hand side of the screen and scans left to right across
each fine. After reaching the bottom the beam (or raster) flys
diagonally back to the top left where it starts to update the screen
again.
ORG 32330D
{TRACE ROUTINE
{FOR 16K SPECTRUM
TRDN:
LD A, 28H ; LOAD A WITH 28H
LD I, A ; AND PLACE IN THE
;I REGISTER
IM 2 j SET UP INTERRUPT MODE 2
El ;AND ENABLE
RET
TRACE: DI
PUSH AF {SAVE REGS
PUSH BC
PUSH DE
PUSH HL
PUSH IX
LD DE,16384
CALL CONV {PRINT NUMBER ON SCREEN.
INC DE
LD A, 0FBH
IN A,(0FEH>
RRA
JR C, SKIP
CALL WAIT
CALL WAIT
SKIP:
POP IX
POP HL
POP DE
POP BC
POP AF
JP 0038H
WAIT: LD HL, 00
LD DE, 80
LD BC, 00
LDIR
RET
EX DE, HL
LD IX, ZEROADD POINT TO START OF
NUMERIC DATA
ADD IX, DE ADD OFFSET TO START OF
NUMBER
EX DE, HL
LD B, B ;SET COUNTER
POP HL
POP IX
POP BC ;RESTORE REGISTERS
POP DE
RET
DECT2:
DEFW 1000
DECT3: DEFW 100
DEFW 10
DEFW 1
CQNV2:
LD IX,DECT3
JR NDIG2
CONV: LD IX,DECT2
NDIG2: LD B, (IX+1)
LD C,(IX+0)
LD A,' 0' -1
AND A
CAR; INC A
SBC HL, BC
JR NC,CAR
ADD HL, BC
CALL PRDIGIT
INC DE
INC IX
150 Interrupts on the Spectrum
INC IX
DEC C
JR NZ, NDIG2
RET
END
Hexadecimal Listing
7E4A 3E 28 ED 47 ED 5E FB C9
7E52 ED 56 C9 00 00 00 00 00
7E5A 00 00 F3 F5 C5 D5 E5 DD
7E62 E5 2A 45 5C 7C 3C 28 ID
7E6A 11 00 40 CD CE 7E 13 3A
7E72 47 5C 26 00 6F CD C8 7E
7E7A 3E FB DB FE IF 38 06 CD
7E82 90 7E CD 90 7E DD El El
7E8A D1 Cl FI C3 38 00 21 00
7E92 00 11 00 00 01 00 00 ED
7E9A B0 C9 D5 C5 DD E5 E5 26
7EA2 00 6F 29 29 29 EE: DD 21
7EAA 00 3C DD 19 EE: 06 08 DD
7EB2 7E 00 12 14 DD 23 10 F7
7EBA El DD El Cl D1 C9 E8 03
7EC2 64 00 0A 00 01 00 DD 21
7ECA C2 7E 18 04 DD 21 C0 7E
7ED2 DD 46 01 DD 4E 00 3E 2F
7EDA A7 3C ED 42 30 FE: 09 CD
7EE2 9C 7E 13 DD 23 DD 23 0D
7EEA 20 E6 C9 CD CE 7E 13 3A
7EF2 47 5C 26 00 6F CD C8 7E
7EFA 3E FB DB FE IF 38
branch off to 38 hex on every interrupt. To start the clock off again
simply type randomize usr32330
BASIC Listing
Assembler Listing
ORG 32338D
;CLOCK ROUNTINE
;FOR 16K SPECTRUM
TRON:
LD A,28H j SET UP I REGISTER
LD I, A j TO PAGE 28 HEX
IM 2 j SET INTERRUPT MODE 2
El ;AND ENABLE
RET
FRAMES:
DB 0
INC A
LD <FRAMES >, A
CP 50 ,* HAVE WE COUNTED THROUGH
;1 SEC?
JR NZ,PRCLOCK »NO, SO PRINT TIME ANYWAY.
PRCLOCKi
;ROUTINE TO PRINT THE CLOCK ON THE SCREEN
LD B, 8 j SET COUNTER
END
Hexadecimal Listing
7E4A 3E 28 ED 47 ED 5E FB C?
7E52 00 00 00 00 00 00 00 00
7E5A 00 00 F3 F5 C5 D5 E5 DD
7E62 E5 3A 52 7E 3C 32 52 7E
7E6A FE 32 20 ID AF 32 52 7E
7E72 11 83 7E 21 88 7E 06 03
7E7A 7E C6 01 27 77 1A BE 20
7E82 08 36 00 13 2B 10 FI 23
7E8A 34 21 17 40 11 B6 7E 06
7E92 03 1A 4F E6 F0 0F 0F 0F
7E9A 0F CD B9 7E 23 79 E6 0F
7EA2 CD B9 7E 13 23 23 10 E9
7EAA DD El El D1 Cl FI C3 38
7EB2 00 60 60 13 00 00 00 E5
7EBA C5 CB 27 CB 27 CB 27 06
7EC2 00 4F DD 21 80 3D DD 09
Interrupts on the Spectrum 155
7ECA 06 08 DD 7E 00 77 24 DD
7ED2 23 10 F7 Cl El C9 00 00
7EDA 00 00 F3 F5 C5 D5 E5 DD
7EE2 E5 3A 52 7E 3C 32 52 7E
7EEA FE 32 20 ID AF 32 52 7E
7EF2 11 B3 7E 21 B8 7E 06 03
7EFA 7E C6 01 27 77 1A
12 Machine code
miscellany
BRICKOUT
The machine code routine comes in three main sections. The first
initializes the score, the number of bricks left, the number of balls
left and draws the screen. The second routine, movbat, moves the
user's bat, controlled by the <z>, <x> and <caps shift> keys. The
last routine, muball, deals with moving the ball around the screen,
knocking out bricks, rebounding off the bat and walls, and updating
the score. We'll break down the assembler listing, and give the
whole hexadecimal listing at the end.
To start off the game we jump into the portion of code labelled
start. This follows the initialisation:
ORG 32000
JP START
BATYX: DEFW 160FH BATS POSITION
BALLX: DB 10H BALLS X POSITION
BALLY: DB 01H BALLS Y POSITION
TPBLYX:
DEFW 0 TEMP AREA
XINC: DB 1 X MOVEMENT
YINC: DB 1 Y MOVEMENT
LEVEL: DB 4 LEVEL
NUMBER OF HALTS FOR DELAY
SCORE: DEFW 0 SCORE
BALLS: DB 0 NUMBER OF BALLS
HITS: OB 0 NUMBER OF BRICKS HIT
PATTBL:
SPACE: DB 0, 0, 0, 0, 0, 0,
START:
LD A, 2
CALL 1601H jOPEN CHANNEL TWO
XOR A ;SET A REGISTER TO ZERO
OUT (0FEH), A ;SET BORDER TO BLACK
BATAGN:
LD A, (BALLS) J LOOK AT THE NUMBER
;OF BALLS LEFT
AND A ;IS IT ZERO?
JR Z,GMOVER ;IF IT IS GO TO
)DEAL WITH END OF GAME
;WE ARE STILL PLAYING
CALL MOVBAT ;MOVE BAT
CALL MUBALL ;MOVE BALL
Machine code miscellany 159
El
HALT ;WAIT FOR 1/50 OF A SECOND
DI
JR BATAGN ; KEEP PLAYING
GMOVER:
LD BC, (SCORE) PASS SCORE TO
BC REGISTER PAIR
El ENABLE INTERRUPTS
RET AND RETURN TO BASIC
The games ends when there are no balls left, which causes the
routine to jump to the label gmover. This gets the current score and
places it into the bc register pair to be passed back to basic.
movbat is used to control the movement of the bat according to
the keys <z> or <x> being pressed. If the user presses the key
ccaps shift> then the routine goes back to the label five,9@ to move
the bat again.
A call is made to either righttb or lefttb to move the bat right or
left. After this the routine returns to move the ball.
MOVBAT:
;MOVE PLAYERS BAT
BATPRT;
CALL PRTBAT jPRINT BAT ON SCREEN
RET jAND RETURN
When moving the ball left or right a check must be made to make
sure that the bat does not go off the screen. The variable batxy holds
the x,y co-ordinate of the left hand side of the bat. The bat is made
up of three characters.
RIGHTS:
PUSH AF j GOING RIGHT, SAVE AF PAIR
LD HL,(BATYX) ;GET X,Y C0-0RD OF BAT
JIN HL PAIR
LD A, 1DH iLOAD A REGISTER WITH 29
CP L ;TEST TO SEE IF WE HAVE
;HIT THE RIGHT SIDE
JR Z,REDGE J HIT, SO DON'T UPDATE
INC L 1INCREASE X CQ-ORD
LD (BATYX), HL ;AND SAVE
REDGE:
POP AF ;RESTORE KEY STATUS
RET ;AND RETURN
LEFTS:
PUSH AF j GOING LEFT,SAVE KEY STATUS
LD HL,(BATYX) ;GET X, Y C0-0RDS
LD A, L f TEST IF HIT LEFT HAND SIDE
AND A ;IE IF EQUAL TO 0
JR Z, LEDGE ;HIT SO DON'T UPDATE
DEC L ;DECREASE ONE OFF X C0-0RD
LD (BATYX), HL ;AND SAVE
LEDGE:
POP AF ;RESTORE KEY STATUS
RET ;AND RETURN
The routine clrbat is used to remove the bat from the screen. To do
this we print the character space which consists of zeros. While the
routine prtbat is used to print the bat to the screen. Both these
routines call the routine prtch which prints the character held in the
a register. In this routine a call is made to two rom routines. The
routine at 0E9E hex calculates the screen address for a given y co¬
ordinate. The routine at the address 0E88 hex calculates the attribute
in the de register pair for a given screen address.
Machine code miscellany 161
CLRBATi
PUSH AF SAVE AF REGISTER
LD HL, (BATYX) GET X, Y CO-ORD
LD BC,338H SET INK AND PAPER
WHITE PAPER BLACK INK
CLRIT: B REGISTER IS
LOADED WITH 3
PUSH BC SAVE CHAR CODE
PUSH HL SAVE X, Y CO-ORD AND
COUNTER
XOR A SET A TO ZERO
CALL PRTCH PRINT SPACE
POP HL RESTORE X,Y
INC L POINT TO NEXT CHAR OF BAT
POP BC RESTORE X, Y CO-ORD
AND COUNTER
DJNZ CLRIT RUB OFF 3 CHARACTERS
POP AF RESTORE AF REGISTER
RET
PRTBAT:
LD HL,(BATYX ) ;GET X, Y CO-ORD
LD BC, 339H i1 SET B=3 AND COLOUR TO
i1 WHITE PAPER AND RED INK
LD A, 2 11INTIALIZE A REG TO FIRST
j;CHARACTER OF BAT
NEXBAT:
PUSH BC SAVE COLOUR AND COUNTER
PUSH HL SAVE X, Y CO-ORD
CALL PRTCH PRINT PART OF BAT
INC A NEXT PART OF BAT
POP HL RESTORE X,Y
INC L NEXT X POSTION OF BAT
POP BC RESTORE COUNTER AND COLOUR
DJNZ NEXBAT DO 3 TIMES
RET AND RETURN
PRINTCHAR:
;H=Y L=X A=CHAR NUMBER C=C0L0UR
162 Machine code miscellany
PRTCH:
PUSH AF
PUSH BC ;SAVE CHARACTER
PUSH HL ; SAVE COLOUR
J SAVE X, Y CO-ORDS
PUSH BC
PUSH AF ;SAVE COLOUR
PUSH HL ;SAVE CHARACTER
;SAVE X, Y CO-ORDS
LD A, H
CALL 0E9EH ; LOAD A WITH Y CO-ORD
POP DE ;CALCULATE SCREEN ADDRESS
LD D, 0 ;PLACE X CO-ORD IN E REG
ADD HL, DE ;PLACE 0 IN D
EX DE, HL ; FIND SCREEN ADDRESS
;AND PLACE IN DE
POP AF ;GET CHARACTER CODE
LD BC,PATTBL ;BC POINTS TO CHARACTER SET
LD H, 0 ;LOAD H WITH 0
LD L, A ,* LOAD A WITH CHARACTER
;NUMBER
ADD HL, HL ; TIMES BY 2
ADD HL, HL ;TIMES BY A
ADD HL, HL ;TIMES BY 8
ADD HL, BC ;AOD CHARACTER
;TABLE ADDRESS
;HL NOW POINTS
j TO CHARACTER DATA
LD B, 8 ; LOAD B WITH DATA COUNT
LD A, C ;PLACE IN A REGISTER
LD (DE), A j SET ATTRIBUTE
POP HL j RESTORE X, Y CO-ORD
POP BC .■RESTORE COLOUR CODE
POP AF .•RESTORE CHARACTER
RET ;RETURN FROM PRINTING
The routine setup is called only once: at the start of each new game.
It is used to draw the bricks on the screen.
SETUP:
CALL 0D66H ;CLEAR SCREEN
LD BC,2020H ;32 GREEN BRICKS
LD A. 5 ;PLACE BRICK CHAR IN A REG
LD HL, 300H ;START X, Y CO-ORD OF BRICKS
CALL NXCOL j DRAW BRICKS
NXCOL:
CALL PRTCH ;PRINT BRICK
INC L ;POINT TO NEXT X CO-ORD
DJNZ NXCOL ;REPEAT 32 TIMES
RET ;RETURN
peek is the routine which is used to detect any collision between the
ball and any bricks or the bat. The x and y co-ordinates are placed in
the hl pair and after CALLing this routine the attribute or colour code
is RETURNed in the A register.
164 Machine code miscellany
PEEK!
RETURNS ATTRIBUTE
OF GIVEN X, Y (IN HL PAIR)
IN A REGISTER
LD A, L jPLACE X CO-ORD
;IN A REGISTER
LD L, H ;PLACE Y CO-ORD
;IN L REGISTER
LD H, 0 ;32 BIT NUMBER SO
/ PLACE 0 IN H
ADD HL, HL ;TIMES BY 2
ADD HL, HL /TIMES BY 4
ADD HL, HL /TIMES BY 8
ADD HL, HL /TIMES BY 16
ADD HL, HL /TIMES BY 32
LD B, 0 /LOAD B REG WITH 0
LD C, A /PLACE X CO-ORD IN C REGISTER
ADD HL, BC /FIND OFFSET
LD BC,5800H
ADD HL, BC /CALCULATE ATTRIBUTE
j ADDRESS
LD A, (HL) {GET CONTENTS OF
/THAT ADDRESS AND PLACE
/IN A REGISTER
RET ;RETURN
LD BC, 0
CP 30H /HAVE WE HIT A YELLOW BRICK
JR NZ,NTYLW ;NOT YELLOW
Machine code miscellany 165
NTYLW:
CP 18H ;HAVE WE HIT A
j NAGENTA BRICK?
JR NZ, NTMAGN j NOT MAGENTA
LD A, -1 ;SEND BALL IN
;OTHER DIRECTION
LD ( YINC), A
LD BC, 5 ;SCORE
JR BEEP ; AND MAKE A SOUND
NTMAGN:
CP 20H HAVE WE HIT A GREEN BRICK?
JR NZ, ERROR GOD KNOWS WHAT WE HIT!
LD BC, 10 GIVE HIM A BIG SCORE
JR BEEP AND MAKE A NOISE!
ERROR:
LD DE, 40H
LD HL, 666H
CALL 3B5H ;MAKE A LONGER BEEP!
BEEP:
LD HL, (SCORE) GET SCORE
ADD HL, BC AND ADD 0,5 OR 16
LD < SCORE ),HL SAVE UPDATED SCORE
LD HL, HITS POINT TO NUMBER OF HITS
DEC < HL) SUBTRACT ONE
JR NZ, NOEND ALL BRICKS HIT?
MAXLEV:
CALL RNDBAL {GET A RANDOM BALL POSTION
CALL SETUP {SET UP THE WALL
NOEND:
LD DE, 8
LD HL, 666H
CALL 3B5H {BEEP
LD A, 0 {MAKE SURE
OUT < 0FEH), A {WE HAVE A BLACK BORDER
RET
The routine muball is one of the main routines which deals with the
movement of the ball. The ball has a y direction (yinc) and x direction
(xinc). These two variables are offsets which are added to the ball's x
and y co-ordinates. These are either 1 or —1. If the ball passes the
bottom of the screen, one is deducted off the number of remaining
balls. If there is still any left then a branch is made to rndball which
sets up another ball at a random x position. If the ball collides with
an object than its x direction and/or y direction is reversed.
MUBALL:
LD HL,(BALLX) I GET BALLS X, Y CO-ORD
{BALL DOESNT GO THROUGH
{THE BAT...
NTBAT:
LD HL,(BALLX) GET X, Y CO-ORD
LD A, (XINC) GET X DIRECTION
ADD A, L GET NEW X CO-ORD
LD L, A AND SAVE IN L REG
LD (TPBLYX), A PLACE NEW X CO-ORD IN TEMP
AND A IS IT AT THE
LEFT HAND SIDE?
JR Z.NGXINC YES THEN GO TO
CHANGE X DIRECTION
CP 1FH IS IT ON THE
RIGHT HAND SIDE?
JR C,YCHECK NO SO CHECK Y MOVEMENT
NGXINC:
LD A, (XINC) GET X DIRECTION
NEG REVERSE X DIRECTION
LD (XINC), A AND SAVE
YCHECK:
LD A, (YINC) ;GET Y DIRECTION
ADD A, H ;GET NEW Y CO-ORD
LD H, A ;AND SAVE IN H REG
LD (TPBLYX+1), A ;AS WELL AS TEMP+1
AND A ;HAVE WE HIT THE TOP?
JR Z,NGYINC ;YES THEN CHANGE
Y DIRECTION
168 Machine code miscellany
END
MAZE GENERATOR
Hexadecimal Listing
7 D00 C3 40 7D 0F 16 10 01 00
7D08 00 01 01 04 00 00 00 00
7D10 00 00 00 00 00 00 00 00
7D18 3C 7E FF FF FF FF 7E 3C
7D20 3F 7F FF FF FF FF 7F 3F
7D28 FF FF FF FF FF FF FF FF
7D30 FC FE FF FF FF FF FE FC
7D38 FF 81 81 81 81 81 81 FF
7D40 3E 02 CD 01 16 AF D3 FE
7D48 21 00 00 22 0C 7D 3E 05
7D50 32 0E 7D 3E 04 32 0B 7D
7D58 3E 60 32 0F 7D CD 1A 7E
7D60 CD 5F 7F 21 10 16 a- 03
7D68 7D CD D9 7D 3A 0E 7D A7
7D70 28 0B CD 83 7D CD B7 7E
7D78 FB 76 F3 18 EF ED 4B 0C
Machine code miscellany 171
7 D80 7D FB C9 CD D9 7D 3E FE
7D88 DB FE E6 IF FE IF C8 CD
7D90 C5 7D CB 4F CC B7 7D CB
7D98 57 CC A8 7D CB 47 20 04
7DA0 CB C7 18 EE CD D9 7D C9
7DA8 F5 2A 03 7D 3E ID BD 28
7DB0 04 2C 22 03 7D FI C9 F5
7DB8 2A 03 7D 7D A7 28 04 2D
7DC0 oo
Am Am 03 7D FI C9 F5 2A 03
7DC8 7D 01 38 03 C5 E5 AF CD
7DD0 ED 7D El 2C Cl 10 F5 FI
7DD8 C9 2A 03 7D 01 39 03 3E
7DE0 02 C5 E5 CD ED 7D 3C El
7DE8 2C Cl 10 F5 C9 F5 C5 E5
7DF0 C5 F5 E5 7C CD 9E 0E D1
7DF8 16 00 19 EB FI 01 10 7D
7E00 26 00 6F 29 29 29 09 06
7E08 08 7E 12 23 14 10 FA EB
7E10 CD 88 0E Cl 79 12 El Cl
7E18 FI C9 CD 6B 0D 01 20 20
7E20 3E 05 21 00 03 CD 37 7E
7E28 01 18 20 21 00 04 CD 37
7E30 7E 01 30 20 21 00 05 CD
7E38 ED 7D 2C 10 FA C9 7D 6C
7E40 26 00 29 29 29 29 29 06
7E48 00 4F 09 01 00 58 09 7E
7E50 C9 01 00 00 FE 30 20 0A
7E58 3E FF 32 0A 7D 01 02 00
7E60 18 20 FE 18 20 0A 3E FF
7E68 32 0A 7D 01 05 00 18 12
7E70 FE 20 20 05 01 0A 00 18
7E78 09 11 40 00 21 66 06 CD
7E80 E:5 03 2A 0C 7D 09 22 0C
7E88 7D 21 0F 7D 35 20 1A 36
7E90 60 3A 0B 7D A7 28 0C 3D
7E98 32 0B 7D 3A 0E 7D C6 02
7EA0 32 0E 7D CD 5F 7F CD 1A
7EA8 7E 11 08 00 21 66 06 CD
7EB0 B5 03 3E 00 D3 FE C9 2A
7EB8 05 7D 3A 0A 7D 84 67 3A
172 Machine code miscellany
7EC0 09 7D 85 6F E5 CD 3E 7E
7EC8 El FE 39 20 26 3A 0A 7D
7ED0 ED 44 32 0A 7D 3A 09 7D
7ED8 ED 44 85 6F CD 3E 7E FE
7EE0 38 20 D4 3A 05 7D A7 28
7EE8 CE 3A 09 7D ED 44 32 09
7EF0 7D 18 C4 2A 05 7D 3A 09
7EF8 7D 85 6F 32 07 7D A7 28
7F00 04 FE IF 38 08 3A 09 7D
7F08 ED 44 32 09 7D 3A 0A 7D
7F10 84 67 32 08 7D A7 28 0E
7F18 FE 17 30 31 CD 3E 7E FE
7F20 38 28 0B CD 51 7E 3A 0A
7F28 7D ED 44 32 0A 7D 3E 00
7F30 0E 38 2A 05 7D CD ED 7D
7F38 3E 01 2A 07 7D 22 05 7D
7F40 CD ED 7D 3A 0B 7D FB 76
7F48 F3 3D 20 FA C9 3E 00 0E
7F50 38 2A 05 7D CD ED 7D CD
7F58 5F 7F 21 0E 7D 35 C9 3A
7F60 78 5C CB 3F E6 0F C6 05
7F68 32 05 7D 3E 06 32 06 7D
7F70 2A 05 7D oo
A** A— 07 7D 06 32
7F78 FB 76 F3 10 FB C9 A7 28
with the two walls intact. This is represented by the two first bits of
its number being set high (i.e. the number three). Knocking down
the walls is represented by re-setting a particular bit. If bit 0 of the
number represents the bottom wall and bit 1 represents the right
hand side wall then we can see the process if we knock down a wall.
Going downwards we reset bit 0 of the cell we are in. If we knock
down a wall going up we reset bit 0 of the cell above,the cell we are
entering. Going right we re-set bit 1 of the cell we are in, going left
we re-set bit 1 of the adjacent cell.
One point we have to look out for is that we do not 'back track' on
a particular walk we are doing. We do this by giving each walk a path
number and if we do happen to back track on our original path then
we do not bother to knock down any walls. Using this method we
guarantee our maze does not have any gaping holes and that it is
singular in nature.
The program comes in two parts, one basic and one machine
code. The machine code routine generates a random maze. The
basic program draws the top and left hand side of the wall to
complete the maze. When you use the generator in a game the
unused bits in the array maze can be used to represent up to 63
objects such as axes, torches, wands or nasties! The second array is
unused once the maze is generated so it could be used to store
other variables or data in the game. The maze takes about two
seconds to generate, very slow by machine code standards, perhaps
you could set yourself the task to make it faster. One way of
improving the speed for48K Spectrum owners would be to place the
routine higher up in the memory map above the address 32768.
Moving the code here would stop the Z80 cpu 'waiting' for the
Spectrum's ULAto update the screen.
BASIC Listing
1 CLEAR 29000
10 CLS:LET SC=USR 32000
20 CLS:PRINT AT 10,10;"SCORE =
";SC
30 FOR X=1 TO 200:NEXT X
40 PRINT #0;"PRESS A KEY TO ST
ART"
50 PAUSE 0
60 GO TO 10
174 Machine code miscellany
Assembler Listing
ORG 30000
JP START
XPOS; DB 0
YPOS: DB 0
PATH: DB 0
INTMAZE:
LD A, 2 {OPEN SCREEN CHANNEL
CALL OPENCH
LD HL,NOUGHT ;SET USER DEF GRAPHICS
;TO OURS
LD (UDG), HL
CALL RANDI ;INTIALIZE RANDOM
;NUMBER GENERATOR
LD HL,MAZE ;RE-BUILD THE MAZE
LD DE,MAZE+1
LD BC, 22*32 ; OF 22 BY 32
LD < HL), 3 {WITH MALLS
LDIR
5 HL POINTS TO BUILD
J DE POINTS TO BUILD+1
RANDI:
LD A, 0 j SET UP RANDOM VARIABLES
LD < RANDS), A
LD A, 173
LD (RAND1), A
LD A, 206
LD < RAND2), A
LD A, R ;ENSURE SOME RANDOMNESS
LD (RAND3), A
RET
176 Machine code miscellany
WALK:
LD B, LENW
KEW: ;KEEP WALKING
CALL RANDW
LD A, (HL) !GET CONTENTS OF
;NEW POSTION
AND A ;TEST FOR NEW LOCATION
JR Z,PUTIN ;ZERO SO MARK PATH!
CP C 1 GOING BACK ON PATH?
JR Z,PUTIN ;YES MARK IT!
RET ;HAVE REACHED A VALUE LOWER
PUTIN:
LD A, (PATH) ;GET PATH NUMBER
LD (HL), A ;AND PLACE IN BUILD
DJNZ KEW J DO THIS FOR LENW MAXIMUM
LD A,(PATH) ;ONLY DO LENW FOR PATH 1
CP 1
JR NZ,WALK
RET
RANDW:
CALL RAND jGET RANDOM NUMBER
AND 3 1 MASK OFF FOR
1 NUMBERS 0 TO 3
AND A
;GO EAST
Machine code miscellany 177
EAST:
LD A,(XPOS) GET X CO-ORD
CP 31 TEST TO SEE IF WE ARE ON
JR Z,RANDW THE RIGHT HAND SIDE
IF SO GO AGAIN
INC A ELSE INCREASE
X CO-ORD BY 1
LD < XPOS >, A AND SAVE
LD C, < HL) OLD VALUE IN C REGISTER
INC HL NEW POSTION
LD A, (HL) NEW VALUE
CP C ARE THEY EQUAL?
RET Z DON'T BACKTRACK!
WEST:
NORTH:
LD A, (YPOS) j GET Y CO-ORD
AND A {ARE WE AT THE TOP?
JR Z, RANDW {YES, THEN PICK ANOTHER
;DIRECTION
DEC A ;GOING UP
LD (YPOS), A ;SAVE NEW Y CO-ORD
LD C, < HL) ;GET OLD VALUE
LD DE,-32 {OFFSET FOR GOING UP
ADD HL, DE {POINT TO NEW PART OF
{BUILD ARRAY
LD A, (HL) {GET PATH NUMBER
CP C {COMPARE WITH OLD VALUE
RET Z {DON'T BACKTRACK!
SOUTH:
LD A, (YPOS) {GET Y CO-ORD
CP 21 {HAVE WE HIT THE BOTTOM?
JR Z, RANDW {YES THEN PICK
{ANOTHER DIRECTION
Machine code miscellany 179
PRINTC:
PUSH HL ;SAVE REGISTERS
PUSH BC
BUILDM:
LD HL,BUILD ;POINT TO ARRAY BUILD
XOR A ;INTIALIZE THE X CO-ORD
LD < XPOS>, A {AND Y CO-ORD
LD (YPOS), A
FIND:
LD A, (HL) 1 GET PATH NUMBER
AND A !TEST FOR ZERO
JR Z, SKIP ;START WALKING ON ZERO
INC HL 1 NEXT ONE ACROSS
LD A,(XPOS) iUPDATE X CO-ORD
INC A
LD (XPOS), A
CP 32 !HAVE WE GONE RIGHT ACROSS?
JR NZ, FIND JNO,SO CARRY ON LOOKING
XOR A ;YES...RESET X CO-ORD
LD (XPOS >, A
LD A, < YPOS) JGO ONE DOWN
INC A
LD < YPOS), A
CP 22 {HAVE WE GONE ALL
JR NZ, FIND J THE WAY DOWN7.NO THEN KEEP
RET J LOOKING.. ELSE RETURN
SKIP:
LD A,(PATH) J GET PATH NUMBER
LD (HL), A ;PLACE AT NEW CELL
CALL WALK J DO A RANDOM WALK
LD A,(PATH) ;UPDATE. ....
INC A
LD (PATH), A ;NEW PATH NUMBER
JR BUILDM ;CARRY ON BUILDING
Machine code miscellany 181
STARTs
CALL INTMAZE ;CLEAR MAZE
CALL BUILDM ;BUILD MAZE
CALL DISPLAY jDISPLAY MAZE
RET
NOUGHT;
db e, 0,0,0,0,0,0> 0
DB 0, 0. 0. 0< 0, 0^ 0. 255
DB 1,1,1.1.1.1.1» 1
DB li 1» 1> li 1/1< 25%j
MAZE: DS 32*22
BUILD: DS 32*22
END
Hexadecimal Listing
7530 C3 8D 76 00 00 00 3E 02
7538 CD 01 16 21 97 76 22 7B
7540 5C CD 90 75 21 B7 76 11
7548 E8 76 01 C0 02 36 03 ED
7550 B0 01 C0 02 36 00 ED B0
7558 AF 32 33 75 32 34 75 3C
7560 32 35 75 C9 00 00 00 00
7568 3A 65 75 0F 0F 0F C5 F5
7570 3A 66 75 47 3A 67 75 4F
7578 FI 80 81 07 07 32 64 75
7580 78 32 65 75 79 32 66 75
7588 3A 64 75 32 67 75 Cl C9
7590 3E 00 32 64 75 3E AD 32
7598 65 75 3E CE 32 66 75 ED
75A0 5F 32 67 75 C9 06 FF CD
75A8 C0 75 7E A7 28 04 B9 28
182 Machine code miscellany
75B0 01 09 3A 35 75 77 10 EF
75B8 3A 35 75 FE 01 20 E6 09
7500 CD 68 75 E6 03 A7 28 36
7508 FE 01 28 4D FE 02 28 16
75D0 3A 33 75 FE IF 28 E9 30
75D8 32 33 75 4E 23 7E B9 08
75E0 2B CD F5 75 23 09 3A 33
75E8 75 A7 28 D4 3D 32 33 75
75F0 4E 2B 7E B9 08 E5 11 40
75F8 FD 19 OB 8E El 09 3A 34
7600 75 A7 28 BC 3D 32 34 75
7608 4E 11 E0 FF 19 7E B9 08
7610 E5 11 40 Ft) 19 OB 86 El
7618 09 3A 34 75 FE 15 28 A0
7620 30 32 34 75 4E 11 20 00
7628 19 7E B9 08 A7 ED 52 CD
7630 10 76 11 20 00 19 09 E5
7638 05 D7 01 El 09 21 B7 76
7640 3E 16 F5 06 20 7E 06 90
7648 CD 37 76 23 10 F7 FI 3D
7650 20 F0 09 21 77 79 AF 32
7658 33 75 32 34 75 7E A7 28
7660 1C 23 3A 33 75 30 32 33
7668 75 FE 20 20 F0 AF 32 33
7670 75 3A 34 75 30 32 34 75
7678 FE 16 20 El 09 3A 35 75
7680 77 CD A5 75 3A 35 75 30
7688 32 35 75 18 06 CD 36 75
7690 CD 53 76 CD 3D 76 09 00
7698 00 00 00 00 00 00 00 00
76A0 00 00 00 00 00 00 FF 01
76A8 01 01 01 01 01 01 01 01
76B0 01 01 01 01 01 01 FF 00
7 6B8 00 00 00 00 00 00 00 00
7600 00 00 00 00 00 00 00 00
7608 00 00 00 00 00 00 00 00
76D0 00 00 00 00 00 00 00 00
76D8 00 00 00 00 00 00 00 00
76E0 00 00 00 00 00 00 00 00
76E8 00 00 00 00 00 00 00 00
Machine code miscellany 183
76F0 00 00 00 00 00 00 00 00
76F8 00 00 00 00 00 00 00 00
7790 00 00 00 00 00 00 00 00
7708 00 00 00 00 00 00 00 00
7710 00 00 00 00 00 00 00 00
7718 00 00 00 00 00 00 00 00
7720 00 00 00 00 00 00 00 00
7728 00 00 00 00 00 00 00 00
LARGE PRINT
1 wrote this routine to enhance my own basic programs. The routine
prints characters on the screen twice the width of normal characters.
I have 'patched' part of the basic operating system so that the large
characters can be PRiNTed from basic and will accept all the control
characters, such as ink, paper, at, tab, etc. To enable the large print
facility we first call the routine at address 30000. This gives the
Spectrum an additional channel, channel number 5. Then, to print
large characters to the screen we simply us the basic syntax:
PRINT#5;"STRING"
50 PRINT
60 PRINT "It can cope with con
trol codes"
70 PRINT #5;AT 5,5;"such as AT
II
75 PRINT
80 PRINT #5; INK 5; PAPER 2;"a
nd colours"
90 PRINT #5; INVERSE 1 $ TAB 7;"
inverse"
100 PRINT #5; FLASH 1;" as well
as -Flashing"
184 Machine code miscellany
Assembler Listing
ORG 30000
CHANINDi
DEFW PRINTD iPRINT OUT ROUTINE
DEFW REPORT! jINPUT ROUTINE.
DEFB ' D'
Machine code miscellany 185
PRINTDi
;WHEN BASIC CALLS THIS ROUTINE THE
;A REG CONTAINS CHAR NUMBER.
PQTV2D:
LD DE,POCONTD {SAVE FIRST OPERAND
;IN TVDATH
LD (TVDATH), A
JP POCHANGE {CHANGE ADDRESS OF
{CURRENT CHANNEL
ATTAB:
LD DE,P0TV2D {NEXT TIME ROUND GOTO POTVD
JR POTV1D {SAVE CHARACTER CODE
{IN TVDATAL
INKOVER:
LD DE,POCONTD {NEXT TIME POCONTD
POTV1D: LD < TVDATL), A {SAVE CONTROL CODE
JP POCHANGE {CHANGE OUTPUT ADDRESS
186 Machine code miscellany
LD HL, DUGD
PUSH HL
POP IX iEX HL WITH IX REG
LD 8,8 ;DATA COUNT
CALL 09F4H
LD A, 144 ;N0W PRINT USER DEFINED
;GRAPHICS
CALL 09F4H
POP HL ;RESTORE UDG
LD (UDG), HL
RET
FETD:
CALL NYBBLE ;D0 ONE NYBBLE
LD L, H
;NOW GET NEXT NYBBLE
NYBBLE:
LD B, 4 ; NUMBER OF BITS
NBIT: RRCA
DUGD: DS 8*2
END
Hexadecimal Listing
7570 E5 C3 03 0B 11 8B 75 32
7578 0F 5C C3 80 0A 11 74
7580 18 03 11 8B 75 32 0E 5C
7588 C3 80 0A 11 4F 75 C3 8A
7590 0A ED SB 36 5C 26 00 6F
7598 29 29 29 19 EB 21 DE 75
75A0 E5 DD El 06 08 1A C5 CD
75A8 CF 75 Cl DD 75 00 DD 74
75B0 08 DD 23 13 10 EF 2A 7B
75B8 5C E5 21 DE 75 A- A.. 7B 5C
75C0 3E 91 CD F4 09 3E 90 CD
75C8 F4 09 El oo
A*. 7B 5C C9 CD
75D0 D3 75 6C 06 04 0F CB 19
75D8 CB 29 10 F9 61 C9 00 00
75E0 00 00 00 00 00 00 00 00
75E8 00 00 00 00 00 00 82 75
75F0 E5 C3 03 0B 11 8B 75 32
75F8 0F 5C C3 80 0A 11 74 75
PIXEL SCROLL
This routine allows the user to scroll any portion of the screen to
either left or right. It has a 'wrap-around' effect, and so could be
most useful when writing arcade games with scrolling background
scenery of mountains, high rise flats or the like. When calling the
routine the hl register pair must point to the screen address of the
position from which you wish to scroll. The program below is a
demonstration program showing how the routine can be used from
basic:
Assembler Listing
ORG 32000D
SLEFT:
LD HL,ADD+NBYTES-■1
{POINT TO RIGHT HAND SIDE
LD C,NLINES {NUMBER OF LINES TO SCROLL
CHARX:
RL (HL) {SCROLL LEFT THROUGH
DEC HL iCARRY
DJNZ CHARX {REPEAT NBYTES TIMES
POP HL {RESTORE RIGHT HAND
{SIDE ADDRESS
LD A, 0 {SET A TO ZERO
190 Machine code miscellany
SRIGHTi
LD HL,ADD2 jPOINT TO LEFT HAND
;OF SCREEN
LD C,NLINES iNUMBER OF LINES TO SCROLL
LINER: PUSH HL 1 SAVE LEFT HAND
;SIDE ADDRESS
LD B,NBYTES ;NUMBER OF BYTES TO SCROLL
CHARR:
RR (HL) SCROLL RIGHT THROUGH CARRY
INC HL TO THE RIGHT
DJNZ CHARR REPEAT UNTIL DONE
NBYTES TIMES
POP HL RESTORE LEFT HAND SIDE
LD A, 0 ROTATE CARRY INTO LEFT
HAND SIDE
RRA
OR < HL)
LD ( HL ), A
CALL INCY ;NEXT PIXEL LINE DOWN
DEC C 1 ONE LESS PIXEL LINE
JR NZ,LINER jREPEAT UNTIL NO MORE LINES
RET
ADD A, 20H
LD L, A
RET C ;DO NOT ADJUST SECTOR SINCE
; WE HAVE GONE OVER
LD A, H ;WITHIN SECTOR SO
SUB 8 ;RE-ADJUST
LD H, A
RET
TEST:
CALL SLEPT ;SCROLL LEFT
CALL SRIGHT ;SCROLL RIGHT
RET
END
Hexadecimal Listing
7D00 C3 46 7D 21 IF 40 0E 10
7D08 E5 06 20 CB 16 2B 10 FB
7D10 El 3E 00 8F B6 77 CD 37
7D18 7D 0D 20 EC C9 21 00 48
7D20 0E 10 E5 86 20 CB IE 23
7D28 10 FE: El 3E 00 IF B6 77
7D30 CD 37 7D 0D 20 EC C9 24
7D38 7C E6 07 C0 7D C6 20 6F
7D40 D8 7C D6 08 67 C9 CD 03
7D48 7D CD ID 7D C9 00 00 00
7D50 00 00 00 00 00 00 00 00
7D58 00 00 00 00 00 00 00 00
7D60 00 00 00 00 00 00 00 00
7D68 00 00 00 00 00 00 00 00
7D70 00 00 00 00 00 00 00 00
7D78 00 00 00 00 00 00 00 00
In Chapter 11, this uses interrupts. The routine loads the ascii line
number into the system variable last-k every 1/50th of a second. This
causes the line number to be placed in the edit area and lower
screen of the Spectrum. To enable the auto line facility, key in the
instruction rand usr 32333. To turn off the auto line first delete the line
number currently being edited and then enter randusr3233@.
Don't forget to clear memory to keep the machine code safe.
clear32329 is suitable for this.
Assembler Listing
ORG 32330D
DISINT:
IM 1 ;ENABLE DEFAULT INTERRUPTS
RET
ENABLE;
XOR A j SET A TO ZERO
LD (STATE), A ;NOT OUTPUTTING ASCII CHARS
LD A, 28H ;SET I REG TO PAGE 2BH
LD Ii A
IM 2 ;AND ENABLE INTERRUPT MODE 2
El
RET
LD A, (ECHOE)
CP 20H
JR NZ,BYE
LD A,(ECHOE+1)
CP 17H
JR NZ,BYE ;ARE WE AT BOTTOM OF SCREEN
jARRIVE HERE IF ME ARE AT T BOTTOM OF THE SCREEN
0UTP2:
LD (LASTK), A ;PLACE CHAR IN LASTK
LD HL, FLAGS ;SIGNIFY WE ....
SET 5, (HL) ;PRESSED A KEY
RET
Machine code miscellany 195
END
Hexadecimal Listing
7 E4A ED 56 C9 AF 32 E9 7E 3E
7 E52 28 ED 47 ED 5E FB C9 00
7E5A 00 00 CD 38 00 F3 F5 C5
7E62 D5 E5 DD E5 3A E9 7E A7
7E6A 20 31 3A 46 5C FE FF 20
7E72 37 3A 82 5C FE 20 20 30
7E7A 3A 83 5C FE 17 20 29 3A
7E82 08 5C FE 0D 20 22 3E 04
7E8A 32 E9 7E 21 B2 7E 22 E7
7E92 7E 2A 49 5C 11 0A 00 19
7E9A 22 EA 7E 3A E9 7E 3D 32
7EA2 E9 7E 2A EA 7E CD BA 7E
7EAA DD El El D1 Cl FI FB C9
7EB2 E8 03 64 00 0A 00 01 00
7EBA DD 2A E7 7E DD 4E 00 DD
7EC2 46 01 3E 2F A7 3C ED 42
7ECA 30 FB 09 22 EA 7E CD DE
7ED2 7E DD 23 DD 23 DD E5 El
7EDA 22 E7 7E C9 32 08 5C 21
7EE2 3B 5C CB EE C9 00 00 00
7EEA 00 00 3A 46 5C FE FF 20
7EF2 37 3A 82 5C FE 20 20 30
7EFA 3A 83 5C FE 17 20
SORT
Another program which can be used with basic, this sort routine
which allows you to sort strings into alphabetical order. The routine,
when called in basic searches for the dimensional array as. It should
be first set up with the number of objects to sort and the length of
each string. If the string is not found or the length is too large then it
will exit from the sort routine with an appropriate error message.
When you wish to sort the string you simply call the machine code
from basic by using the instruction rand usr 32000. This will then sort
196 Machine code miscellany
out the string in ascending order. The method used to sort out the
strings is known as a 'Bubble Sort'. This method of sorting is not the
most efficient. However, under one second to sort out 100 strings of
25 characters in length is not slow!
The basic listing below demonstrates how the machine code pro¬
gram is used:
5 LET sort=32000
10 DIM a$(100,25)
20 FOR p=l to 100
30 FOR c—1 to 25
40 LET a$(p,c)=CHR$ ((RND*26)+
65)
50 NEXT c
60 NEXT p
70 PRINT #0;"Press L to list,S
to sort"
80 LET k*=INKEY$: IF k$="" THE
N GO TO 80
90 IF k$="L" OR k$="l" THEN GO
SUB 120: GO TO 70
100 IF k$<>"s“ AND k*<>"S" THEN
GO TO 80
110 CLS: PRINT "sortingRANDOM
IZE USR sort: BEEP 1,1: GO SUB 1
20: STOP
120 FOR p=l TO 100
130 PRINT a$(p)
140 NEXT p
150 RETURN
Assembler Listing
QRG 32800D
VARS EflU 23627D
START:
LD HL,(VARS) ;SET HL TO POINT TO
;VARIABLE AREA
TEST: LD A, (HL) ;GET 1ST BYTE OF VARIABLE
CP 128 jEND OF VARS MARKER?
JR Z,NOTFOUND ; FINISHED LOOKING AT VARS
Machine code miscellany 197
;010 OR 110
INC HL
LD E, < HL) ;GET LENGTH LOW
INC HL
LD D, (HL) ;GET LENGTH HIGH
INC HL
ADD HL, DE j SKIP PASS VARIABLE
JP TEST ;TEST FOR NEXT VARIABLE
NOTFOUND:
RST 08
DB 01 ;VARIABLE NOT FOUND ERROR!
ERROR:
RST 08 ;SUBSCRIPT WRONG ERROR!
DB 02
ADI19:
LD DE, 19 ;GO PASS VARIABLE
ADD HL, DE
JP TEST ;TEST NEXT VARIABLE
SKIPC:
INC HL {SKIP PASS VARIABLE,
BIT 7, < HL) {NAME
JR Z,SKIPC {TILL BIT 7 IS SET
ADISIX:
LD DE, 6 {GO PASS VARIABLE
198 Machine code miscellany
ADD HL, DE
JP TEST jTEST NEXT VARIABLE
FOUND;
INC HL
INC HL
INC HL 1 POINT TO NUMBER OF
;MUST BE TWO OR LES
LD A, (HL)
CP 2
JR NZ,ERROR jSHOULD BE TWO DIMENSIONS.
LD A, C ;SAVE SIZE
LD (SIZE), A
SORT:
rnitPAPr *
PUSH HL {SAVE REGISTERS
PUSH DE
PUSH BC
COMPARSi {COMPARE STRINGS
{ONE POINTED BY THE HL PAIR
;AND ONE POINTED BY
{THE DE PAIR
200 Machine code miscellany
SWAP:
; SWAP THE TWO STRINGS POINTED
; BY THE HL PAIR AND THE DE PAIR
LD C, A iGET COUNTER
LDIR ;MOVE TO SECOND STRING
Hexadecimal Listing
7D00 2A 4B 5C 7E FE 80 28 1C
7D08 FE Cl CA 3B 7D E6 E0 FE
7D10 60 28 21 FE E0 28 11 FE
7D18 A0 28 14 23 5E 23 56 23
7D20 19 C3 03 7D CF 01 CF 02
7D28 11 13 00 19 C3 03 7D 23
7D30 CB 7E 28 FB 11 06 00 19
7D38 C3 03 7D 23 23 23 7E FE
7D40 02 20 E3 23 46 23 7E A7
7D48 20 DC 23 4E 23 7E A7 20
7D50 D5 23 79 32 A7 7E C5 AF
7D58 32 A6 7E E5 E5 59 16 00
7D60 19 EB El CD 77 7D DC 87
7D68 7D EB 10 F0 El Cl 3A A6
7D70 7E A7 C8 05 20 E0 C9 E5
7D78 D5 C5 1A 96 20 05 23 13
7DB0 0D 20 F7 Cl D1 EL C9 C5
7DB8 D5 E5 3A A7 7E 4F 06 00
7D90 11 A7 7D ED B0 D1 D5 4F
7D98 ED B0 21 A7 7D 4F ED B0
7DA0 32 A6 7E El D1 Cl C9 00
7DA8 00 00 00 00 00 00 00 00
7DB0 00 00 00 00 00 00 00 00
7DB8 00 00 00 00 00 00 00 08
202 Machine code miscellany
RECURSION
Assembler Listing
ORG 32000D
PLS:
INC IX ;POINT
INC IX ;TO RETURN POSTION
INC IX
LD L, H
;ADJUST SUBSTRING ADDRESS
LD H,E
POP AF i GET RID OF OLD STRING
;ADDRESS
PUSH IX j PUT IN NEW STRING ADDRESS
FRERE:
DB 61
DEFW FRERE1
DB 01
DEFW FRERE1
FRERE1: DB 01
DEFW TUNE1
DB 01
DEFW TUNE1
DB 01
DEFW TUNE2
DB 01
DEFW TUNE2
204 Machine code miscellany
DB 01
DEFW TUNE3
DB 01
DEFW TUNE3
DB 01
DEFH TUNE4
DB 01
DEFH TUNE*?
TUNE1:
DEFW 66AH
DEFW 105H
DEFW 5B3H
DEFW 125H
DEFW 560H
DEFW 9BH
DEFW 5B3H
DEFW 92H
DEFW 66AH
DEFW 105H
DEFW 00
TUNE2:
DEFW 560H
DEFW 137H
DEFW 4C6H
DEFW 15DH
DEFW 43DH
DEFW 188H
DEFW 00
Machine code miscellany 205
TUNE3;
DEFU 43DH
DEFU 126H
DEFW 3FFH
DEFU 67H
DEFU 43DH
DEFU 0C4H
DEFU 4C6H
DEFU 0AEH
DEFU 560H
DEFU 9BH
DEFU 5B3H
DEFU 92H
DEFU 66AH
DEFU 105H
DEFU 00
TUNE4i
DEFU 66AH
DEFU 10SH
DEFU 89AH
DEFU 0C4H
DEFU 66AH
DEFU 20AH
DEFU 00
END
Hexadecimal Listing
7D00 DD 21 41 7D CD 08 7D C9
7D08 DD E5 DD 6E 00 DD 66 01
7D10 DD 5E 02 DD 56 03 7D FE
7D18 01 28 0E 38 21 CD B5 03
7D20 DD El 11 04 00 DD 19 18
7D28 DF DD 23 DD 23 DD 23 6C
7D30 63 FI DD E5 E5 DD El CD
7D38 08 7D DD El 18 CA DD El
206 Machine code miscellany
7D40 C9 01 47 70 01 47 70 01
7D48 5F 7D 01 5F 70 01 75 70
7050 01 75 70 01 83 70 01 83
7D58 7D 01 A1 70 01 A1 70 6A
7060 06 05 01 B3 05 25 0.1 60
7D68 05 9B 00 B3 05 92 00 6A
7D70 06 05 01 00 00 60 05 37
7D78 01 C6 04 5D 01 30 04 88
7D80 01 00 00 3D 04 26 01 FF
7D88 03 67 00 3D 04 04 00 06
7D90 04 AE 00 60 05 9B 00 B3
7D98 05 92 00 6A 06 05 01 00
7OA0 00 6A 06 05 01 9A 08 04
7DA8 00 6A 06 0A 02 00 00 60
7DB0 63 FI DD E5 E5 DD El CD
7DB8 08 7D DD El 18 CA DO El
7DC0 C9 01 47 70 01 47 7D 01
7DC8 5F 7D 01 5F 7D 01 75 70
7DD0 01 75 7D 01 83 7D 01 83
7DD8 7D 01 A1 7D 01 A1 70 6A
7DE0 06 05 01 B3 05 25 01 60
7DE8 05 9B 00 B3 05 92 00 6A
7DF0 06 05 01 00 00 60 05 37
7DF8 01 C6 04 50 01 3D 04 88
Appendix 1
Z80 instructions listed
by mnemonic
8E 142 ADC A, (HL)
DD 8E dd 221 142 dd ADC A,(IX 1 d)
FD 8E dd 253 142 dd ADC A,(IY,d)
8F 143 ADC A,A
88 136 ADC A,B
89 137 ADC A,C
8A 138 ADC A,D
8B 139 ADC A/E
8C 140 ADC A,H
8D 141 ADC A,|_
CE XX 206 XX ADC A,N
ED 4A 237 74 ADC HL,BC
ED 5A 237 90 ADC HL,DE
ED 6A 237 106 ADC HL,HL
ED 7A 237 122 ADC HL,SP
86 134 ADD A, (HL)
DD 86 dd 221 134 dd ADD A , (I X 1 d )
FD 86 dd 253 134 dd ADD A,(IY1d)
87 135 ADD A,A
80 128 ADD A,B
81 129 ADD A,C
82 130 ADD A,D
83 131 ADD A/E
84 132 ADD A,H
85 133 ADD A/L
C6 XX 198 XX ADD A,N
09 9 ADD HL,BC
19 25 ADD HL,DE
29 41 ADD HL,HL
39 57 ADD HL,SP
DD 09 221 9 ADD IX,BC
DD 19 221 25 ADD IX,DE
DD 29 221 41 ADD IX,IX
DD 39 221 57 ADD IX,SP
FD 09 253 9 ADD IY , B C
208 Appendix 1 - Z80 instructions listed by mnemonic
2B 43 DEC HL
DD 2B 221 43 DEC IX
FD 2B 253 43 DEC IY
2D 45 DEC L
3B 59 DEC SP
F3 243 DI
10 16 XX DJNZ N
FB 251 El
E3 227 EX (SP),HL
DD E3 221 227 EX (SP),IX
FD E3 253 227 EX (SP),IY
08 8 EX A F,A F 1
EB 235 EX DE,HL
D9 217 EXX
76 118 HALT
ED 46 237 70 IM 0
ED 56 237 86 IM 1
ED 5E 237 94 IM 2
ED 78 237 120 IN A,(C)
DB XX 219 XX IN A,(N)
ED 40 237 64 IN B,(C)
ED 48 237 72 IN C,(C)
ED 50 237 80 IN D,(C)
ED 58 237 88 IN E,(C)
ED 60 237 96 IN H,(C)
ED 68 237 104 IN L,(C)
34 52 INC (H L)
DD 34 dd 221 52 dd INC (I X 1 d)
FD 34 dd 253 52 dd INC (IY r d)
3C 60 INC A
04 4 INC B
03 3 INC BC
oc 12 INC C
14 20 INC D
13 19 INC DE
1 C 28 INC E
24 36 INC H
23 35 INC HL
DD 23 221 35 INC IX
FD 23 253 35 INC IY
2C 44 INC L
33 51 INC SP
ED AA 237 170 IND
ED BA 237 186 INDR
212 Appendix 1 -Z80 instructions listed by mnemonic
49 73 LD C,C
4A 74 LD C,D
4B 75 LD C,E
4C 76 LD C,H
4D 77 LD C,L
56 86 LD D,(HL)
DD 56 dd 221 86 dd LD D,(IX’d )
FD 56 dd 253 86 dd LD D,(IY'd )
57 87 LD D,A
50 80 LD D,B
51 81 LD D,C
52 82 LD D,D
53 83 LD D,E
54 84 LD D,H
55 85 LD D,L
16 XX 22 XX LD D,N
ED 5B XXXX 237 91 XXXX LD DE,(NN)
11 17 XXXX LD DE,NN
5E 94 LD E,(HL)
DD 5E dd 221 94 dd LD E,(IX1d )
FD 5E dd 253 94 dd LD E , (I Y 1 d )
5F 95 LD E,A
58 88 LD E,B
59 89 LD E,C
5A 90 LD E,D
5B 91 LD E,E
5C 92 LD E,H
5D 93 LD E,L
IE XX 30 XX LD E,N
66 102 LD H,(HL)
DD 66 dd 221 102 dd LD H,(IX1d)
FD 66 dd 253 102 dd LD H,(IY’d)
67 103 LD H,A
60 96 LD H,B
61 97 LD H,C
62 98 LD H,D
63 99 LD H,E
64 100 LD H,H
65 101 LD H,L
26 XX 38 XX LD H,N
2A XXXX 42 XXXX LD HL,(NN)
ED 6B XXXX 237 107 XXXX LD H L,C NN)
21 XXXX 33 XXXX LD HL,NN
ED 47 237 71 LD I,A
Appendix 1 -Z80 instructions listed by mnemonic 215
CB 1 E 203 30 RR (HL)
DD CB dd 1 E 221 203 dd 30 RR CIX1d)
FD CB dd 1 E 253 203 dd 30 RR CIY1d)
CB 1 F 203 31 RR A
CB 18 203 24 RR B
CB 19 203 25 RR C
CB 1A 203 26 RR D
CB 1 B 203 27 RR E
CB 1 C 203 28 RR H
CB 1 D 203 29 RR L
1 F 31 RRA
CB OE 203 14 RRC (HL)
DD CB dd OE 221 203 dd 14 RRC (I X1d)
FD CB dd OE 253 203 dd 14 RRC (IY'd)
CB OF 203 15 RRC A
CB 08 203 8 RRC B
CB 09 203 9 RRC C
CB OA 203 10 RRC D
CB OB 203 11 RRC E
CB OC 203 12 RRC H
CB OD 203 13 RRC L
OF 15 RRCA
ED 67 237 103 RR D
C7 199 RST 0
D7 215 RST 10
DF 223 RST 18
E7 231 RST 20
EF 239 RST 28
F7 247 RST 30
FF 255 RST 38
CF 207 RST 8
9E 158 SBC A,(H L)
DD 9E dd 221 158 dd SBC A,(IX1d)
FD 9E dd 253 158 dd SBC A,(IY1d)
9F 159 SBC A,A
98 152 SBC A,B
99 153 SBC A,C
9A 154 SBC A,D
9B 155 SBC A,E
9C 156 SBC A,H
9D 157 SBC A,L
DE XX 222 XX SBC A,N
ED 42 237 66 SBC HL,BC
ED 52 237 82 SBC HL,DE
220 Appendix 1 - Z80 instructions listed by mnemonic
CB 21 203 33 SLA C
CB 22 203 34 SLA D
CB 23 203 35 SLA E
CB 24 203 36 SLA H
CB 25 203 37 SLA L
CB 2E 203 46 S RA (HL)
DD CB dd 2E 221 203 dd 46 S RA (I X1d)
FD CB dd 2E 253 203 dd 46 SRA (IY'd)
CB 2F 203 47 SRA A
CB 28 203 40 S RA B
CB 29 203 41 S RA C
CB 2A 203 42 S RA D
CB 2B 203 43 S RA E
CB 2C 203 44 SRA H
CB 2D 203 45 S RA L
CB 3E 203 62 SRL (HL)
DD CB dd 3E 221 203 dd 62 SRL CIX 1 d)
FD CB dd 3E 253 203 dd 62 SRL (IY'd)
CB 3F 203 63 SRL A
CB 38 203 56 SRL B
CB 39 203 57 SRL C
CB 3A 203 58 SRL D
CB 3B 203 59 SRL E
CB 3C 203 60 SRL H
CB 3D 203 61 SRL L
96 150 SUB (HL)
DD 96 dd 221 150 dd SUB (I X ' d )
FD 96 dd 253 150 dd SUB (IY'd)
97 151 SUB A
90 144 SUB B
91 145 SUB C
92 146 SUB D
93 147 SUB E
94 148 SUB H
95 149 SUB L
D6 XX 214 XX SUB N
EE XX 238 XX XOR N
AE 174 XOR (HL)
DD AE dd 221 174 dd XOR (IX1d)
FD AE dd 253 174 dd XOR (IY'd)
AF 175 XOR A
A8 168 XOR B
A9 169 XOR C
Appendix 1 -Z80 instructions listed by mnemonic 223
AA 170 XOR D
AB 171 XOR E
AC 172 XOR H
AD 173 XOR L
Appendix 2
Z80 instructions
listed by opcode
00 0 NOP
01 XXXX 1 XXXX LD BC,NN
02 2 LD CBC),A
03 3 INC BC
04 4 INC B
05 5 DEC B
06 XX 6 XX LD B,N
07 7 RLCA
08 8 EX A F,A F 1
09 9 ADD HL,BC
OA 10 LD A,(BC)
OB 11 DEC BC
OC 12 INC C
OD 13 DEC C
OE XX 14 XX LD C,N
OF 15 RRCA
10 16 XX DJNZ N
11 17 XXXX LD DE,NN
12 18 LD (D E),A
13 19 INC DE
14 20 INC D
15 21 DEC D
16 XX 22 XX LD D,N
17 23 R LA
18 XX 24 XX JR N
19 25 ADD HL,DE
1A 26 LD A , ( D E )
IB 27 DEC DE
1C 28 INC E
1 D 29 DEC E
1 E XX 30 XX LD E,N
1 F 31 RRA
20 XX 32 XX JR NZ,N
21 XXXX 33 XXXX LD HL,NN
22 XXXX 34 XXXX LD <NN),HL
Appendix 2~Z80 instructions listed by opcode 225
23 35 INC HL
24 36 INC H
25 37 DEC H
26 XX 38 XX LD H,N
27 39 DAA
28 XX 40 XX J R Z,N
29 41 ADD HL,HL
2A XXXX 42 XXXX LD HL,(NN)
2B 43 DEC HL
2C 44 INC L
20 45 DEC L
2E XX 46 LD L,N
2F 47 CPL
30 XX 48 XX J R NC,N
31 XXXX 49 XXXX LD SP,NN
32 XXXX 50 XXXX LD (NN),A
33 51 INC SP
34 52 INC (HL)
35 53 DEC (HL)
36 XX 54 XX LD (HL),N
37 55 SCF
38 XX 56 XX JR C,N
39 57 ADD HL,SP
3A XXXX 58 XXXX LD A,(NN)
3B 59 DEC SP
3C 60 INC A
3D 61 DEC A
3E XX 62 XX LD A , N
3F 63 CCF
40 64 LD B,B
41 65 LD B,C
42 66 LD B,D
43 67 LD B,E
44 68 LD B,H
45 69 LD B/L
46 70 LD B, (HL)
47 71 LD B,A
48 72 LD C,B
49 73 LD C,C
4A 74 LD C,D
4B 75 LD C,E
4C 76 LD C,H
40 77 LD C,L
4E 78 LD C, (HL)
226 Appendix 2 - Z80 instructions listed by opcode
4F 79 LD C , A
50 80 LD D,B
51 81 LD D,C
52 82 LD D , D
53 83 LD D,E
54 84 LD D,H
55 85 LD D,L
56 86 LD D,(HL)
57 87 LD D,A
58 88 LD E,B
59 89 LD E,C
5A 90 LD E,D
5B 91 LD E,E
5C 92 LD E,H
5D 93 LD E/L
5E 94 LD E,(HL)
5F 95 LD E,A
60 96 LD H,B
61 97 LD H,C
62 98 LD H,D
63 99 LD H,E
64 100 LD H,H
65 101 LD H/L
66 102 LD H,(HL)
67 103 LD H,A
68 104 LD L/B
69 105 LD L,C
6A 106 LD L,D
6B 107 LD L,E
6C 108 LD L,H
6D 109 LD L,L
6E 110 LD L,(HL>
6F 1 1 1 LD L,A
70 112 LD (HL),B
71 113 LD (HL) ,C
72 114 LD (HL),D
73 115 LD (HL),E
74 116 LD (HL),H
75 117 LD (HL),L
76 118 HALT
77 119 LD ( H L),A
78 120 LD A , B
79 121 LD A , C
7A 122 LD A , D
Appendix 2-Z80 instructions listed by opcode 227
7B 123 LD A ,E
7C 124 LD A
7D 125 LD A ,L
7E 126 LD A , (HL)
7F 127 LD A ,A
80 128 ADD A,B
81 129 ADD A,C
82 130 ADD A,D
83 131 ADD A,E
84 132 ADD A,H
85 133 ADD A,L
86 134 ADD A , C H L)
87 135 ADD A,A
88 136 ADC A,B
89 137 ADC A,C
8A 138 ADC A,D
8B 139 ADC A/E
8C 140 ADC A,H
8D 141 ADC A,L
8E 142 ADC A , ( H L)
8F 143 ADC A,A
90 144 SUB B
91 145 SUB C
92 146 SUB D
93 147 SUB E
94 148 SUB H
95 149 SUB L
96 150 SUB (HL)
97 151 SUB A
98 152 SBC A,B
99 153 SBC A,C
9A 154 SBC A, D
9B 155 SBC A/E
9C 156 SBC A,H
9D 157 SBC A,L
9E 158 SBC A, (HL)
9F 159 SBC A,A
AO 160 AND B
A1 161 AND C
A2 162 AND D
A3 163 AND E
A4 164 AND H
A5 165 AND L
A6 166 AND (HL)
228 Appendix 2- Z80 instructions listed by opcode
A7 167 AND A
A8 168 XOR B
A9 169 XOR C
AA 170 XOR D
AB 171 XOR E
AC 172 XOR H
AD 173 XOR L
AE 174 XOR <HL)
AF 175 XOR A
BO 176 OR B
B1 177 OR C
B2 178 OR D
B3 179 OR E
B4 180 OR H
B5 181 OR L
B6 182 OR (HL)
B7 183 OR A
B8 184 CP B
B9 185 CP C
BA 186 CP D
BB 187 CP E
BC 188 CP H
BD 189 CP L
BE 190 CP (HL)
BF 191 CP A
CO 192 RET NZ
Cl 193 POP BC
C2 xxxx 194 XXXX JP NZ,NN
C3 xxxx 195 XXXX JP NN
C4 xxxx 196 XXXX CALL NZ,NN
C5 197 PUSH BC
C6 XX 198 XX ADD A,N
C7 199 RST 0
C8 200 RET Z
C9 201 RET
CA xxxx 202 XXXX JP Z,NN
CC xxxx 204 XXXX CALL Z,NN
CD xxxx 205 XXXX CALL NN
CE XX 206 XX ADC A,N
CF 207 RST 8
DO 208 RET NC
D1 209 POP DE
D2 xxxx 210 JP NC,NN
D3 XX 211 XX OUT (N),A
Appendix 2 - Z80 instructions listed by opcode 229
CB 03 203 3 RLC E
CB 04 203 4 RLC H
CB 05 203 5 RLC L
CB 06 203 6 RLC (HL)
CB 07 203 7 RLC A
CB 08 203 8 RRC B
CB 09 203 9 RRC C
CB 0A 203 10 RRC D
CB OB 203 1 1 RRC E
CB OC 203 12 RRC H
CB OD 203 13 RRC L
CB OE 203 14 RRC CHL)
CB OF 203 15 RRC A
CB 10 203 16 RL B
CB 1 1 203 17 RL C
CB 12 203 18 RL D
CB 13 203 19 RL E
CB 14 203 20 RL H
CB 15 203 21 RL L
CB 16 203 22 RL CHL)
CB 17 203 23 RL A
CB 18 203 24 RR B
CB 19 203 25 RR C
CB 1 A 203 26 RR D
CB IB 203 27 RR E
CB 1C 203 28 RR H
CB 1 D 203 29 RR L
CB 1 E 203 30 RR CHL)
CB 1 F 203 31 RR A
CB 20 203 32 SLA B
CB 21 203 33 SLA C
CB 22 203 34 SLA D
CB 23 203 35 SLA E
CB 24 203 36 SLA H
CB 25 203 37 SLA L
CB 26 203 38 SLA CHL)
CB 27 203 39 SLA A
CB 28 203 40 SRA B
CB 29 203 41 SR A C
CB 2A 203 42 SRA D
CB 2B 203 43 SRA E
CB 2C 203 44 SRA H
CB 2D 203 45 SRA L
CB 2E 203 46 S RA CHL)
Appendix 2-Z80 instructions listed by opcode 231
CB 2F 203 47 SRA A
CB 38 203 56 SRL B
CB 39 203 57 SRL C
CB 3A 203 58 SRL D
CB 3B 203 59 SRL E
CB 3C 203 60 SRL H
CB 3D 203 61 SRL L
CB 3E 203 62 SRL (HL)
CB 3F 203 63 SRL A
CB 40 203 64 BIT 0,B
CB 41 203 65 BIT 0,C
CB 42 203 66 BIT 0,D
CB 43 203 67 BIT 0,E
CB 44 203 68 BIT 0,H
CB 45 203 69 BIT 0,L
CB 46 203 70 BIT 0, (HL)
CB 47 203 71 BIT 0,A
CB 48 203 72 BIT 1,B
CB 49 203 73 BIT i,c
CB 4A 203 74 BIT 1/D
CB 4B 203 75 BIT 1,E
CB 4C 203 76 BIT 1 /H
CB 4D 203 77 BIT 1,L
CB 4E 203 78 BIT 1 , (HL)
CB 4F 203 79 BIT 1/A
CB 50 203 80 BIT 2/B
CB 51 203 81 BIT 2/C
CB 52 203 82 BIT 2/D
CB 53 203 83 BIT 2/E
CB 54 203 84 BIT 2,H
CB 55 203 85 BIT 2/L
CB 56 203 86 BIT 2, (HL)
CB 57 203 87 BIT 2,A
CB 58 203 88 BIT 3,B
CB 59 203 89 BIT 3/C
CB 5A 203 90 BIT 3,D
CB 5B 203 91 BIT 3,E
CB 5C 203 92 BIT 3,H
CB 5D 203 93 BIT 3/L
CB 5E 203 94 BIT 3, (HL)
CB 5F 203 95 BIT 3,A
CB 60 203 96 BIT 4,B
CB 61 203 97 BIT 4/C
CB 62 203 98 BIT 4,D
232 Appendix 2 - Z80 instructions listed by opcode
Flags
Flag is unchanged by operation.
* Flag is affected according to result of operation.
P P/V is set according to parity result.
V P/V is set according to the overflow result.
0 Flag is set to zero
1 Flag is set to one.
# Result of flag unknown.
f Contents of the interrupt flip flop.
Addressing
s Any 8 bit addressing mode A, B, C, D, E, FI, L, (FIL), (IX+dd),
(lY+dd)
r Any 8 bit register A, C, D, E, H, L
b Bit number 0-7
RR Any 16 bit register,
n Any 8 bit number
JP FSTART
ALTER E0U 39D
/ /
COMMA E8U I
SAVEB: LD A, 0FFH
;IX POINTS TO START OF BLOCK
jDE CONTAINS NUMBER OF BYTES
CALL 04C2H
RET
LOADS: SCF
LD A,0FFH
CALL 0556H
RET
HEADIN:
LD DE, 17
LD IX, HEADER
XOR A
SCF
CALL 0556H
LD A,(HEADER+11)
LD C, A
LD A,' *'
LD (HEADER+11), A
CALL CRLF
LD DE,HEADER+1
CALL PRTSTR
LD A,' '
CALL PRTCHR
244 Appendix4-Spectrum monitor-assembler listing
EX DE, HL
LD (HL), C
INC HL
INC HL
INC HL
LD A, (HL >
CALL HEXO
DEC HL
LD A, (HL)
CALL HEXO
DEC HL
LD A,' '
CALL PRTCHR
LD A. (HL)
CALL HEXO
DEC HL
LD A, (HL)
CALL HEXO
CALL CRLF
RET
HEAOQUT:
LD DE, 17
LD IX,HEADER
XOR A
CALL 04C2H
RET
KEY: PUSH HL
PUSH BC
PUSH DE
CALL WAITS ; PAUSE FOR A WHILE
WAITK: LD A, (23611) ;LOOK AT FLAGS
BIT 5, A
JR Z, WAITK ;NO KEY PRESSED
RES 5, A ;RESET FLAG
LD (23611), A
POP DE
Appendix4- Spectrum monitor-assembler listing 245
POP BC
POP HL
RET
GETKEY:
CALL KEY
LD A, (23560) LOOK AT LAST-K
CALL PRTCHR
RET
0PENCH2:
LD A,02 ;OPENS CHANNEL 'S'
t
/FOR PRINTING
CALL 1601H
RET
PRTCHR:
PUSH AF
PUSH AF
XOR A
LD (23692), A
POP AF
RST 10H
POP AF
RET
PRTSTR:
LD A, (DE) GET CHARACTER
CP IS THIS THE END
OF A STRING?
RET 1 YES THEN RETURN
CALL PRTCHR PRINT CHARACTER
INC DE POINT TO NEXT CHARACTER
JR PRTSTR
FSTART:
LD SP,STACK
CALL 0PENCH2
LD DE,HONE
CALL PRTSTR
LD DE,WELCHESS
CALL PRTSTR
CALL CRLF
VERYSTART:
INITV2: LD SP,STACK
LD A, 8
LD (23658), A ;CAPS ON
246 Appendix4-Spectrum monitor-assembler listing
LD HL, ERRSP
LD <HL), LQW( VERYSTART)
INC HL
LD (HL)( HIGH( VERYSTART)
DEC HL
LD (23613), HL
CALL CRLF
LD A,' >'
CALL PRTCHR
VECTBL:
DEFW ERROR
DEFW ERROR
DEFW ERROR
DEFW DUMP ; DUMP
DEFW MODIFY ;EDIT MEMORY
DEFW FILL ; FILL
DEFW GOTO ; GOTO
DEFW HUNT ; HUNT
DEFW IDENT ;IDENTIFY FILENAME
DEFW ERROR
DEFW ERROR
Appendix4-Spectrum monitor-assembler listing 247
WAITS:
LD BC,8000H
LD DE, 4000H
LD HL,4000H
LDIR
RET
MODIFY:
CALL GETEXPR1 ;GET START ADDRESS
MODIFB: CALL HEXOD jOUTPUT START ADDRESS
LD A,' '
CALL PRTCHR
LD A, (HL)
CALL HEXO
LD A,' '
CALL PRTCHR
PUSH HL
CALL HEX I
POP HL
LD (HL), A
INC HL
CALL CRLF
JR MODIFY ;LOOP UNTIL FORCED
;OUT BY AN ERROR
LD A,' '
CALL PRTCHR
CALL HEXD
PUSH HL
POP DE ;E2
POP HL ;GET El
RET iE1=HL E2=0E
GETEXPR3!
CALL GETEXPR2
PUSH HL
PUSH DE
CALL GETEXPR1
POP DE
POP HL
RET
GETEXPR1!
LD A,' '
CALL PRTCHR
CALL HEXD
PUSH HL
POP BC
CALL CRLF
RET ;E1=HL E2=DE E3=BC
LOADBYTES:
LD A,' '
CALL PRTCHR
CALL GETEXPR2
LD (DESTT), HL
LD A, E
OR D
JP Z,ERRORS
LD (LENTT), DE
LD DE,L0ADMESS
CALL PRTSTR
Appendix 4 - Spectrum monitor-assembler listing 249
LD DE,FILENAME
CALL PRTSTR
GETFILE:
CALL HEADIN
LD DE, HEADER+1
LD HL, FILENAME
LD B, 10
COMPF: LD A, (DE)
RES 5, A
LD C, (HL)
RES 5,C
CP C
JR NZ,GETFILE
INC HL
INC DE
DJNZ COMPF
DESTTi DEFW 0
LENTT: DEFW 0
SAVEBYTES:
LD A,' '
CALL PRTCHR
CALL GETEXPR2 ;GET 2 VALUES START
;AND NUMBER OF BYTES
a
OR
(HEADER+13), HL
A, E
D
JP Z,ERRORS
LD (HEADER+11), DE
LD DE,SAVEMESS
CALL PRTSTR
CALL WAITS
CALL WAITS
CALL WAITS
CALL GETKEY
LD A, 3
LD DE,HEADER
LD HL,FILENAME
LD < DE), A
INC DE
LD BC, 10
LDIR
CALL HEADOUT
CALL WAITS
CALL WAITS
CALL WAITS
LD IX, (HEADER+13)
LD DE, (HEADER+11)
CALL SAVEB
RET
Appendix 4 - Spectrum monitor-assembler listing 251
HEXAS: PUSH HL
CALL HEXO
LD A,' '
CALL PRTCHR
POP HL
RET
LINE: LD B, 8
NBYTE: LD A, (HL)
CALL HEXAS
INC HL
DJNZ NBYTE
CALL CRLF
RET
DUMP:
LD A,' '
CALL PRTCHR
CALL GETEXPR1
ALOCK: LD C, B
BLOCK: CALL HEXOD
LD A,' *
CALL PRTCHR
CALL PRTCHR
CALL LINE
DEC C
JR NZ,BLOCK
CALL CRLF
CALL CRLF
CALL GETKEY
CP CR
JR Z, ALOCK
RET
PINE: LD B, 21
PBYTE: LD A, (HL)
CP 32
JR C,SBQGGY-2
CP 128
JR C,SBOGGY
LD A,'.'
252 Appendix4-Spectrum monitor-assembler listing
PUMP:
LD A,' '
CALL PRTCHR
CALL GETEXPR1
PALOCK: LD C, 8
PLOCK: CALL HEXOD
LD A,' '
CALL PRTCHR
CALL PRTCHR
CALL PINE
DEC C
JR NZ, PLOCK
CALL CRLF
CALL CRLF
CALL GETKEY
CP CR
JR Z, PALOCK
RET
ERROR: NOP
NOTIMP: PUSH DE
LD DE,NOTMESS
CALL PRTSTR
POP DE
RET
1ROUNTINES
; HEXO OUTPUT HEX NUMBER IN ACCUMULATOR
;HEX0D OUTPUT HEX WORD IN HL
jHEXI INPUT HEX NUMBER PUT IN ACCUMULATOR
;HEXD INPUT HEX WORD AND PUT INTO HL
Appendix 4-Spectrum monitor-assembler listing 253
HEXOO;
LD A, H
CALL HEXO
LD A, L
HEXO:
LD E, A
SRL A ;GET TOP FOUR BITS
{INTO LOWER NYBBLE
SRL A
SRL A
SRL A
CALL CONV {CONVERT TO ASCII
{RETURNS ASCII VALUE IN A
LD A, E {GET ORIGINAL VALUE
AND BFH j MASK OFF LOWER FOUR BITS
{CONVERT LAST HEX DIGIT
CONV:
ADD A, 30H
CP 3AH {IS DIGIT IN RANGE 0-??
JP N,DECD jYES THEN PRINT AND RETURN
;IN THE RANGE : 10-15 SO CONVERT TO A-F
ADD A, 7
DECDi CALL PRTCHR {PRINT A HEX DIGIT
RET
ERRORS; JP VERYSTART
HEXI;
CALL GETKEY
CALL C0NV2
LD E, A
CALL GETKEY
CALL C0NV2
SLA E {MOVE LOWER FOUR BITS UP
SLA E
SLA E
SLA E
OR E {MERGE IN SECOND DIGIT
RET
C0NV2: AND A
SBC A, 30H
CP BAH
254 Appendix4-Spectrum monitor-assembler listing
RET C
AND A
SBC A,7
CP 10H
JR NC, ERRORS
RET
LD A,7 '
CALL PRTCHR
CALL GETEXPR2
LD A,7 7
CALL PRTCHR
PUSH HL
EX DE, HL
AND A }CLEAR CARRY
SBC HL, DE
JP C, ERRORS
JP Z, ERRORS
PUSH HL
POP BC
POP HL
PUSH HL
POP DE
INC DE
PUSH HL
PUSH DE
CALL HEXI
Appendix4-Spectrum monitor-assembler listing 255
POP DE
POP HL
LD (HL), A
LDIR
CALL CRLF
RET
GOTO!
LD A,' '
CALL PRTCHR
CALL HEXD
PUSH HL ;GOTO
LD A,' '
CALL PRTCHR
POP HL
LD (HL), 0CDH
INC HL
LD (HL), LOU< BRK)
256 Appendix4-Spectrum monitor-assembler listing
INC HL
LD (HL ), HIGH( BRK)
CALL GETREG
RET
PUTREG: LD (SAVESP), SP
LD SP,AHLREG+2
EX AF, AF'
EXX
PUSH HL
PUSH DE
PUSH BC
PUSH AF
EXX
EX AF, AF'
PUSH IX
PUSH HL
PUSH DE
PUSH BC
PUSH AF
LD SP, (SAVESP)
RET
GETREG! LD (SAVESP), SP
LD SP,AFREG
POP AF
POP BC
POP DE
POP HL
POP IX
EX AF, AF'
EXX
POP AF
POP BC
POP DE
POP HL
EX AF, AF'
EXX
LD SP, (SAVESP)
RET
CALL CRLF
LD A,
CALL PRTCHR
CALL HEXOD
EX DE, HL 1DEST
LD HL,BRKP
LD BC, 3
LDIR ;PUT BYTES BACK
OUTRE6: LD B,4
NXTREG: LD E, (HL) ;LOU
INC HL
LD D, < HL)
PUSH HL
EX DE, HL
DI1R; CALL HEXOD
LD A,' '
CALL PRTCHR
CALL PRTCHR
POP HL
INC HL
DJNZ NXTREG
RET
CALL OUTREG
RET
IXOUTi
DOING: LD A, (HL)
PUSH AF ;SAVE LOW 8YTE
INC HL
LD A, (HL )
CALL HEXO ;OUT HIGH
POP AF ;GET LOW
CALL HEXO ;AND OUT
INC HL
LD A,' '
CALL PRTCHR
CALL PRTCHR
RET
CHREG:
;GETREG VALUE
LD A,' '
CALL PRTCHR
CALL GETKEY
CP ALTER
JR NZ, LOO
CALL GETKEY
LD HL,LOOKUP
LD BC, LENTAB
CPIR
JP NZ,DISPR
DEC HL
LD DE,LOOKUP
AND A
SBC HL, DE
LD DE,AFREG
SLA L
ADD HL, DE
INC HL
Appendix4-Spectrum monitor-assembler listing 259
LD A,' '
CALL PRTCHR
LD A, (HL) ; LOW
CALL HEXO
DEC HL
LD A, (HL) ; HIGH
CALL HEXO
INC HL
LD A,' '
CALL PRTCHR
CALL HEXI
LD (HL), A
DEC HL
CALL HEXI
LD (HL), A
RET
LOOKUP:
DB 'AVBVDVHVX'
DB 'IV J'.'LVP'
; A' B' D' H'
LENTAB EBU 9D
GETBC: PUSH HL
AND A
SBC HL, DE
JR NC,DSWQP
POP HL
EX DE, HL
JR GETBC
DSUOP:
i HL =NUMBER OF BYTES
JDE =START ADDRESS
; BC ^DESTINATION
;(SP )=END ADDRESS
EX DE, HL
;DE =NUM HL=START
iBC=DEST
260 Appendix4-Spectrum monitor-assembler listing
BACKW:
ADD HL, BC
DEC HL
EX DE, HL
ADD HL, BC
DEC HL
EX DE, HL
LDDR
RET
IDENT:
LD A,' '
CALL PRTCHR
CALL GETKEY
CP 0D
RET Z
CP 65
JP C, ERRORS
LD HL, FILENAME
LD B, 10
LD C, 32
CLBUFF; LD (HL >,C
INC HL
DJNZ CLBUFF
Appendix 4 - Spectrum monitor-assembler listing 261
LD HL, FILENAME
LD B, 9
PUTBUF: LD (HL), A
DEC B
RET Z
INC HL
CALL GETKEY
CP 0DH
RET Z
CP 65
JP C,ERRORS
JR PUTBUF
HUNT:
LD A,' '
CALL PRTCHR
CALL GETEXPR2
PUSH HL ;SAVE START
EX DE, HL
AND A
SBC HL, DE
JP C,ERRORS
JP Z, ERRORS
PUSH HL
POP BC
POP HL
LD A,' '
CALL PRTCHR
PUSH HL
PUSH DE
CALL HEX I
POP DE
POP HL
262 Appendix 4-Spectrum monitor-assembler listing
COMP: CP (HL)
PUSH AF
JR NZ, NFOUND
CALL CRLF
CALL HEXOD
CALL GETKEY
CP SDH
JR NZ,BHUN
NFOUND:
INC HL
DEC BC
LD A, B
OR C
JR Z,BHUN
POP AF
JR COMP
BHUN:
POP AF
RET
CRLF: LD A, CR
CALL PRTCHR
RET
REGMESS:
DEFM ' AF BC
0000::0000::
DEFM ' DE HL
DEFM 'IX'
DB CR,' *'
REGS:
AFREG: DEFW 0000H AF
BCREG: DEFW 0000H BC
DEREG: DEFW 0000H DE
HLREG: DEFW 0000H HL
IXREG: DEFW 0000H IX
AAFREG: DEFW 0000H AF'
ABCREG: DEFW 0000H BC'
ADEREG: DEFW 0000H DE'
AHLREG: DEFW 0000H HL'
BRKP: DB 0, 0,0
SAVESP: DB 0,0
Appendix 4-Spectrum monitor-assembler listing 263
HEADER: DS 17
FILENAME:
DS 10
DB CR,' ♦'
DS 75
STACK: DB 0
ERRSP: DEFW 0
END
Index
A C
Accumulator 19,29,31 CALL 21,38-39
ADC 32-34 Carry flag 19,32,41,70
addition 29-32,70 cassette recorder 56
addressing 10 loading/saving 103-114
register indirect 27 CCF 70
index 27 channel routines 94-114
indirect 30 Clock program 150-155
ports 54 comments 15
screen 121-139 compare 51-52
relative 37 CPD 66
AND 59 CPDR 65
animation 127-129 CPI 66
A register see Accumulator CP1R 65
arithmetic operations 29-40 CPL 70
assembler 13-17,84-87
attribute file 121-139
D
Auto Line Number program
DAA 70
191-195
DEC 29,32-33
decimal system 9-11
B
DEFB 14-15
BASIC loader 78-78
DEFM 65
binary system 8-11
DEFS 15
BIT 53
DEFW 15
block compare 65-66
disassembler 14
block manipulation 64-71
DJNZ 37
block transfer 66-69
Dump (monitor) 73
monitor 75
border
scrolling attribute program E
85-95 Edit (monitor) 73
split colour program 144-145 EQU 15
branching 51-52 error messages 39-40
Brickout program 156-169
266 Index
F L
Fill memory (monitor) 74 labels 15
flags 19-20 Large Print program 183-188
floating point arithmetic 40 LDDR 67-68
ROM routines 115-120 LDIR67
F register 19 loader program 77-78
loading operations 22-27
G monitor 75
Goto address (monitor) 74 ROM routines 103-114
logical operations 34,59-63
H
hexadecimal system 12-13 M
Hex Monitor 77-78 masking 55
H flag 20 Maze Generator program
HL registers 20 169-183
Hunt byte (monitor) 74 memory addressing 10
mnemonics 13,84
I mode 1 interrupts 141
Identify filename (monitor) 75 mode 2 interrupts 141-144
IN 55 monitor 14
INC 29,32-33 program 72-84
index addressing mode 27 Move block (monitor) 75
indexing 30 multiplication 33,42,46-48,70
indirect addressing 30 music 99
input/output 53-58
instruction set N
AND 60 NEG 70
OR 62 negative integers 11-12
XOR 63 nesting 39
integers negative 11-12 N flag 19
interfacing 53-58 number systems 8-13
interrupt routines 18,140-155 nybble 13
I register 18
IX registers 18,30 O
IY registers 18,28,30 OR 62
OUT 56
J output 56
JP 35
jump P
conditional 35 Parity overflow flag 20
relative 36 PC register 20
pixel
K scroll program 188-191
keyboard 54-55 POP 21
Index 267
port SCF 70
addressing 54 screen
reading 55 addressing 97
print ASCI I (monitor) 76 file 121-139
printer 140 scrolling program 85-93
printing pixel scroll 188-191
large print 183-188 SET 52
ROM routines 94-97 shifting operations 44
to screen 127-129 signed integer representation
pseudo operators 14,65 11-12
PUSH 21 Sign flag 20
SLA 44
R Sort program 195-201
reading sound generation 57-58
keyboard 54-55 SP register 20
port 54 SRA45
recursion 202-206 SRL 44
register indirect addressing 27 stack 21-22
Register modify (monitor) 76 streams 94
registers 17-28 SUB 31
pairs 17-18,20-24 subtraction 31-35,38
relative addressing 37
remarks 15 T
RES 53 tape loading/saving 103-114
RETURN 39 timing program 150-155
RL 42 toggling 62
RLC 41 Trace program 146-150
RLD45 truth tables
ROM routines 94-120 AND 60
floating point 115-120 OR 61
printing 94-97 XOR62
screen addressing 97 two's complement 12,70
tape loading/saving 103-114
rotating operations 41
V
RR 43
vectored processing 143
RRC 43
R register 18
X
S XOR 62
saving operations 27
monitor 77 Z
ROM routines 103-114 Z80 chip 9-14,17,19-21,23,25,27
SBC 32-34 Zero flag 20
More Pan/PCN Computer Library titles for the
Sinclair ZX Spectrum