

SPECTRAMON

by Simon Goodwin

from ZX Computing Apr/May&Jun/Jul.1983



Simon Goodwin of Hereford unveils an excellent program for the 48K Spectrum.





If you've ever wondered how your ZX Spectrum works,

Spectramon (the Spectrum monitor) will make it easy for you

to find out. This program will print or display the contents

of ROM or RAM in numeric, character or assembly language

form. Addresses may be entered in decimal or hexadecimal,

and the user may select the base used for output.



Spectramon will run on a 48K Spectrum with or without a

printer. The disassembler option has been written with the

failings of ZX80 and ZX81 programs in mind - unlike other

published listings it will handle all 694 standard Z80

instruction codes, using the standard mnemonic names and

formats devised by Zilog, the firm which designed the Z80

processor used in the Spectrum.





Z80 instructions



The Z80 instruction set is the most complicated of any 8-bit

microcomputer. The Z80 processor was designed by a group of

people who left Intel, the firm which makes the 8080

processor, to set up Zilog. The Z80 will execute any of the

instructions of an 8080, plus a large number of extra ones

'tacked on' by Zilog. This approach meant that programs

written to run on an 8080 would also run on a Z80 without

changes. New programs could then be written using the added

facilities of the Z80. That was how many early Z80 programs

were produced. The BASIC interpreter used on the TRS-80, for

instance, is substantially an 8080 program even though the

TRS-80 has a Z80 processor. Only the display and keyboard

routines contain Z80 instructions since they were the last

to be written.



Sinclair BASIC is written using the full features of the Z80

processor. Zilog added instructions to handle fast moving

and searching of tables in memory, extra registers (internal

storage) and instructions to increase the number of things

that could be done with the original 8080 registers. They

wanted to more than double the number of possible

instructions, but there was a problem - Intel had decided to

use a single byte (8 bits) to store the instruction numbers

for the 8080, and most of the 256 possible numbers were

already in use.



Zilog got around this by giving four instruction numbers

special meanings - instructions with one of those numbers

would carry out a certain 'class' of operation, and the next

byte would explain the operation required in detail. In

theory, that gave Zilog plenty of possible numbers - 252

(using the remaining one-byte values) plus 1,024 (4 * 256)

if they were to use all of the possible two-byte

instructions. In practice, they only used 694 of the 1,276

possibilities, but that's still a very large number of

instructions for an 8-bit computer!



