.........1.........2.........3.........4.........5.........6.........7.........8

FIRST STEPS IN MACHINE CODE
An introduction to Z80 machine code by David Nowotnik
part 5 of 6 - ROM Routines
from ZX Computing Aug/Sep'85


Within the 8K of ROM fitted in- side the ZX81, and the 16K
in the Spectrum, there is a wealth of machine code
subroutines. These, of course, supply the operating systems
and BASIC translators for the computers, but there is no
reason why these routines may not be used by machine
language programmers. This is just one of the subjects to be
covered in this, the penultimate part of our series on Z80
machine code. But before the ROM routines are examined,
we'll complete the examination of bit operations started
last time.

If you recall, last issue we examined the logical operators
AND, OR and XOR. These allowed a bit-by-bit comparison of
two bytes. Individual bits were also turned off, on, or were
compared with SET, RESET and BIT. The other manipulation of
bits allowed by machine code is the movement of bits within
a byte. Essentially, bits can be moved left or right within
a byte. At first glance, this might seem like a strange
thing to do. But, if you realise that moving all the bits
within a byte one place to the left effectively multiplies
the value in the byte by two (and moving right divides by
two), then these operations begin to make some sense
(remember, the convention in representing a byte by eight
bits is to have the highest value bit on the left)

The shift and rotate instructions can be confusing to the
beginner, and even a diagram of what they do cannot readily
explain how they work. An animated display works much
better, and that is just the purpose of the listing in Fig.
1. It shows you how each of the shift and rotate
instructions carry out their operations - in slow motion.

The program listing is designed for the ZX81; it will not
work as written on the Spectrum; but to prevent Spectrum
users feeling left out, Fig.2 contains program lines to
substitute in the Fig. 1 listing so that this program will
work on the Spectrum.

Just to explain the basic operations, shift moves all the
bits in a byte along one place in either direction. The last
bit can either be 1 or 0 depending on the operation.

Rotate is a special form of the shift instruction. After all
the bits have been moved left or right, the vacant bit is
filled either with the value in the carry bit, or the bit
which was moved out of the opposite end of the byte.

Confused? The Shift/Rotate program should explain it. After
typing in the program lines, SAVE the program on tape and
RUN it. You'll be presented with a menu listing each of the
shift/rotate instructions. Press the number beside your
choice. The instructions on screen should be clear, but
essentially you'll be asked to fill bytes with values
(between 0 and 255) and fill the carry flag with a 1 or 0.
The byte value will be translated to binary, and will appear
in a 'box' which represents the register or byte in RAM. The
operation will then be demonstrated in slow motion, and the
final values of bytes shown on the screen. As the original
values will also be on the screen, you should be able to see
the effect of the operation on the byte values. Try each
operation several times, using different input values, and
you'll be surprised how soon the operations will become
clear to you.

You may recall in part three of the series there was a
machine code routine which used one of the rotate
instructions (RL E); it was used to transfer, bit by bit,
the contents of the E register (into which the flag register
had been copied) into the carry flag. It demonstrated how
you could test the bit contents on a register, one bit at a
time, without using multiple BIT commands. If the program
listing in part three was a mystery to you at the time, go
back to it, and, with the shift/rotate demonstration
program, see how it works.

Fig.3 contains the Z80 opcodes for all the shift and rotate
instructions. That covers all the bit operations on the
Z80's basic register array. Now, let's go on to the ROM
routines.


The ROM

The ROM on both machines is a highly complex system of
machine code subroutines. These can be used with the CALL
instruction, (described earlier in the series), from within
your own machine code routines. The problem for machine code
programmers is knowing where the routines are located, what
they do, and how they work (i.e. what data is required in
registers, and what registers will be corrupted - will have
their values changed - when a particular routine is CALLed).

Fortunately, for programmers, a certain Dr. Ian Logan spent
many many hours carefully disassembling the ROM on both the
ZX81 and Spectrum, then published the results of all his
efforts. Both publications contain the assembly language
listing of the ROM starting at address 0 and working
steadily through to the end. Each subroutine is sectioned
off, with a brief description of what that routine will do.
Both books are published by Melbourne House press; the ZX81
book is called 'Sinclair ZX81 ROM Disassembly' and comes in
two parts, while the Spectrum book has Dr. Frank O'Hara as
co- author, and is called 'The Complete Spectrum ROM
Disassembly'.

To give you some idea of the location of just a few of the
ROM routines, there is a listing in Fig.4. To use any of
them from your own routine, use CALL address, where
'address' is the routine's start address (given in Fig.4).

