EXOS 2.1 Kernel Specification

7. Device Drivers

A device driver consists of a set of routines, one to implement each of the fourteen entry points contained in the entry point table which was described in the previous chapter. This chapter describes the functions which must be provided by each of these routines, including details of register usage.


7.1 Device Driver Routines - General

Of the fourteen device driver entry points, eleven of them match up directly with EXOS function codes 1 to 11. Whenever the user makes one of these EXOS calls, EXOS will find out which device is the correct one for this channel and call the appropriate entry point of that device driver. These calls are referred to as the device channel calls.

The three remaining device driver entry points are, for initialisation, interrupt service and channel buffer moving. Calls to these three routines are originated from within the EXOS kernel at appropriate times and each is discussed in detail below.

Whenever a device driver routine is entered, the segment containing the entry point will be paged into Z80­page 3. Page 2 will always contain the system segment (segment 0FFh), and page 0 will of course contain the page zero segment. In the case of the device channel calls, the segment containing the channel descriptor and channel RAM (see later) will be in page 1, for other calls the contents of page 1 will be undefined. The stack pointer will be set to the system stack, in Z80­page 2, and there will be at least 100 bytes available on the stack, in addition to that needed for interrupt servicing (only 50 bytes for an interrupt routine).

When a device driver is called, register IY will always contain the adress of the DD_TYPE field of the device descriptor, in Z80­page 2. In the case of extension devices (linked in from extension ROMs), this can be used to access the device RAM which is allocated immediately below the device descriptor in the segment. A user device may sometimes have to access RAM relative to its device descriptor, which will not be in the system segment, so it will have to page the correct segment in (remembering to disable interrupts temporarily since the stack will be paged out). To enable a user device to do this, the segment number of the segment containing its device descriptor is passed in register B' whenever the device is called.

Device driver routines can corrupt all registers, including the index registers and the alternate register set, since they will have been saved by EXOS. The device driver can also corrupt the contents of Z80­page 1 with impunity, but should exercise caution with the other Z80­pages. Generally registers A, BC and DE are used to pass parameters to and return results from the routines.


7.2 Device Initialisation Routine

