FIRST STEPS IN MACHINE CODE

hbar

PART 5: ROM ROUTINES

hbar

An introduction to Z80 Machine Code by David Nowotnik.

Within the 16K of ROM fitted inside the Spectrum, there is a wealth of machine code subroutines. These, of course, supply the operating system and BASIC translators for the computer, 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, well complete the examination of bit operations started last time.

If you recall, in the last part 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 bytes 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 Shift/Rotate demonstration program.

 DOWNLOAD SHIFT/ROTATE DEMONSTRATION 

It shows you how each of the shift and rotate instructions carry out their operations - in slow motion.

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 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 byte(s) 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 byte(s) shown on the screen. As the original value(s) will also be on the screen, you should be able to see the effect of the operation on the byte value(s). 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.

All the Z80 opcodes for the shift and rotate instructions can be viewed in the following table.

 VIEW SHIFT/ROTATE OPCODES 

That covers all the bit operations on the Z80s basic register array. Now, lets go on to the ROM routines.

ZX Line

THE ROM

The ROM 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, Dr. Ian Logan and Dr. Frank O'Hara spent many hours carefully disassembling the ROM, then published the results of their efforts. This publication contains 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. The book is published by Melbourne House press; and is called The Complete Spectrum ROM Disassembly. Here is a list of just a few of the ROM routines.

028E

Keyboard scanning routine

03B5

BEEP routine

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.

To use any of them from your own routine, use CALL address, where address is the routines start address (given above).

There are a special set of subroutines at the beginning of ROM which have a different instruction for their implementation. These are the RST calls, the 'RESTARTS'. There are eight RST opcodes, and, you will see, 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 are shown in the RST commands list.

 VIEW RST COMMANDS LIST 

There will be some examples, and hints, on using RST commands later, in the machine code examples section.

ZX Line

MORE REGISTERS...

As a final bit of theory for this part, 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. The first example 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.

Breakdown of a byte

The diagram above shows 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.

 VIEW ASSEMBLY LANGUAGE LISTING 

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 0 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; 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. This is why there is a CP (compare) instruction in the 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 hexloader as the program below, 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.

 DOWNLOAD EXAMPLE ONE 

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 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.

 DOWNLOAD EXAMPLE TWO 

The other machine code example is shown above. 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 putting 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.

hbar

From
ZX Computing Volume 2 Number 8
August/September 1985

hbar

Go To Previous Page Go To eZine X Page  Go To Contents Page  Go To Next Page