                     Spectrum MC/Basic

                     Sorting Variables



          If you find that setting the parameters

          of a machine-code routine is a bit of a

          drag, Stewart Nicholls' program could

             give you the best of both worlds.



Do you get tired of having to set parameters of a machine-

code routine by poking numerous addresses with the neces-

sary values? If you do, the following machine-code routine

is the answer: it makes poking completely unnecessary.

  The problem is avoided by setting up the values using

Basic variables and then searching the variables area to

find their value and storing them in the spare bytes of the

printer buffer.

  To demonstrate the technique, I have attached the search

program to a routine which prints a message anywhere on the

screen using characters of any height or width, in any ink

colour.

  The x and y variables are used for the start of the mes-

sage; since these are the co-ordinates of the top-left

corner of the first character of the message the usual

Spectrum plot positions apply. That is, 0,0 is the bottom

left-hand corner of the screen. For the height of the cha-

racters, h is used. Normal height is 1, 22 will be full

screen. Width of characters is 2. Normal width is 1, 32 is

full screen for one character.

  The string containing the message to be printed will be

a$. The only limitations on the message are that it can

only contain characters from code 32 - space - to code 127

- copyright - and that the number of characters multiplied

by width of character should not exceed 23 with the plot

position at the left-hand edge of screen. If this is ex-

ceeded the program will not crash but the message will wrap

round the screen overprinting as it goes.

  The program is fully mug-trapped and any wrong parameter

of x and y will be corrected before plotting the string.

The machine code to find the variables x, y, h, w and a$ is

shown in listing 1. [Listing 1 was a code loader, "large"

on the TZX. This loaded (and tested) the machine code de-

scribed, which is on the TZX as "largecode".]

  If you read through Chapter 24 of the Sinclair Manual,

you will see that there are six types of variable, namely:

a number whose name is one letter - type (i); a number

whose name is two or more letters - type (ii); an array of

numbers - type (iii); FOR-NEXT loop control - type (iv);

string variable - type (v); an array of characters - type

(vi).

  Each of these variables is uniquely identified by its

first byte.

Variable type i = 96d + (letter code - 96d)

Variable type ii = 160d + (first letter code - 96d)

Variable type iii = 128d + (letter code - 96d)

Variable type iv = 224d + (letter code - 96d)

Variable type v = 64d + (letter code - 96d)

Variable type vi = 192d + (letter code - 96d)

The variables we will be searching for will be types (i)

and (v).

  If we take, for example, the variable x which has a cha-

racter code of 120, then using the formula (i) given, its

unique first byte is 120. So in order to find variable x we

must search through the variables for this unique code,

which will then point to the start of the x variable infor-

mation. This is not just a simple case of running through

the variables one byte at a time using a CPIR instruction,

as this may find the code 120 held in a string or an array

of characters. So we must find the start of each variable,

check if it is the one we want, and if not, then jump to

the start of the next variable. This means that we must

identify the type of variable found before we know how far

to jump to the next one. This is not as difficult a task as

it might first appear because of the way in which the

Spectrum stores the variable parameters.

  With three of the variables, namely array of numbers,

array of character and strings, the length of the variable

is held in the two bytes following the unique byte code.

What is more helpful, bit 5 of the unique code is zero and

the remaining three variables have bit 5 set at one.

  The length of the three remaining variables can be calcu-

lated by this method:

Type (i) length = 6 bytes including unique code.

Type (ii) length = number of letters of variable + 5 bytes.

Type (iv) length = 19 bytes including unique code.

We now have the information to enable us to jump over each

type of variable, and the checks must be made in the follo-

wing order: first, check if the code is 120; if yes then

return from routine; check bit 5 and if zero jump by

    value in next two bytes + 1 : GOTO 1

Check bit 6 and if zero check following bytes for

            BIT 7 = 1

that is the last character of the variable, then jump six

bytes and Goto 1. Check but 7 and if zero then jump 6 bytes

and Goto 1. Now it must be a FOR/NEXT loop so jump 19 bytes

and Goto 1.

  The mnemonics of the machine-code to do this are shown in

the Find subroutine. This uses address 23728 to hold the

unique code of the variable for which we are searching. The

HL register holds the address being checked, and the accu-

mulator is loaded with the unique code from address 23728.

  The subroutine only takes 46 bytes to check, jump and

locate the start address of any variable.

  Now that we have found the start address of our variable

x we must find its value. You will see that the value for

whole numbers is held in the third and fourth bytes follo-

wing the unique code. In our case we are only interested in

numbers from 0 to 255 for x. So we can ignore the high byte