There are a special set of subroutines at the beginning of
ROM (on both machines) which have a different instruction
for their implementation. These are the RST calls, the
'RESTARTS'. There are eight RST opcodes, and, as you can see
from Fig.5, there is a single byte opcode for each of them -
compare that with the CALL instruction which requires three
bytes for its implementation. In machine code terms, that
represents a big saving in time to carry out that
instruction, and RST calls are intended for those operations
which the designer of the ROM expected the system to use
often. The purpose of the RST instructions in both the ZX81
and Spectrum are shown in Fig.5. Despite the many
differences between the machines the basic operation of all
eight RST routines is the same. There will be some examples,
and hints, on using RST commands later, in the machine code
examples section.


More Registers ...

As a final bit of theory for this issue, how about the
revelation that the Z80 doesn't have one set of registers
called 'A' ,'B', 'C','D','E','H', and 'L' - but two. The
reason for two sets is that the 'spare' set provide you with
a little extra storage space within the CPU. If you remember
that operations involving transfer of bytes from memory to
the Z80 are relatively much slower than transfers within the
CPU, then much time could be saved by using an alternative
register set rather than accessing memory for temporary
storage. There is a 'switching' system between the two sets
of registers, and only one set can be used at any one time.
For example, LD A,6 will only load the value '6' into the
'A' register currently in use, while the other 'A' register
lies dormant until it is switched on, disabling the first
'A' register. When a register is switched 'off', it holds
its value until it is switched back on again, and operations
are carried out on that register.

There are two 'switching' instructions for the range of
registers we have dealt with so far (believe it or not,
there are more registers, and they will appear in the
concluding part of the series). The BC, DE, and HL register
pairs are 'switched' with the EXX instruction (opcode D9
hex). The AF pair are switched with the 'spare' AF pair
using EX AF,AF' (opcode 08 hex).

It is also possible to exchange the values of certain
registers by a single exchange instruction. For example, the
value held in DE can be transferred to HL, and vice versa
with a single instruction EX DE,HL (opcode EB hex). A more
complex exchange instruction involves the top of stack and
the HL register pair. These two values can be exchanged with
EX (SP),HL (opcode E3 hex). This takes the value held at the
top of the stack, places it in HL, and places the previous
value in HL onto the top of the stack. You can do some
devious things with this instruction, such as changing the
return address of a subroutine.

Now, to the machine code examples for this part of the
series. Unlike some earlier examples, the routines that we
will now look at for the two machines are so similar that
they will be covered at the same time.

The first example is shown in Fig.6. This is a machine code
routine to PEEK a byte value, and print that value in
hexadecimal on the screen. It uses both a shift instruction,
and an RST command, and so covers a lot of the theory
described in this article.

The diagram in Fig.6 explains how a byte can be split into
two parts. These half bytes are called - believe it or not -
a nibble. One nibble can have a value between 0 and 15,
which is exactly the range of a single unit of hexadecimal
(00 to 0F), so the upper four bits (nibble) of a byte form
the higher digit of a hexadecimal number, and the lower
nibble the lower digit. So, to convert the byte value to
hex, the value of each nibble has to be determined, then
converted to the corresponding character to be printed on
the screen. The value of the upper nibble is printed first,
followed by the lower nibble.

With that description in mind, take a look at the assembly
language listing of the machine code example in Fig.6.
Ignore the first two lines for a moment; the address of the
byte to be examined is loaded into HL, then the value of
that byte is placed in the 'A' register, and copied into
'E'. The four lower bits are set to O with the AND 240
command, then the higher bits are shifted four times to the
right (SRL) so that they now appear in the lower nibble of
'A'; the higher nibble of 'A' is filled with zeros, so 'A'
contains the value of the higher nibble. The print
subroutine is then called; this is slightly different for
each machine as the character codes differ between the ZX81
and Spectrum.

In the case of the ZX81, the character zero ('0') has a
character code value of 28; so adding 28 to the value in 'A'
gives the appropriate character code. Fortunately, the
character 'A' follows '9' in the ZX81's character set, so
making life easier in converting decimal to hex.

This is not the case with the Spectrum. The code for '0' is
48, so, adding 48 to the value of the upper nibble in
register 'A' gives the character code IF the value is
between 0 and 9. For a larger value, a further seven has to
be added, as the character code for the letter 'A' is seven
above the character after '9' (you can check this in the
back of your Spectrum handbook). This is why there is a CP
(compare) instruction in the Spectrum print routine; if the
value is greater than 57 (character code of '9') then
another 7 is added to the 'A' register to ensure that the
correct alphabetic character is printed.

