.........1.........2.........3.........4.........5.........6.........7.........8
[Sinclair User 40 (Jul'85)]

Continuing his look at Z80 operations Marcus
Jeffery loops the loop

Not having the structure available in many
languages, such as Pascal, machine code programs
rely on the use of jump statements. Those are akin
to the Basic GO TO command. A typical machine code
jump might look like

        EA6O 3E01   LD A,1
        EA62 C360EA JP EA60

which would load the accumulator with the value
one, then jump to location EA60 (hex) to repeat
the instruction.

When using machine code, we usually use an
assembler, and those allow the use of labels to
make jumps clearer. You will have seen those
before in the mnemonic listings, and a number of
labels can be seen in figure two. The above loop
could thus be

        EA60 3E01   LABEL LD A,1
        EA62 C3EA60       JP LABEL

That of course is an infinite loop, and does not
allow an exit. The Basic equivalent would be

        10 LET A=1: GO TO 10

A very similar instruction to JP is JR - Jump
Relative. When using JP, the program always jumps
to the specified location. With JR, however, the
program jumps to a location relative to your
present location. The example above using JR would
be

        EA60 3E01 LABEL LD A,1
        EA62 18FC       JR LABEL

which would jump to the location EA64 - the start
of the next instruction - minus four - giving
EA60.

The JR instruction has the advantage of being one
byte shorter - two bytes instead of three - but
that implies that you cannot use it to jump any
great distance. The displacement byte is assumed
to be PLUS - forward - if it is less than 80h and
MINUS - backwards - if greater than 7Fh. In the
latter case, the value is calculated as 100h (256)
minus the value given.

We can check this with

        FCh     = 252
        256-252 = -4
        EA64h-4 = EA60h

That means that we have a range of

            00h-7Fh (0 to 127)
        and FFh-80h (-1 to -128)

The other advantage of using JR, rather than JP,
is that it is relocatable. We will be discussing
this in a later article, but essentially it
implies that the actual address is not used.

Of course, just as in Basic, jumps are not much
use on their own. Their real power is only
realised when they are combined with conditions.
Z80 machine code does not have the flexibility of
Basic when testing for conditions.

All testing is carried out on the hitherto unused
F (Flag) register. The individual bits of this
register are used as shown in figure one. The
values of the bits in the register are set based
on the last operation, so they can then be tested
using special instructions.

All the jump instructions are shown in figure six,
but here is a typical example, showing how to form
a loop.

        .
        .
        .
        LD HL,1
LOOP    ADD HL,HL
        DEC A
        JR NZ,LOOP
        .
        .
        .

The HL registers are initially loaded with the
value of one. The ADD instruction effectively
multiplies HL by two. The value of A is then
decremented, and if Not Zero (NZ) a relative jump
is made, otherwise the code continues. This small
piece of code has the effect of placing the value
of 2^A into HL, assuming that A is greater than
zero and not too large to cause HL to overflow.

Special instructions are also provided whose only
job is to set flags in the F register. Those are
the compare instructions - see figure six. In the
counter program we use.

        CP 8Ah

That subtracts the data (8Ah) from whatever value
is currently in the accumulator. The flags are
set, but the value of the accumulator is left
unchanged. The next instruction is

        JR NZ,CHANGE

which will now jump to the label CHANGE if the
contents of the accumulator are not 8Ah - that is
Acc-8Ah <> 0.

One final test instruction which we will look at
is BIT. That can be used to test individual bits
of a particular register or location. In our
program

        BIT 7,A

tests to see if the most significant bit - the top
bit, counting from bit 0 left to right - in the
accumulator is set. If the specified bit is set -
in other words, is a '1' - then Z (Zero) in the
Flag register will be False. That may seem complex
at first, because the complement of the specified
bit is placed into the Z flag. Have a look at the
program to see how this works.

Other bit instructions which you may come across
are SET and RES, which place a '1' or '0'
respectively into a specified bit. Variations of
this instruction are shown in figure six.

We have seen how to implement a loop using a
conditional jump instruction. Z80 also has a
specified abbreviated loop instruction called
DJNZ. This stands for 'Decrement the B register
and Jump to a given label if Not Zero'.

So, if we wanted to improve the example above, by
using the B register instead of A, we could write

        .
        .
        .
        LD HL,1
LOOP    ADD HL,HL
        DJNZ,LOOP
        .
        .
        .

There is one last set of instructions to look at
before we deal with this month's program. Those
are the 'stack' operations. When using machine
code, it's very easy to run out of registers to
use. One way of dealing with that is to save the
register to a spare location, use the register for
another task, then reload it from the location.

There are a number of disadvantages with this
method, and it is such a common problem that there
are a number of special instructions to deal with
it. Those are called PUSH and POP. A stack can be
thought of as a pile of plates. If a plate is
PUSHed onto the top of the pile, then it must also
be the first removed - POPped.

Registers may be pushed onto the stack in pairs -
A+F, B+C, D+E and H+L - and popped from the stack
in the same way. A pointer to use the top of the
stack, surprisingly known as the stack pointer
(SP), is automatically updated for you.

The only thing to remember is to take the register
pairs off the stack in reverse order to which they
were put on. This is known as a LIFO - Last In,
First Out - structure.

Now the program. The idea is to implement a
digital counter, which will automatically update
its value by one whenever it is called, and
display the new value on the screen.

The clever bit is to ensure that this update is
shown graphically as rotating digits, just like
the real digital display. The assembly code for
this - figure two - can be divided into three
distinct stages

        EA60-EA76 which adds one to the
four-digit counter.

        EA77-EAB0 which does the on-
screen scrolling.

        EAB1-EABB which resets, ready
for the next call.

The comments should enable you to follow the
program, but here are a few points. When
incrementing the four digits - each held in one
byte, including a fifth overflow byte - the top
bit of the digit is set if it has changed. That is
the reason for the test 8Ah (Top Bit + 10).

Figure three should give some indication of how
the scrolling works. The code from EA88 to EA94
scrolls the necessary characters down by seven
pixel lines, and the code to EAA2 finds the value
of the next byte to be placed on the screen from
the Spectrum character set. The four instructions
from ESHIFT create a delay loop, to give you time
to see the display. To halve the speed of the
scrolling, increase the number loaded into E, at
ESHIFT, by one.

Finally, the two numbers held in location EAC1 and
EAC2 - label DSPLY - are the bottom- right screen
address of the four bytes displayed. Figure four
gives a list of the values you can place in here
to move the counter on the screen.

Figure five gives a Basic loader program and
example calling routine. You will notice that
lines 40 to 70 use our special abbreviations for
graphics characters, so please read the
instructions on the first page of Program
Printout.

You might like to change the program. First of
all, to make the program count downwards, rather
than up, change the following

Line 2010: 3C to 3D
           FE8A to FE7F
Line 2020: 3680 to 3689
Line 2170: 09090909 to 00000000
Line 2180: 00 to 09

This will then count down from 9999. Note that all
the digits change when the routine is first
called, ensuring that all the digits are
displayed.

If you would like to count from a different
number, simply change the start digits at the end
of line 2170, and print the start number in line
70, instead of the four spaces. The INK statement
in line 70 also specifies the colour of the
digits.

It is quite a simple matter to modify the code to
work with more than four digits. To add a fifth
digit, change thee start number (line 2170) to a
five- digit figure. You must also change the
address of the display location - presently EAC1.
So alter the C1EA to read C2EA in lines 2030 and
2050.



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

[see SU40_531.GIF]

Figure 1. Flag register



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

                     ORG   60000
                     LOAD  60000

EA60 21BCEA          LD    HL,NMBRS   ;HL = start of digits
EA63 3E80     OVFLOW LD    A,80H      ;set top bit in A
EA65 86              ADD   A,(HL)     ;add previous digit
EA66 3C              INC   A          ;    and update it
EA67 77              LD    (HL),A     ;replace digit in memory
EA68 FE8A            CP    8AH        ;has it reached 10?
EA6A 2005            JR    NZ,CHANGE  ;go on if not, else
EA6C 3680            LD    (HL),80H   ;reset digit to zero
EA6E 23              INC   HL         ;increment to next digit
EA6F 18F2            JR    OVFLOW     ;go back for next digit

EA71 21C1EA   CHANGE LD    HL,DSPLY
EA74 2B              DEC   HL         ;zero overflow digit
EA75 3600            LD    (HL),0
EA77 0608            LD    B,8        ;B counts 8 shifts
EA79 21BCEA   NSHIFT LD    HL,NMBRS   ;HL points to digits
EA7C ED5BC1EA        LD    DE,(DSPLY) ;DE = bottom right of display
EA80 7E       NDIGIT LD    A,(HL)     ;get digit in A
EA81 CB7F            BIT   7,A        ;is top bit set?
EA83 2824            JR    NZ,ESHIFT  ;jump to finish if not
EA85 D5              PUSH  DE
EA86 E5              PUSH  HL         ;stack registers
EA87 F5              PUSH  AF
EA88 62              LD    H,D
EA89 6B              LD    L,E        ;HL = display line above DE
EA8A 25              DEC   H
EA8B 0E07            LD    C,7        ;C counts 7 shifts
EA8D 7E       SCROLL LD    A,(HL)     ;move a byte to the
EA8E 12              LD    (DE),A     ;    line below
EA8F 15              DEC   D          ;move HL and DE pointers
EA90 25              DEC   H          ;    up by one line
EA91 0D              DEC   C
EA92 20F9            JR    NZ,SCROLL  ;repeat 7 times
EA94 F1              POP   AF         ;restore AF register
EA95 CB27            SLA   A
EA97 CB27            SLA   A          ;multiply A by 8
EA99 CB27            SLA   A
EA9B 80              ADD   B          ;add shift count
EA9C C680            ADD   A,80H      ;add lsb loc "0"
EA9E 6F              LD    L,A
EA9F 263D            LD    H,3DH      ;HL = 3D80H+8*digit+shift
EAA1 7E              LD    A,(HL)
EAA2 12              LD    (DE),A     ;display appropriate byte
EAA3 E1              POP   HL         ;restore digits to HL
EAA4 23              INC   HL         ;move to next digit
EAA5 D1              POP   DE         ;restore display position
EAA6 1B              DEC   DE         ;previous display position
EAA7 18D7            JR    NDIGIT     ;do next digit
EAA9 1E01     ESHIFT LD    E,1
EAAB 76       DELAY  HALT             ;delay to give user time
EAAC 1D              DEC   E          ;    to see the scrolling
EAAD 20FC            JR    NZ,DELAY
EAAF 10C8            DJNZ  NSHIFT     ;repeat eight times

EAB1 21BCEA          LD    HL,NMBRS   ;HL = start of digit data
EAB4 0604            LD    B,4        ;there are four digits
EAB6 CBBE     RESBIT RES   7,(HL)     ;top (changed) bit = 0
EAB8 23              INC   HL         ;next digit
EAB9 10FB            DJNZ  RESBIT
EABB C9              RET

EABC 09090909
EAC0 00       NMBRS  DB    9,9,9,9,0
EAC1 714F     DSPLY  DB    71H,4FH
                     END

Work area     - A692 to A82B
ORG end       - EAC3
LOAD end      - EA60

Figure 2. Assembly code



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

[see SU40_533.GIF]

Figure 3. Example of scrolling



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

[see SU40_534.GIF]

Figure 4. Screen display locations (hex)



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

  10 CLEAR 59999
  20 GO SUB 1000
  30 CLS
  40 INK 2: PRINT AT 10,13;"(ig4:4*g3:g7)"
  50 PRINT AT 11,13;"(ig5:4*sp:g5)"
  60 PRINT AT 12,13;"(ig1:4*ig3:ig2)"
  70 INK 1: PRINT AT 11,14;"(4*sp)"
  80 RANDOMIZE USR 60000
  90 GO TO 80
 100 STOP
1000 REM HEX LOAD ROUTINE
1010 DEF FN p(x)=CODE h$(x)-48-7*(CODE h$(x)>=65)
1020 LET byte=0
1030 RESTORE 2000
1040 READ start
1050 READ h$
1060 IF h$="*" THEN GO TO 1160
1070 IF LEN h$<>2*INT (LEN h$/2) THEN PRINT "Odd number of hex digits in: ";h$: STOP
1080 FOR i=1 TO LEN h$
1090 IF NOT((h$(i)>="0" AND h$(i)<="9") OR (h$(i)>="A" AND h$(i)<="F")) THEN PRINT "Illegal hex digit: ";h$(i): STOP
1100 NEXT i
1110 FOR i=1 TO LEN h$ STEP 2
1120 POKE start+byte,16*FN p(i)+FN p(i+1)
1130 LET byte=byte+1
1140 NEXT i
1150 GO TO 1050
1160 PRINT "Code entered"
1170 PAUSE 150
1180 RETURN
2000 DATA 60000,"21BCEA","3E80"
2010 DATA "86","3C","77","FE8A"
2020 DATA "2005","3680","23"
2030 DATA "18F2","21C1EA","2B"
2040 DATA "3600","0608","21BCEA"
2050 DATA "ED5BC1EA","7E","CB7F"
2060 DATA "2824","D5","E5","F5"
2070 DATA "62","6B","25","0E07"
2080 DATA "7E","12","15","25"
2090 DATA "0D","20F9","F1"
2100 DATA "CB27","CB27","CB27"
2110 DATA "80","C680","6F"
2120 DATA "263D","7E","12","E1"
2130 DATA "23","D1","1B","18D7"
2140 DATA "1E01","76","1D"
2150 DATA "20FC","10C8","21BCEA"
2160 DATA "0604","CBBE","23"
2170 DATA "10FB","C9","09090909"
2180 DATA "00","714F","*"

Figure 5. Basic program



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

JP        address      - jump to address
JP        cond,address - jump to address if condition is true,
                         where condition is:
                                NZ - non-zero
                                Z  - zero
                                NC - no carry
                                C  - carry
                                PO - parity odd
                                PE - parity even
                                P  - positive
                                M  - negative
JP        (HL)         - jump to the address specified by the HL register pair
JR        offset       - jump to present address plus offset
JR        cond,offset  - jump to present address plus offset if the condition
                         is true, where condition is:
                                NZ - non-zero
                                Z  - zero
                                NC - no carry
                                C  - carry
CP        data         - set flags for result of Accumulator minus data
CP        register     - set flags for result of Accumulator minus register
CP        (HL)         - set flags for result of Accumulator minus the contents
                         of the location held in the HL register pair
BIT        bit,reg     - test specified bit in register
BIT        bit,(HL)    - test specified bit in contents of location
                         addressed by HL register pair
SET        bit,reg     - set specified bit in register
SET        bit,(HL)    - set specified bit incontents of location
                         addressed by HL register pair
RES        bit,reg     - reset specified bit in register
RES        bit,(HL)    - reset specified bit incontents of location
                         addressed by HL register pair
DJNZ        offset     - decrement the B register. If the contents are
                         NOT ZERO, then jump to present address plus offset
PUSH        rp         - write register pair to top of stack
                         (rp = AF, BC, DE or HL)
POP        rp          - read register pair from top of stack
                         (rp = AF, BC, DE or HL)

Figure 6. New Z80 instruction codes



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