• Keine Ergebnisse gefunden

COMMON ERRORS IN INTERRUPT SERVICE ROUTINES

Im Dokument Assembly Language Subroutines (Seite 171-176)

Many errors that are related to interrupts involve both hardware and software. The following are some of the more common mistakes:

· Failing to reenable interrupts. The Z80 disables interrupts automatically after accepting one, but does not reenable interrupts unless it executes EI.

· Failing to save registers. The Z80 does not automatically save any registers except the program counter, so any registers that the service routine uses must be saved explicitly in the stack.

· Saving or restoring registers in the wrong order. Registers must be restored in the opposite order from that in which they were saved.

· Enabling interrupts before initializing modes, priorities, the interrupt vector register, or other parameters of the interrupt system.

· Forgetting that the response to an interrupt includes saving the program counter at the top of the stack. The return address will thus be on top of whatever else is in the stack.

· Not disabling the interrupt during multi-byte transfers or instruction sequences that cannot be interrupted. In particular, watch for possible partial updating of data (such as time) that a service routine may use.

CHAPTER 3 COMMON PROGRAMMING ERRORS

159

· Failing to reenable interrupts after a sequence that must be run with interrupts disabled. One problem here is that interrupts should not be enabled afterward if they were not enabled originally. This requirement is difficult to meet on the Z80 since its interrupt enable is not directly readable. The only way to access the interrupt flip-flop is by executing LD A,I or LD A,R; either instruction moves the interrupt flip-flop to the Parity

I

Overflow flag.

· Failing to clear the signal that caused the interrupt. The service routine must clear the interrupt even if no 110 operations are necessary. For example, even when the processor has no data to send to an interrupting output device, it must nonetheless either clear or disable the interrupt. Otherwise, the processor will get caught in an endless loop. Similarly, a real-time clock will typically require no servicing other than an updating of time, but the service routine still must clear the clock interrupt. This clearing may involve reading a timer register.

· Failing to communicate with the main program. The main program will not know that the interrupt has been serviced unless it is informed explicitly. The usual way to inform the main program is to have the service routine change a flag. The main program can tell from the flag's value whether the service routine has been executed.

This procedure works like a postal pairon raising a flag to indicate that there is mail to be picked up. The letter carrier lowers the flag after picking up the mail. Note that this simple procedure means that the main program must examine the flag often enough to avoid missing changes in its value. Of course, the programmer can always provide a buffer that can hold many data items.

· Failing to save and restore priority. The priority of an interrupt is often held in a write-only register or in a memory location. That priority must be saved just like a CPU register and restored properly at the end of the service routine. If the priority register is write-only, a copy of its contents must be saved in memory.

REFERENCES

1. Duncan, F.G., "Level-Independent Notation for Microcomputer Programs,"

IEEE Micro, May 1981, pp. 47-52.

Introduction to the Program Section

The program section contains sets of assembly language subroutines for the Z80 microprocessor. Each subroutine is documented with an introductory section and comments and is followed by at least one example of its use. The introductory material contains the following information about the purpose of the routine: its procedure and the registers that are used; the execution time, program size, and data memory required for the routine; as well as special cases, entry conditions, and exit conditions.

We have made each routine as general as possible. This is particularly difficult for the inputl output (II 0) and interrupt service routines described in Chapters 10 and ll, since these routines are always computer-dependent in practice. In such cases, we have limited the computer-dependence to generalized input and output handlers and inter-rupt managers. We have drawn specific examples from computers based on the CP

1M

operating system, but the general principles are applicable to other Z80-based com-puters as well.

In all routines, we have used the following parameter passing techniques:

I. A single 8-bit parameter is passed in the accumulator. A second 8-bit parameter is passed in register B, and a third in register C.

2. A single 16-bit parameter is passed in register pair HL with the more significant byte in H. A second 16-bit parameter is passed in register pair DE with the more significant byte in D.

3. Large numbers of parameters are passed in the stack, either directly or indirectly.

We assume that subroutines are entered via a CALL instruction that places the return address at the top of the stack, and hence on top of the parameters.

Where there has been a choice between execution time and memory usage, we have generally chosen to minimize execution time. We have therefore avoided slowly executing instructions such as stack transfers and instructions that use the index registers, even when they would make programs shorter. However, we have used

161

162

Z80 ASSEMBLV LANGUAGE SUBROUTINES

relative jumps whenever possible rather than the slightly faster but longer absolute jumps to make programs easier to relocate.

We have also chosen the approach that minimizes the number of repetitive calcula-tions. For example, in the case of array indexing, the number of bytes between the starting addresses of elements differing only by one in a particular subscript (known as the size of that subscript) depends only on the number of bytes per element and the bounds of the array. Thus, the sizes of the various subscripts can be calculated as soon as the bounds ofthe array are known; the sizes are therefore used as parameters for the indexing routines, so that they need not be calculated each time a particular array is indexed.

As for execution time, we have specified it for most short routines. For longer routines we have given an approximate execution time. The execution time of pro-grams involving many branches will obviously depend on which path the computer follows in a particular case. This is further complicated for the Z80 because condi-tionaljump instructions themselves require different numbers of clock cycles depend-ing on whether the branch is taken. Thus, a precise execution time is often impossible to define. The documentation always contains at least one typical example showing an approximate or maximum execution time.

Although we have drawn examples from CP / M-based systems, we have not made our routines compatible with the 8080 or 8085 processors. Readers who need routines that can run on any of these processors should refer to the 8080/8085 version of this book. We have considered the Z80 as an independent processor and have taken advantage of such features as block moves, block compares, loop control instructions, and relative jumps.

Our philosophy on error indicators and special cases has been the following:

l. Routines should provide an easily tested indicator (such as the Carry flag) of whether any errors or exceptions have occurred.

2. Trivial cases, such as no elements in an array or strings of zero length, should result in immediate exits with minimal effect on the underlying data.

3. Incorrectly specified data (such as a maximum string length of zero or an index beyond the end of an array) should result in immediate exits with minimal effect on the underlying data.

4. The documentation should include a summary of errors and exceptions (under the heading of "Special Cases").

5. Exceptions that may actually be convenient for the user (such as deleting more characters than could possibly be left in a string rather than counting the precise number) should be handled in a reasonable way, but should still be indicated as errors.

Obviously, no method of handling errors or exceptions can ever be completely consistent or well-suited to all applications. And rather than assume that the user will

INTRODUCTION TO THE PROGRAM SECTION

163

always provide data in the proper form, we believe a reasonable set of subroutines must deal with this issue.

The subroutines are listed as follows:

Im Dokument Assembly Language Subroutines (Seite 171-176)