and store the low byte, that is the third byte. This

applies to all our variables x, y, h and w.

  With this in mind a subroutine, Setup, can be assembled

to move along three bytes from the address held in HL and

then store the value held in this address in the printer

buffer area of memory. So we now have the means of finding

and storing values of x, y, h and w in addresses 23296/7/8

and 9.

  We can now move to the slightly more complicated string

parameters. In our case we need to find a$, unique code 65.

The same Find routine can be used to find the start of the

variable. Once found we then move to the next two bytes to

find the LEN of the string as follows:

        INC HL

        LD E,(HL)

        INC HL

        LD D,(HL)

This will put the length of the string into the DE regis-

ter, and, as we can safely limit the length of the string

to 255 characters we only need to store the number in low

byte:

        LD (23300),DE

The number in address 23301 will be overwritten with the

first character in the string:

        PUSH DE      Get number of characters into BC

        POP BC

        INC HL       Set HL at start of characters

        LD DE,23301

        LDIR         Transfer information

The above will transfer the string characters from the

variables to the printer buffer starting at address 23301.

  With this routine we now have x, y, h, w, length of

string, and characters in string stored consecutively in

the printer buffer, and can go straight into the routine to

plot the string based on these parameters.

  The plot routine finds the start of the eight bytes for

each character in the character and, for each byte, a

Rotate Left instruction is carried out either plotting or

unplotting depending whether Carry is set or not. Again use

is made of the two unused bytes in the Systems Variables

23728/9 to hold and update the x,y plot positions for each

character.

  If the y plot position goes below zero, then it is reset

to 175 to give a wrap around effect and likewise if the x

position goes above 255 it is reset to zero. So now we have

a machine-code program than can be called from Basic with

no POKEs in sight - for example:

    10 LET x=0: LET y=0: LET h=8: LET w=4:

  LET a$="Finished": INK 6: RANDOMIZE USR 32393

This saves 13 POKEs including LEN a$. Listing 1 ["large"]

shows the Basic program to set up the machine code above

RAMTOP. On a 16K Spectrum this is immediately before the

user-defined graphics and occupies 255 bytes. Once the

machine-code is entered the Basic can be NEWed and the code

saved in the usual way.

  There are a couple of points to watch with this program.

First, ensude that all the variables are defined before a

call is made to the routine as failure to find a variable

will crash the program. Second, do not use x, y, h or w as

a control variable in a FOR-NEXT loop because the Spectrum

will then delete the simple variable and use the FOR-NEXT

loop to hold further values of x, y, h and w. This can be

demonstrated by the simple Basic program:

    10 FOR a=1 TO 10

    20 PRINT a;

    30 NEXT a

    40 LET a=3

    50 NEXT a

This program will produce a continuous loop resetting a to

3 and jumping back into the FOR-NEXT loop showing that the

variable - a - is line 40 is part of the FOR-NEXT loop

control variable. If this was not the case the Spectrum

would give an error report:

    1 NEXT without FOR, 50:1

  You can have a small Basic subroutine to work out the

width of characters and the start plot position to give

information printed centrally on any line; such a program

is given in listing 2 with sample printout. [This is called

"Plot" on the TZX, with one line added to auto-load the

machine code.]

  We can now tackle a machine-code program to print out all

the variables used after a program has been run. Note that

this may not be all the variables in the listing as when a

program is run certain subroutines may not have been called

and so variables held in those routines will not have been

placed into the variables area.

  This program - listing 3 - could be of use in debugging

Basic programs. Listing 3 is in the form of a machine-code

dump which should be held in DATA statements as listing 1,

and poked into a memory position of your choice. There are

no Jump or Call commands to within the program. I find it

handy to have two versions of the program, one stored above

RAMTOP and another in a line 1 REM statement containing 331

zeros and called using

        RANDOMIZE USR 23760

The machine-code is then held in the Basic area of RAM and

can be merged with any program - as long as the program

does not have a line 1, which will be overwritten. One

point to note when saving the Basic REM statement on tape

is to use CLEAR to erase all variables used in the machine-

code loader, otherwise these will be saved and merged along

with the REM statement. This is good practice on any pro-

gram which does not require the variables to be saved.

[This code, saved as a CODE file ready to be loaded any-

where in memory, including into a REM statement should you

want to, is on the TZX as "variables".]

  Listing 4 is a demonstration program setting up varia-

bles, and shows a screen copy of the machine-code output

which lists these variables under their variable types.

Notice how the Spectrum converts all variables to lower

case and also that variables a, h, l, o, w, x and z have