Once the routine has worked out the right character code,
use is made of RST 10 (hex) to print the character on the
screen at the next print position. On return from this
subroutine, the original value of the byte is returned to
the 'A' register from the storage place in 'E', and the
higher nibble masked (set to zero) with the command AND 15.
The print routine is called again to place this value on the
screen.

So, type in the appropriate hex loader from Fig.6 (remember
to lower RAMTOP on your 16K ZX81 first), then SAVE and RUN.
In response to the flashing cursor, enter a value between 0
and 255 (integers only) and you should see the hexadecimal
equivalent printed on the screen. The RST 10 instruction on
the ZX8l will only print to the screen, but with the
Spectrum, you have a little more flexibility. RST 10 can be
used to send characters to the printer or the lower part of
the screen (the area normally reserved by the system) as
well as the 'upper' screen, which we have access to through
BASIC. The way the Spectrum decides to which 'device' it
should be printing is by setting some system variables; the
safest way of doing this is to use one of the ROM routines,
and this is located at 1601 (hex); this is the routine
called at the start of the Spectrum machine code routine.
The value in the 'A' register when this routine is called
defines which device is activated. A value of 2 activates
the main screen; 3 activates the printer, and 1, the lower
screen.

You can also send non- printable characters via the RST
routine, although care has to be taken when doing this. For
example, INK and PAPER commands can be sent via RST 10, as
well as AT and TAB. The best way of learning about the
flexibility of RST 10 is to try out various things for
yourself.

The other machine code example is shown in Fig.7. This is a
simple routine which shows you how RST 08 - the error
message routine - works. You'll notice that there is no RET
instruction, this is because a return to BASIC occurs
automatically during the operation of RST 08. The routine
allows you to put values into the byte immediately following
the RST 08 command. This byte holds the error number (plus
one). Try put- ting various values into this byte, and if an
error exists for that number (plus one), then you'll see
that message at the bottom of the screen.

Next issue we'll reach the final part of the series. There
will be two long machine code examples covering many of the
operations covered in this series, some notes on items not
covered, and some suggestions for further reading.


- Fig.1. Listing of the Shift/Rotate demonstration program  - - - - - - - -

  10 REM Z80 SHIFT/ROTATE DEMONSTRATION
  15 REM D. NOWOTNIK.  ZX COMPUTING
  20 REM JUNE 1985
  25 DIM A$(9,8)
  30 LET A$(1)="RLCA/RLC"
  35 LET A$(2)="RLA/RL"
  40 LET A$(3)="RRCA/RRC"
  45 LET A$(4)="RRA/RR"
  50 LET A$(5)="SLA"
  55 LET A$(6)="SRL"
  60 LET A$(7)="SRA"
  65 LET A$(8)="RLD"
  70 LET A$(9)="RRD"
  75 LET B$="DEMONSTRATION OF SHIFT/ROTATE"
  80 LET C$="        "
  85 LET P=4000
 100 CLS
 105 PRINT AT 2,1;B$
 110 PRINT AT 6,2;"SELECT:-"
 115 PRINT
 120 PRINT "   1.  ";A$(1)
 125 PRINT "   2.  ";A$(2)
 130 PRINT "   3.  ";A$(3)
 135 PRINT "   4.  ";A$(4)
 140 PRINT "   5.  ";A$(5)
 145 PRINT "   6.  ";A$(6)
 150 PRINT "   7.  ";A$(7)
 155 PRINT "   8.  ";A$(8)
 160 PRINT "   9.  ";A$(9)
 170 IF INKEY$="" THEN GOTO 170
 175 LET O$=INKEY$
 180 IF O$<"1" OR O$>"9" THEN GOTO 170
 182 LET O=VAL O$
 185 GOSUB (VAL O$*200)
 190 GOTO 100
 200 GOSUB 2000
 210 GOSUB 4300
 220 GOSUB 4400
 230 GOSUB 6000
 240 RETURN
 400 GOSUB 2000
 410 GOSUB 4300
 420 GOSUB 4500
 430 GOSUB 5000
 440 GOSUB 6000
 450 RETURN
 600 GOSUB 2000
 610 GOSUB 4600
 620 GOSUB 4700
 630 GOSUB 5000
 640 GOSUB 6000
 650 RETURN
 800 GOSUB 2000
 810 GOSUB 4600
 820 GOSUB 4750
 830 GOSUB 5000
 840 GOSUB 6000
 850 RETURN