If you consult [Appendix A of the Sinclair Spectrum manual,

you will see the standard Z80 mnemonics listed. The prefix

byte 203 is used to generate add-on instructions for

'bitwise' operations - instructions which manipulate or test

binary digits. The prefix byte 221, indicates that the next

instruction is an 8080 one, which would use register pair HL

but must now use register IX instead. Likewise, the prefix

253, indicates that IY should replace HL in the next

instruction. If HL was in brackets in the old 8080

instruction (as in LD a, (HL)) then the Z80 version allows

an offset to be applied to IX or IY before use - this is

specified in an extra byte after the end of the 8080

instruction. Finally, the prefix 237 is used to indicate

that the instruction following is one of a group of

miscellaneous Z80 add-ons.





Monitoring the situation



If all this sounds very complicated you've probably realised

why a monitor is a useful program - Spectramon will

automatically convert sequences like 'EDH 7BH 3DH 5CH' into

the mnemonic: LD SP,(23613). The EDH told Spectramon that it

was a miscellaneous Z80 add-on instruction (EDH is 237

decimal). The 7BH corresponds to LD SP,(some address), and

the 3DH 5CH corresponds to the value 23613. To check that,

convert 3DH and 5CH to decimal then add the first result to

the second (multiplied by 256). It's an awful lot simpler to

let the computer puzzle that out than it is to work it out

for every instruction by hand.



Of course, you may think that LD SP,(23613), is just as

baffling as EDH 7BH 3DH 5CH - in which case, you'll have to

learn a little about Z80 machine code before Spectramon

becomes useful to you. Before you can investigate the ROM of

a computer, you do need to understand the computer language

in which it was written - assembler, in most cases. LD

SP,(23613) is an assembler (or "assembly language" or

"machine code') command. If you don't understand assembler,

please don't throw this article away! It will take you no

longer to learn assembler than it did to learn BASIC (it

should be just as much fun too) and you can come back to

Spectramon when you know more.



In fact, the instruction LD SP,(23613) has a very simple

purpose - it tells the computer to put the number in address

23613 into the register called 'SP'. If you consult the

Spectrum manual you will find out that 23613 is the "address

of item on machine stack to be used as error return", which

tells you that the instruction is part of the ROM

error-handler. Using a disassembled listing and the table of

'System variables' in chapter 25 of the manual, you can

trace your way through the ROM, finding out what each

section does.





Using the program



Spectramon takes about 15 seconds to set itself up when

first RUN. During this time, it is building a table of

instruction codes for the disassembler, and once that is

complete, the menu of commands will appear.



To quit from the monitor, type 'Q' followed by Enter. This

returns you to ZX BASIC. If you wish to disassemble a

program in RAM or ROM, then you should type 'D' followed

immediately by the address at which you want to start.

Addresses may be entered to Spectramon in decimal or Hex -

if you want to disassemble from address 126 (decimal), you

could type Dl26 or D007EH or D7EH - leading zeros are

optional - and if you enter more than four Hex digits, only

the last four will be considered. If a meaningless address

is typed (such as D, DFF, D123456 or D-1) then the command

will be ignored.



The disassembler displays the contents of memory one

screenful (21 lines) at a time. The left-hand column shows

the address of the instruction. It is followed on the same

line by a hexadecimal representation of the instruction, and

then the assembly language text. After 21 lines have been

displayed, the prompt "More? (Enter = No)" will appear.

Press any alphabetic or numeric key and the listing will

continue on a new screen. Press the Enter key to return to

the menu.



After each line is displayed, the program checks to see

whether or not a key has been pressed. The Space key pauses

the display, which will continue when any alphanumeric key

is pressed. The Enter key causes disassembly to cease and

the menu is displayed.





Magic numbers?



The third option allows display of the numeric contents of

memory. Although the disassembler does this, it only lists

between one and four bytes per line (depending upon the

instruction). The N command allows eight bytes to be listed

on each line of the display. A start address may be

specified in Hex or decimal, just as for the D command.



The N command is useful for displaying the contents of

tables used by a program or the ROM. Type 'N150' to see the

Spectrum reserved-word table. That is where ZX BASIC stores

the spellings of words such as PRINT and RETURN. The words

are stored in a modified version of ASCII code - the last

letter of each word has 128 (80H) added to it, to make it

easy for the ROM routine which displays words to find where

each one starts and ends.



If you found the numeric representation of the BASIC words

rather hard to follow, you can use the command A150 to

display the reserved word table in character form. The

command uses 7-bit ASCII values, so that letters with 128

added to their code still print out correctly. To avoid

changing colours or moving the cursor unexpectedly, the

ASCII output routine displays control characters (those with

a code less than 32) as full stops.



You can use the Space and Enter keys to control listings

output by commands N and A, just as you would for a

disassembly. Every 21 lines the "More? (Enter=No)" message

will appear before a new screen is started.



The final two commands don't output anything themselves, but

they do change the output which the others generate. When

you first RUN Spectramon, the message "P Printer option (Now

OFF)" appears. Type the command 'P' followed by Enter and

the message will become "P Printer option (Now ON)". If you

then display memory contents (using A, N or D) the

information will be sent to the printer as well as the

television. Once you've finished printing, press Enter to

stop the display and then use the command P to switch the

printer option off again.



Notice that the printer routine does not output any lines

until an entire screen-full has been generated. In fact, it

deliberately avoids using the LPRINT statement to send each

line to the printer. Instead it uses COPY, the ZX BASIC

command which sends all of the text on the screen to the

printer. That's because it is almost twice as fast to build

up a full screenfull of data and then print it using COPY

than it is to use LPRINT for each line as it is generated.



The printer can't stop and start very quickly and

consequently the LPRINT statement is much slower than COPY -

the printer must rev up and slow down 21 times (once for

each line) instead of just once. In fact, the printer always

outputs the last line of a group at half speed, to make sure

that everything falls in the correct place when it stops. As

far as it is concerned each LPRINT is the last line of a

group (when there's less than 33 characters being printed).





Base choice



The final option allows the user to select the base in which

numbers are output by the program. Sometimes it is useful to

have numbers printed in decimal (for example, when referring

to addresses mentioned in the Spectrum manual) and sometimes

hexadecimal is more convenient (when displaying address

tables or working out jump offsets). Type the command 'B' to

change the output base. When you first run Spectramon it

will be Hex (hence the display "B Base selection (Now =

HEX)") but you can switch it to decimal with the B command.

If you wish to re-select Hex output later you can 'toggle'

back by typing 'B' again.



If an unknown command is entered, Spectramon will ignore it.

If it is called upon to show the contents of non-existent

memory (past address 65535) it will display the message "End

of Memory". If the end of memory is encountered while the

program is half-way through processing a line of numbers or

ASCII characters, it will fill the rest of the line with

zeros or spaces.



If you have to stop the monitor for any reason by typing

Break (perhaps because your desk has melted from under the

computer or the ZX printer is strangling itself) you can

re-start Spectramon by entering GO TO 200 after the panic is

over. So long as you've not typed LOAD, CLEAR or NEW in the

meantime, the menu will appear immediately (without the 15

second wait for table set-up) and the current base (Hex or

Decimal) will be preserved.





The next byte



The second part of this article, complete with program

listing for Spectramon, will be published in the June/July

issue of ZX Computing. In the meantime, if you can't wait to

try out Spectramon for yourself, a tape of the program is

now available from ASP Software priced at #5.99. For more

details, check out the advertisement elsewhere in this

issue.





= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =



Presenting the second part of this feature article,

including the full listing of Simon Goodwin's incredible

Spectrum monitor program.





Spectramon is written in ZX BASIC but it should be quite

easy to convert for other computers. Obviously, it will only

be useful on machines which use the Z-80 processor!



The Spectrum CODE function corresponds to ASC on other

computers - brackets around its argument are optional in ZX

BASIC. String arrays are handled rather oddly by Sinclair

BASIC - the variable Z$ is set up by line 40 as having a

fixed length of 32. Unused character-positions contain

spaces - so that Z$ is simply used as an array of space

characters by the instruction formatting routine. The array

O$ contains 608 strings (numbered from one, not zero) and

each string has a fixed length of nine characters (line

130).



The other string variables are normal 'Microsoft' strings -

they vary in length to accommodate whatever is stored in

them. ZX BASIC allows sub-strings to be extracted from a

string using the 'TO' instruction - A$(1 TO 1) returns the

first character of a string, corresponding to LEFT$(A$,1) in

Microsoft BASIC. If A$ is set up as 'SPECTRAMON' then

A$,6,3). [sic] In short, the 'TO' instruction extracts all

the characters from one position TO another, inclusive.



Spectrum BASIC allows long variable names to be specified,

and (unlike Microsoft BASIC) all the characters of a name

are significant. On the Spectrum, INDEX and INDIRECT are two

different, valid variables - in Microsoft BASIC they will

have to be renamed, otherwise they would be treated as the

same variable because they have the same first two

characters. In some versions of Microsoft BASIC, neither

variable name would be allowed since they both contain the

key-word 'IN'. Sinclair BASIC is also unusual in that it

allows spaces to occur in variable names. Table 1 shows all

the variable names used in Spectramon and documents their

usage.



Other systems can ignore the lines using COPY to send out a

listing and simply LPRINT L$ if LP=1, printing out lines one

at a time rather than en masse. A user defined function is

set up in line 50, but it is fairly easy to code around this

if your computer doesn't support that feature. FN H(H$)

simply returns the decimal value of the first character in

H$ - 1 for '1', 10 for 'A', 11 for 'B' and so on.



Spectramon uses a few PEEKs and POKEs which will not be

required on other systems. POKE 23658,8 is a useful command

which forces the Spectrum into capitals-lock (selecting a

flashing 'C' as a cursor rather than a flashing 'L'). This

ensures that commands are entered in capitals (unless the

user purposely switches to lower-case in the course of

entering a command). The location 23689 contains the number

of empty lines on the Spectrum screen - when PEEK 23689 is

three or less the screen is assumed to be full since the

bottom two lines aren't normally used for text and a line is

needed for the "More? ..." message.



Location 23560 contains the ASCII code of the key most

recently pressed. It is set to 32 when the space bar has

been pressed (or is being simulated) and 13 when Enter has

been typed.





The last word ...



When I received my Spectrum I was convinced that I'd never

get used to the keyboard. After writing, editing and typing

in Spectramon I was well practiced! Hopefully, the program

also illustrates a few useful quirks of ZX BASIC, both from

the BASIC and the assembler programmer's point of view.





= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =



Table 1. Variables used in Spectramon



Z$ -              Fixed length string of 32 spaces, used in formatting.

H$ -              Hex characters '0'-'F' - also a local variable used in the

                  Hex-Decimal conversion function, FN H (line 50).

LP -              'Flag' set to 1 if printout is required.

DEC -             'Flag' set to 1 if numbers must be output in decimal.

CheckIndex -      Line number of the routine which checks to see whether an

                  operation could involve IX or IY.

GetInstruction -  Line number of the routine which formats a complete line of

                  disassembler output.

MakeText -        Line number of the routine which formats a complete line of

                  disassembler output.

ByteValue -       Line number of a routine which expresses the contents of

                  C (0-255) in C$, using the current base.

WordValue -       Line number of a routine which sets up C$ with a string copy

                  of C (0-65535) in the current base.

F$ -              String containing register names.

O$ -              String array containing the opcode text.

I,K,T -	          Loop counters and temporary values.

A$ -              The command typed in by the user.

C$ -              The first character of the command.

SUB -             Line number of the chosen monitor subroutine.

LOC -             The location being examined by the monitor.

L$ -              The line of text to be output by the monitor.

I0,I1,I2 -        The instruction code and its operands.

N$ -              The name of the current index register.

S$ -              The name of the current indirect register (N$).

M$ -              The mnemonic form of the instruction.

NBYTES -          Length of instruction, in bytes.

INDEX -           Set to 1 if IX or IY are to replace HL.

INDIRECT -        Set to 1 if (IX) or (IY) are to replace (HL).

R$ -              Character within instruction mnemonic.

MODE -            Addressing mode 0-9; declares number and format of operands.

C -               Number for conversion into a decimal or Hex string.

C$ -              Number after conversion into a string.

D$ -              Part of disassembler output line.



= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =





[I've made the following corrections to the listing. JimG.]



Line 6090 should just have eight "9" elements in the DATA statement, not nine.



Line 1320 should be LET i0=i0+416





= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =



[Letter from ZX Computing Aug/Sep.1983.                           ]

[The corrections given here have also been included.         JimG.]





BYTING BUGS



Dear ZX Computing,



I'm afraid I have found a couple of minor errors in my

Spectramon program. However, I have two solutions to these

problems:



1) When the Spectrum tries to disassemble close to the top

   limit of memory, because of the way in which the Z80

   instruction set is constructed we may have a 'look ahead' by up

   to four bytes. If you are at location 65533 and this 'look

   ahead' occurs it will try to PEEK beyond the range of memory.

   This causes an "out of range" error. There is no true solution

   without major alteration of the program. However, a simple

   'fix' can be achieved by changing line 605 to read:



    605 IF loc>65532 THEN PRINT "End of memory".": POKE 23560,32:

        GO TO 610: REM Pretend SPACE was typed



2) The other problem in the program is far more subtle and

   occurs when the Spectrum tries to wrap around its

   memory map going from 65535 back to zero. This

   shows up as a subscript error when using the hexadecimal

   conversion routine. This can easily be cured with the 

   addition of the line 3435:



   3435 IF c>65535 THEN LET c=c-65536



Hopefully these two solutions will end all your worries with

my Spectramon program.



Yours faithfully,



Simon Goodwin, Hereford





--

Another Fine Product transcribed by:

Jim Grimwood (jimg@globalnet.co.uk), Weardale, England

--