been listed under the FOR-NEXT loop only and not under

number. [This is on the TZX as "Basicvars".]

  Listing 5 shows the machine-code mnemonics for assembly

into a REM statement, for anyone who wishes to check

through the program to see how it works. The first section

from addresses 5CD0 to 5CE5 sets up the border, paper and

ink colours; from 5Ce6 to 5D46 prints the heading. The

remainder of the program checks the variables one by one

finding the type of variable, locating the next print posi-

tion in that variables column, updating the print position

and printing the variable.

  The section from 5D94 to 5DBE is interesting in that it

checks for a column reaching the bottom of the screen and

if it does then waits for a key press before scrolling 21

lines, leaving the heading on the screen and printing the

next variable in position. A return to Basic is only made

when all the variables have been listed.



[ As listing 5 provided nothing more than a bare assembly

dump of the code (with a typo, to boot), I haven't copied

it. Listings 1 and 2, however, were accompanied by (unnum-

bered) hex listings with labels (which are referred to in

the article). The order was not quite rational, but sorted

by address, these listings were as follows: ]



Start address for 16K machine code 32235

        ORG 32235

Subroutine to find variables

FIND    LD HL, (23627)

START:  LD A, (23728)

        CP (HL)

        RET Z

        BIT 5, (HL)

        JR NZ, NEXT

        INC HL

        LD E, (HL)

        INC HL

        LD D, (HL)

        ADD HL, DE

        INC HL

        JR START

NEXT:   BIT 6, (HL)

        JR NZ, NEXT1

LOOP5:  INC HL

        LD A, (HL)

        BIT 7, A

        JR Z, START

LOOP6: LD DE, 6

        ADD HL, DE

        JR START

NEXT1:  BIT 7, (HL)

        JR Z, LOOP6

        LD DE, 19

        ADD HL, DE

        JR START



Subroutine to set parameters of variable to be found

SETUP   LD (23728), A

        CALL FIND

        INC HL

        INC HL

        INC HL

        LD A, (HL)

        LD (BC), A

        RET



Start of machine-code routine

RUN     LD BC, 23296

        LD A, 120

        CALL SETUP

        INC BC

        LD A, 121

        CALL SETUP

        INC BC

        LD A, 104

        CALL SETUP

        INC BC

        LD A, 119

        CALL SETUP

        LD A, 65

        LD (23728), A

        CALL FIND

        INC HL

        LD E, (HL)

        INC HL

        LD D, (HL)

        LD (23300), DE

        PUSH DE

        POP BC

        INC HL

        LD DE, 23301

        LDIR



Routine to plot the string

        LD HL, (23296)

        XOR A

        LD A, H

        SBC A, 176

        JR C, YES

        LD H, A

        LD (23296), HL

YES:    LD (23728), HL

        LD HL, 23301

RUN1:   PUSH HL

        LD A, (HL)

        LD H, 0

        LD L, A

        ADD HL, HL

        ADD HL, HL

        ADD HL, HL

        LD DE, 15360

        ADD HL, DE

        LD B, 8

LOOP4:  PUSH BC

        LD BC, (23297)

LOOP3:  LD A, (HL)

        PUSH HL

        PUSH BC

        LD B, 8

LOOP2:  PUSH BC

        RLA

        PUSH AF

        JP C, PLOT

        LD HL, (23299)

        LD A, (23728)

        ADD A, L

        LD (23728), A

        JP 32527

PLOT:   LD BC, (23298)

LOOP1:  PUSH BC

        LD BC, (23728)

        PUSH BC

        CALL 22E5H

        POP BC

        INC C

        LD (23728), BC

        POP BC

        DJNZ LOOP1

END:    POP AF

        POP BC

        DJNZ LOOP2

        LD A, (23296)

        LD HL, 23728

        LD (HL), A

        INC HL

        XOR A

        LD A, (HL)

        SBC A, 176

        JR C, OK

        LD (HL), A

        JR CONT

OK:     LD A, (HL)

        CP 0

        JR NZ, OK1

        LD (HL), 176

OK1:    DEC (HL)

CONT:   POP BC

        POP HL

        DJNZ LOOP3

        INC HL

        POP BC

        DJNZ LOOP4

        LD A, (23299)

        ADD A, A

        ADD A, A

        ADD A, A

        LD L, A

        LD A, (23728)

        ADD A, L

        LD (23296), A

        LD (23728), A

        LD A, (23297)

        LD (23729), A

        POP HL

        INC HL

        LD A, (23300)

        DEC A

        RET Z

        LD (23300), A

        JP RUN1