1000 GOSUB 2000
1010 GOSUB 4300
1020 GOSUB 4800
1030 GOSUB 5000
1040 GOSUB 6000
1050 RETURN
1200 GOSUB 2000
1210 GOSUB 4600
1220 GOSUB 4850
1230 GOSUB 5000
1240 GOSUB 6000
1250 RETURN
1400 GOSUB 2000
1410 GOSUB 4900
1420 GOSUB 4600
1430 GOSUB 4930
1440 GOSUB 5000
1450 GOSUB 6000
1460 RETURN
1600 GOSUB 6100
1610 GOSUB 6400
1620 GOSUB 7000
1630 GOSUB 6000
1640 RETURN
1800 GOSUB 6100
1820 GOSUB 6500
1830 GOSUB 7000
1840 GOSUB 6000
1850 RETURN
2000 CLS
2010 PRINT AT 1,1;B$
2020 PRINT AT 4,2;A$(O)
2030 PRINT AT 7,12;"REGISTER"
2040 PRINT AT 8,10;"|----------|"
2050 PRINT AT 9,10;"|          |"
2060 PRINT AT 10,10;"|----------|"
2070 PRINT AT 11,12;"76543210"
2080 PRINT AT 13,16;"C"
2090 PRINT TAB 15;"|-|"
2100 PRINT TAB 15;"| |"
2110 PRINT TAB 15;"|-|"
2120 PRINT
2130 PRINT "REGISTER = A,B,C,D,E,H,L,(HL)"
2140 GOSUB P
2150 PRINT AT 8,0;"ENTER"
2160 PRINT "REGISTER"
2170 INPUT D
2180 IF D<0 OR D>255 OR D<>INT D THEN GOTO 2170
2190 GOSUB 4100
2200 PRINT AT 9,12;Y$
2210 PRINT AT 8,0;C$
2220 PRINT C$
2230 PRINT AT 9,2;DD
2240 PRINT AT 15,0;"ENTER CARRY"
2250 INPUT E
2260 IF NOT (E=0 OR E=1) THEN GOTO 2250
2270 PRINT AT 15,0;C$;C$(TO 5);AT 15,16;E
2280 PRINT AT 15,2;E
2290 GOSUB P
2300 RETURN
4000 FOR I=1 TO 25
4010 NEXT I
4020 RETURN
4100 LET Y$="00000000"
4110 LET DD=D
4120 FOR I=8 TO 1 STEP -1
4130 IF D/2<>INT (D/2) THEN LET Y$(I)="1"
4140 LET D=INT (D/2)
4150 NEXT I
4160 RETURN
4300 PRINT AT 9,12; CHR$ (CODE Y$+128)
4310 GOSUB P
4320 PRINT AT 9,12;" ";AT 9,6; CHR$ (CODE Y$+128)
4330 GOSUB P
4335 FOR I=1 TO 8
4340 PRINT AT 9,10+I;Y$(I)+" "
4345 FOR J=1 TO 10
4350 NEXT J
4355 NEXT I
4360 GOSUB P
4365 RETURN
4400 PRINT AT 9,6;" ";AT 9,19; CHR$ (CODE Y$+128);AT 15,16; CHR$ (CODE Y$+128)
4405 GOSUB P
4410 PRINT AT 9,19;Y$(1);AT 15,16;Y$(1)
4415 LET Y$=Y$(2 TO)+Y$(1)
4420 GOSUB 5000
4425 RETURN
4500 PRINT AT 15,16; CHR$ (E+156)
4505 GOSUB P
4510 PRINT AT 15,16;" ";AT 9,19; CHR$ (E+156)
4515 GOSUB P
4520 PRINT AT 9,6;" ";AT 15,16; CHR$ (CODE Y$(1)+128)
4525 GOSUB P
4530 PRINT AT 15,16;Y$(1);AT 9,19; CHR$ (E+156)
4540 LET Y$=Y$(2 TO)+CHR$ (E+28)
4550 RETURN
4600 PRINT AT 9,19; CHR$ (CODE Y$(8)+128)
4605 GOSUB P
4610 PRINT AT 9,19;" ";AT 9,24; CHR$ (CODE Y$(8)+128)
4615 GOSUB P
4620 FOR I=7 TO 1 STEP -1
4625 PRINT AT 9,11+I;" "+Y$(I)
4630 FOR J=1 TO 10
4635 NEXT J
4640 NEXT I
4645 GOSUB P
4650 RETURN
4700 PRINT AT 9,24;" ";AT 9,12; CHR$ (Y$(8)+128);AT 15,16; CHR$ (CODE Y$(8)+128)
4705 GOSUB P
4710 PRINT AT 9,12;Y$(8);AT 15,16;Y$(8)
4715 LET Y$=Y$(8)+Y$(TO 7)
4720 RETURN
4750 PRINT AT 15,16; CHR$ (E+156)
4755 GOSUB P
4760 PRINT AT 15,16;" ";AT 9,12; CHR$ (E+156)
4765 GOSUB P
4770 PRINT AT 9,24;" ";AT 15,16; CHR$ (CODE Y$(8)+128)
4775 GOSUB P
4780 PRINT AT 9,12; CHR$ (E+28);AT 15,16;Y$(8)
4785 LET Y$=CHR$ (E+28)+Y$(TO 7)
4790 RETURN
4800 PRINT AT 9,6;" ";AT 15,16; CHR$ (CODE Y$+128)
4810 GOSUB P
4815 PRINT AT 9,23; CHR$ 156
4820 GOSUB P
4825 PRINT AT 9,23;" ";AT 9,19; CHR$ 156
4830 GOSUB P
4835 PRINT AT 9,19;"0";AT 15,16;Y$(1)
4840 LET Y$=Y$(2 TO)+"0"
4845 RETURN
4850 PRINT AT 9,24;" ";AT 15,16; CHR$ (CODE Y$(8)+128)
4855 GOSUB P
4860 PRINT AT 9,6; CHR$ 156
4865 GOSUB P
4870 PRINT AT 9,6;" ";AT 9,12; CHR$ 156
4875 GOSUB P
4880 PRINT AT 9,12;"0";AT 15,16;Y$(8)
4885 LET Y$="0"+Y$(TO 7)
4890 RETURN
4900 PRINT AT 9,12; CHR$ (CODE Y$+128)
4905 GOSUB P
4907 PRINT AT 9,6; CHR$ (CODE Y$+128)
4910 GOSUB P
4915 PRINT AT 9,12;Y$
4920 GOSUB P
4925 RETURN
4930 PRINT AT 9,24;" ";AT 15,16; CHR$ (CODE Y$(8)+128)
4935 GOSUB P
4940 PRINT AT 9,6;" ";AT 9,12; CHR$ (CODE Y$+128)
4945 GOSUB P
4950 PRINT AT 9,12;Y$(1);AT 15,16;Y$(8)
4955 LET Y$=Y$(1)+Y$(TO 7)
4960 RETURN
5000 LET X=0
5005 FOR I=8 TO 1 STEP -1
5010 LET X=X+(CODE Y$(I)-28)*(2**(8-I))
5020 NEXT I
5030 PRINT AT 9,22;"= ";X
5040 RETURN
6000 PRINT AT 21,2;"PRESS ANY KEY TO CONTINUE"
6010 IF INKEY$="" THEN GOTO 6010
6020 RETURN
6100 CLS
6102 PRINT AT 2,1;B$
6105 PRINT AT 4,2;A$(O)
6110 PRINT AT 7,14;"(HL)"
6115 PRINT AT 8,11;"|--------|"
6120 PRINT AT 9,11;"|        |"
6125 PRINT AT 10,11;"|--------|"
6130 PRINT AT 11,12;"76543210"
6135 PRINT AT 13,16;"A"
6140 PRINT AT 14,11;"|--------|"
6145 PRINT AT 15,11;"|        |"
6150 PRINT AT 16,11;"|--------|"
6155 PRINT AT 17,12;"76543210"
6160 GOSUB P
6165 PRINT AT 9,0;"INPUT (HL)"
6170 INPUT D
6175 IF D<0 OR D>255 OR D<>INT D THEN GOTO 6170
6185 GOSUB 4100
6190 PRINT AT 9,0;C$+"   ";AT 9,2;DD
6195 PRINT AT 9,12;Y$
6200 LET Z$=Y$
6205 PRINT AT 15,0;"INPUT A"
6210 INPUT D
6215 IF D<1 OR D>255 OR D<>INT D THEN GOTO 6210
6220 GOSUB 4100
6225 PRINT AT 15,0;C$;AT 15,12;Y$;AT 15,2;DD
6235 LET X=128
6330 LET R$=CHR$ (CODE Z$+X)+CHR$ (CODE Z$(2)+X)+CHR$ (CODE Z$(3)+X)+CHR$ (CODE Z$(4)+X)
6335 LET S$=CHR$ (CODE Z$(5)+X)+CHR$ (CODE Z$(6)+X)+CHR$ (CODE Z$(7)+X)+CHR$ (CODE Z$(8)+X)
6340 LET T$=CHR$ (CODE Y$(5)+X)+CHR$ (CODE Y$(6)+X)+CHR$ (CODE Y$(7)+X)+CHR$ (CODE Y$(8)+X)
6345 RETURN
6400 PRINT AT 15,16;T$
6405 GOSUB P
6410 PRINT AT 15,16;"    ";AT 15,23;T$
6415 GOSUB P
6420 PRINT AT 9,12;R$
6425 GOSUB P
6430 PRINT AT 9,12;"    ";AT 15,16;R$
6435 GOSUB P
6440 PRINT AT 9,16;"    ";AT 9,12;Z$(5 TO)
6445 GOSUB P
6450 PRINT AT 15,23;C$;AT 9,16;T$
6455 LET T$=Z$(5 TO)
6460 LET R$=Y$(5 TO)
6465 LET Y$=Y$(TO 4)+Z$(TO 4)
6470 LET Z$=T$+R$
6472 GOSUB P
6475 PRINT AT 9,12;Z$;AT 15,12;Y$
6480 RETURN
6500 PRINT AT 15,16;T$
6505 GOSUB P
6510 PRINT AT 15,16;"    ";AT 15,23;T$
6515 GOSUB P
6520 PRINT AT 9,16;S$
6525 GOSUB P
6530 PRINT AT 9,16;"    ";AT 15,16;S$
6535 GOSUB P
6540 PRINT AT 9,12;"    ";Z$(TO 4)
6545 GOSUB P
6550 PRINT AT 15,23;C$;AT 9,12;T$
6555 LET T$=Z$(5 TO)
6560 LET R$=Y$(5 TO)
6565 LET Y$=Y$(TO 4)+T$
6570 LET Z$=R$+Z$(TO 4)
6572 GOSUB P
6575 PRINT AT 9,12;Z$;AT 15,12;Y$
6580 RETURN
7000 LET X=0
7005 FOR I=8 TO 1 STEP -1
7010 LET X=X+(CODE Z$(I)-28)*(2**(8-I))
7020 NEXT I
7030 PRINT AT 9,23;"= ";X
7040 LET X=0
7045 FOR I=8 TO 1 STEP -1
7047 LET X=X+(CODE Y$(I)-28)*(2**(8-I))
7050 NEXT I
7055 PRINT AT 15,23;"= ";X
7060 RETURN