The device initialisation routine is passed no parameters (other than the segment and address of the channel descriptor in B' and IY), and returns no results. It is called when the device is first linked into the system, and again whenever a reset EXOS function call is made, which occurs at a warm reset or when a new applications program takes control.

Any channels which the device may have open will vanish when this routine is called, and so any variables or data areas which the device may keep must be reset. Note that any RAM segments allocated to the device will not be freed, so the device must remember that it still has these after subsequent initialisations.


7.3 Channel RAM Allocation

Every channel which is open has an area of channel RAM allocated to it. It is the job of the open channel or create channel routines (described below) to make an allocate buffer EXOS call to obtain the required amount of RAM. The allocate buffer EXOS call itself is described in section 11.25. This function call must be made before the open or create channel routine returns to EXOS, even if zero bytes of channel RAM are required, since it also sets up a channel descriptor for the channel.

When the allocate buffer call is made, it will return the address of the channel RAM in register IX. This will be in Z80­page 1 and the correct segment will be in page 1. Whenever the device driver is entered in future with a channel call to this channel, page 1 and register IX will be set up correctly. if n bytes of channel RAM are allocated then they can be accessed at addresses:

IX-1, IX-2, ···, IX-n

The 16 bytes of RAM immediately above the channel RAM (IX+0 ·· IX+15) contain a channel descriptor. This contains system information about the channel and should not be modified by the device.

In the case of non­video devices, the channel RAM will all be in one segment. In the case of video devices however, only a certain amount of the RAM, specified by the device and starting at IX-1, will definitely be in one segment, the rest may carry on down into other segments. If this is the case then each new segment will have a segment number one less than the previous one and they will all be video segments (0FCh to 0FFh). This allows a video device to obtain sufficient RAM for a large video page. Normally only the built­in video driver will be a video device, although any device can make itself one simply by having a bit set in its device descriptor (see section 6.2).

Once allocated the channel RAM can be moved by EXOS. This can only occur when another channel is opened or closed, or a user device linked in. Since devices are not allowed to make any of these EXOS calls, it is impossible for the channel RAM to be moved while the device driver is executing. Whenever the channel RAM is moved the buffer moved entry point of the device driver will be called. This entry point is described below.


7.4 The Buffer Moved Routine

The buffer moved entry point is called by EXOS immediately after it has moved a channel buffer of this device. This routine returns no result but is passed the following parameters:

  B', IY = Device desriptor segment & address (as usual)
      IX = New address of channel descriptor, will be paged
           into Z80-page 1
       A = Channel number of channel buffer moved
      BC = Amount that channel buffer has moved

The channel buffer may have been moved into a different segment. If the device needs to know this then it can read the new segment number from the page 1 register. The distance moved parameter in register BC is strictly speaking a signed 17­bit number, with the sign bit missing. This means that if, for example, a value of 1 is passed in BC, then this could mean that the buffer has been moved either up by 1 byte, or down by 65535 bytes. In practice this difference does not matter since it only affects the new segment number and this can be determined seperately.

Whenever the buffer moved entry point is called, interrupts will be disabled and should not be re­enabled by the device driver. This is to ensure that the device's interrupt routine cannot be called while it is in an intermediate state.


7.5 Device Interrupt Routines

EXOS can handle interrupts from any of the four possible sources on the Enterprise computer (video, sound, 1Hz and external). When an interrupt occurs, EXOS examines the Dave chip to determine which source it came from. It then scans through the device chain calling the interrupt entry point of any device which has requested servicing of this type of interrupt (by setting a bit in DD_IRQFLAG in its device descriptor - see section 6.2). When all devices have been called, the interrupt is cleared in the Dave chip, all registers and paging restored and EXOS returns to the interrupted program.

Interrupts are allowed at any time, including while executing device driver code, except while certain system variables are being updated or channel buffers are being moved. Also, interrupts are disabled while servicing an earlier interrupt, so there is no nesting of interrupts. If an interrupt from another source occurs while already servicing an interrupt then it will be held up until servicing of the first one is complete. Thus no interrupts should be missed but they may be serviced late.

The interrupt entry point of a device driver is optional, it is only required if the DD_IRQFLAG field of the device descriptor is non­zero. When a device is linked in, EXOS will ensure that any sources of interrupts which the device wants to service are enabled in the Dave chip.

The device's interrupt routine will be entered just like any other entry point, with registers B' and IY set up to the device descriptor segment and address as usual. No results are returned from the interrupt routine and all registers can be corrupted. (AF, BC, DE, HL, IX, IY, AF', BC', DE', HL'). The entry point will be called with interrupts disabled and they should not be re­enabled, neither should the device attempt to reset the interrupt flag in the Dave chip - EXOS does that.

There is an EXOS variable (see chapter 8) called IRQ_ENABLE_STATE which defines which of the four sources of interrupts are currently enabled. Any of them can be enabled or disabled by changing this EXOS variable and writing it out to the interrupt enable register in the Dave chip. This should be done with care since the keyboard will not be scanned if video interrupts are disabled so it can be difficult to recover from this.


7.6 Device Channel Calls

The device channel calls are the device entry points which correspond with EXOS function codes 1 to 11. Full details of these EXOS calls can be found in chapter 11. This section describes them only from the device's point of view.

All of these routines have certain parameters and results in common. These are:

Parameters:
 B', IY = Device descriptor segment and address
     IX = Pointer to channel RAM in Z80-page 1
      A = Channel number +1 (see next paragraph)
BC & DE = General parameters to routine          
Results:
      A = Status code, returned to user
BC & DE = General results from routine           

The channel number parameter passed to the device routine is one greater than the channel number as specified by the user. This due to the way in which EXOS handles channel numbers internally, and means that a device can never be passed a channel number of zero.

The device driver does not need to return with the status register set depending on the value returned in A. The setting of flags is done by EXOS before returning to the user.

7.6.1 Open Channel and Create Channel Routines

For most devices the open channel and create channel routines can be the same. The difference is only relevant for file handling devices, where "open" is intended to open an existing file and "create" is intended to create a new one.

The routine will be passed a pointer to a filename string in DE (length byte first). This will have been copied from the string passed by the user, into a buffer in the system segment, and will have been uppercased and checked for syntax and length (max 128 characters). If no filename was specified by the user this will be the null string.

The unit number specified by the user (or a default) will be passed in register C. Unit numbers are explained in section 11.1.

Assuming that the device decides that it will accept the open channel call, it must make an allocate buffer call to setup the channel descriptor and obtain any channel RAM which it may need for this channel. Details of this call can be found in section 11.25. This function call will return a pointer to the RAM in IX and page it into page 1. This is the only case of an EXOS call corrupting any unusual registers or the paging.

7.6.2 Block Read and Write Routines

All devices must provide a block read and a block write routine, wich are capable of reading or writing up to 65535 bytes. Some devices (such as disk) will implement these intelligently, doing data transfers directly into the user's buffer. However most devices simply do repeated calls to their own character read or write routines, copying the bytes into or out of the buffer.

Special care must be taken with accessing the user's buffer area. The buffer pointer is passed in DE straight from the user's call. This may point to any address in any of the four Z80 pages, and refers to the segment which was in that page when the user called EXOS, not when EXOS called the device driver routine. The device driver will therefore have to translate this address to one in Z80­page 1, and page in the correct segment in order to access the buffer, but must not forget the segment with its channel RAM in. In order to determine the segment number, four variables are provided in the system segment which define the four segments which where paged in when the EXOS call was made. These are called USR_P0, USR_P1, USR_P2 and USR_P3. Their addresses were given in section 5.4. These variables are handled re­entrantly, so they will survive nested EXOS calls correctly.

Note also that the user's buffer can cross a segment boundary and so the segment may need to be changed, and the address adjusted several times. Also the device should cope correctly with a block size of zero bytes, simply returning a zero status code without doing anything.

If an error occurs part way through a block read or write the registers DE and BC should be returned with their values correctly adjusted to indicate how much has been read or written.


Next Chapter: EXOS Variables



David Bouman. (dsbouma@cs.vu.nl)