- Fig.2. Exchange program lines for the Spectrum version of Shift/Rotate  -

4000 FOR i=1 TO 100
4300 PRINT AT 9,12; INK 7; PAPER 0;y$(1)
4320 PRINT AT 9,12;" ";AT 9,6; INK 7; PAPER 0;y$(1)
4345 FOR j=1 TO 40
4400 PRINT AT 9,6;" ";AT 9,19; INK 7; PAPER 0;y$(1);AT 15,16;y$(1)
4500 PRINT AT 15,16; INK 7; PAPER 0;e
4510 PRINT AT 15,16;" ";AT 9,19; INK 7; PAPER 0;e
4520 PRINT AT 9,6;" ";AT 15,16; INK 7; PAPER 0;y$(1)
4530 PRINT AT 15,16;y$(1);AT 9,19; INK 7; PAPER 0;e
4540 LET y$=y$(2 TO)+STR$ e
4600 PRINT AT 9,19; INK 7; PAPER 0;y$(8)
4610 PRINT AT 9,19;" ";AT 9,24; INK 7; PAPER 0;y$(8)
4630 FOR j=1 TO 40
4700 PRINT AT 9,24;" ";AT 9,12; INK 7; PAPER 0;y$(8);AT 15,16;y$(8)
4750 PRINT AT 15,16; INK 7; PAPER 0;e
4760 PRINT AT 15,16;" ";AT 9,12; INK 7; PAPER 0;e
4770 PRINT AT 9,24;" ";AT 15,16; INK 7; PAPER 0;y$(8)
4780 PRINT AT 9,12;e;AT 15,16;y$(8)
4785 LET y$=STR$ e+y$(TO 7)
4800 PRINT AT 9,6;" ";AT 15,16; INK 7; PAPER 0;y$(1)
4815 PRINT AT 9,23; INK 7; PAPER 0;"0"
4825 PRINT AT 9,23;" ";AT 9,19; INK 7; PAPER 0;"0"
4850 PRINT AT 9,24;" ";AT 15,16; INK 7; PAPER 0;y$(8)
4860 PRINT AT 9,6; INK 7; PAPER 0;"0"
4870 PRINT AT 9,6;" ";AT 9,12; INK 7; PAPER 0;"0"
4890 PRINT AT 9,12; INK 7; PAPER 0;y$(1)
4907 PRINT AT 9,6; INK 7; PAPER 0;y$(1)
4930 PRINT AT 9,24;" ";AT 9,12; INK 7; PAPER 0;y$(8)
4940 PRINT AT 9,6;" ";AT 9,12; INK 7; PAPER 0;y$(1)
5010 LET x=x+(CODE y$(i)-48)*(2^(8-i))
6330 LET r$=z$(TO 4)
6335 LET s$=z$(5 TO)
6340 LET t$=y$(5 TO)
6400 PRINT AT 15,16; INK 7; PAPER 0;t$
6410 PRINT AT 15,16;"    ";AT 15,23; INK 7; PAPER 0;t$
6420 PRINT AT 9,12; INK 7; PAPER 0;r$
6430 PRINT AT 9,12;"    ";AT 15,16; INK 7; PAPER 0;r$
6440 PRINT AT 9,16;"    ";AT 9,12; INK 7; PAPER 0;s$
6450 PRINT AT 15,23;c$;AT 9,16; INK 7; PAPER 0;t$
6510 PRINT AT 15,16;"    ";AT 15,23; INK 7; PAPER 0;t$
6520 PRINT AT 9,16; INK 7; PAPER 0;s$
6530 PRINT AT 9,16;"    ";AT 15,16; INK 7; PAPER 0;s$
6550 PRINT AT 15,23;c$;AT 9,12; INK 7; PAPER 0;t$
7010 LET x=x+(CODE z$(i)-48)*(2^(8-i))
7047 LET x=x+(CODE y$(i)-48)*(2^(8-i))


- Fig.3. Opcodes for the Shift and Rotate instructions  - - - - - - - - - - 

RLCA    07        RLA    17        RRCA    0F        RRA    1F
RLC A   CB07      RL A   CB17      RRC A   CB0F      RR A   CB1F
RLC B   CB00      RL B   CB10      RRC B   CB08      RR B   CB18
RLC C   CB01      RL C   CB11      RRC C   CB09      RR C   CB19
RLC D   CB02      RL D   CB12      RRC D   CB0A      RR D   CB1A
RLC E   CB03      RL E   CB13      RRC E   CB0B      RR E   CB1B
RLC H   CB04      RL H   CB14      RRC H   CB0C      RR H   CB1C
RLC L   CB05      RL L   CB15      RRC L   CB0D      RR L   CB1D
RLC(HL) CB06      RL(HL) CB16      RRC(HL) CB0E      RR(HL) CB1E

SLA A   CB27      SRA A  CB2F      SRL A   CB3F
SLA B   CB20      SRA B  CB28      SRL B   CB38
SLA C   CB21      SRA C  CB29      SRL C   CB39
SLA D   CB22      SRA D  CB2A      SRL D   CB3A
SLA E   CB23      SRA E  CB2B      SRL E   CB3B
SLA H   CB24      SRA H  CB2C      SRL H   CB3C
SLA L   CB25      SRA L  CB2D      SRL L   CB3D
SLA(HL) CB26      SRA(HL)CB2E      SRL(HL) CB3E

RLD     ED6F      RRD    ED67

(All opcodes are in hexadecimal)


- Fig.4. Some useful ROM routines - - - - - - - - - - - - - - - - - - - - - 

1. ZX81

02BB - Keyboard scanning routine; this returns a coded
       version of the key in HL. To translate this to a
       character code, you'll need to transfer HL contents
       to BC and call the routine at 07BD.
02E7 - Set FAST mode
0A2A - CLS routine
0C0E - SCROLL routine
0F2B - Set SLOW mode

2. Spectrum

028E - Keyboard scanning routine
03B5 - BEEP subroutine
0DFE - SCROLL subroutine (number of lines in B)
0E44 - Clears the lower part of the screen, the number
       of lines governed by the value in B.
0D6E - Clears the 'command' area of the screen


- Fig.5. RST commands - - - - - - - - - - - - - - - - - - - - - - - - - - - 

RST 00 (opcode C7)
When you first switch on your computer, this is
where it starts. The routine carries out some
tests, then wipes RAM clean. You can usr RST 00 to
make sure the computer has completely reset.

RST 08 (opcode CF)
The error trapping routine - it causes the error
messages to be generated, and stops BASIC. The
error number is the value in A minus 1 when the
restart routine is called.

RST 10 (opcode D7)
The print routine - it prints on the screen (or
the 'active' device in the Spectrum) a character,
or print control character.

RST 18 (opcode DF)
Collects a character addressed by the system
variable CH_ADD, and checks whether it is
printable.

RST 20 (opcode E7)
Used in the BASIC translator to check collect the
next character, and translate it. [sic. JG.]

RST 28 (opcode EF)
Calls the floating point calculator routine.

RST 30 (opcode F7)
Creates a space in RAM in the workspace; the size
of the space is governed by the value in BC.

RST 38 (opcode FF)
The 'Maskable Interrupt' routine - some details on
that in the next part of the series.


- Fig.6. Print hexadecimal example  - - - - - - - - - - - - - - - - - - - - 

Assembly language listing:
LD   A,2       3E02   ) Spectrum
CALL 5633      CD0116 )   only
LD   HL,32000  21007D
LD   A,(HL)    7E
LD   E,A       5F
AND  240       E6F0
SRL  A         CB3F
SRL  A         CB3F
SRL  A         CB3F
SRL  A         CB3F
CALL PRINT     CD4E75
LD   A,E       7B
AND  15        E60F
CALL PRINT     CD4E75
RET            C9

ZX81 PRINT subroutine:
ADD  A,28      C61C
RST  10        D7
RET            C9

Spectrum PRINT subroutine:
ADD  A,48      C630
CP   58        FE3A
JR   C,+2      3802
ADD  A,7       C607
RST  10        D7
RET            C9

---

ZX81 program listing

First, lower RAMTOP with these three direct commands:
     POKE 16388,47
     POKE 16389,117
     NEW

Then type in, SAVE, and RUN the following listing:
  10 LET X=30005
  20 INPUT Y
  30 PRINT AT 21,0;X,Y
  40 SCROLL
  50 IF Y=-1 THEN STOP
  60 POKE X,Y
  70 LET X=X+1
  80 GOTO 20

Enter the following sequence of numbers:
33,0,125,126,95,230,240,203,63,203,63,203,63,203,63,
205,78,117,123,230,15,205,78,117,201,198,28,215,201,-1

NEW the machine code loader, then type in and RUN:
  10 INPUT X
  20 POKE 32000,X
  30 RAND USR 30005
  40 PRINT
  50 GOTO 10

Your decimal to hex conversion should now be working.

---

Spectrum program listing

Type in, SAVE, and RUN this program:
  10 CLEAR 29999
  20 LET x=30000
  30 READ y: IF y=-1 THEN GO TO 50
  40 POKE x,y: LET x=x+1: GO TO 30
  50 INPUT "Enter a number (0-255) ";z
  60 POKE 32000,z
  70 RANDOMIZE USR 30000
  80 PRINT
  90 GO TO 50
 100 DATA 62,2,205,1,22,33,0,125,126,95
 110 DATA 230,240,203,63,203,63,203,63
 120 DATA 203,63,205,78,117,123,230,15
 130 DATA 205,78,117,201,198,48,254,58
 140 DATA 56,2,198,7,215,201,-1


- Fig.7. BASIC error message demonstration  - - - - - - - - - - - - - - - - 

In the ZX81, lower RAMTOP with the same three direct
commands as listed in Fig.6. With the Spectrum, lower 
RAMTOP with the command: CLEAR 29999

Now, type in and RUN this listing:
  10 POKE 30000,207
  20 INPUT X
  30 POKE 30001,X
  40 RAND USR 30000       (ZX81)
  40 RANDOMIZE USR 30000  (Spectrum)

RUN each time you get an error message; check the 
message against the number you have INPUT, and the
section on error messages in your handbook.

Assembly language listing:
RST  08        CF        (that's all!)

---

       BYTE
        |
 ---------------
|               |
  HIGHER  LOWER
  NIBBLE  NIBBLE
    |       |
 ------- -------
|       |       |
 - - - -|- - - -
| | | | | | | | |
 - - - - - - - -
 7 6 5 4 3 2 1 0
       BITS


--
Another Fine Product transcribed by:
Jim Grimwood (jimg@globalnet.co.uk), Weardale, England
--
