Appendix V Index Appendix VII

Appendix VI

CP/M Plus on the Spectrum +3

This appendix describes the CP/M Plus implementation on the Spectrum +3, with particular reference on how to use the extensive facilities available in the BIOS extended jumpblock. This is not a tutorial, the reader should have some knowledge of CP/M Plus and its structure.

Note: The term "bank" will only be used in the CP/M Plus sense in which bank 0 is the BDOS bank, bank 1 is the TPA bank etc.

Hexadecimal numbers are preceded by a #. All other numbers are in decimal.

Features

VI.1 Character Devices

Three character devices are supported:

Device 0 - CRT
Input/output device. Input is detched from the keyboard, output is sent to the screen via a terminal emulator.
Device 1 - LPT
Output device. The built-in 8 bit Centronics port.
Device 2 - SIO
Input/output serial device with software settable baud rate. RS232 serial interface.

Device 0 - CRT - Keyboard

The keyboard can be in one of four shift states for each of which there is a separate user programmable translation table. These shifts are:
normalwhich produces lower case letters
CAPSwhich produces upper case letters
SYMBwhich produces the symbol characters marked on many keys.
EXTENDwhich produces "control" values in the range 0..31. Note that CAPS+SYMB is exactly the same as EXTEND.

There is a software settable "Num Lock". If num lock is set then all the translations tables are scanned for a "numeric" character "0123456789+-.," otherwise the key is translated according to the current shift state. The tables are scanned in the order normal, Caps, Symb, Extend.

There is also a software settable "Shift Lock", which if asserted has exactly the same effect as if the CAPS SHIFT key was permanently held down.

There is also a CAPS LOCK key, next to the lefthand CAPS SHIFT key. If this has been toggled on, characters in the range #61..#7A (a..z) and #E0 .. #FB (accented lower case letters) are upshifted.

Note that the Shift Lock and Num Lock are software settable in that they can only be set by an application program which uses KM KT PUT. They are included mainly for compatibility with other CP/M Plus implementations.

Device 0 - CRT - Terminal Emulator

The CRT device drives the Spectrum screen via a terminal emulator. This provides a machine independent screen environment which can be preserved across a range of machines and should facilitate the installation of standard application programs.

The emulator does not attempt to simulate precisely a particular terminal but is similar to many. The emulator is almost the same as that implemented by Digital Research for their 16bit operating systems.

The emulator is highly compatible with the terminal emulators within the CP/M Plus systems implemented on the CPC6128 and PCW machines. Details of the terminal's character handling and of the screen control codes are given in Appendix III.

Viewport

The terminal emulator restricts its operation to within a viewport. The viewport may be all, or part, of the screen aligned on character boundaries. The size and shape of the viewport is changed by the ESC 3 or ESC X sequences or by selecting 24 x 80 mode or by enabling or disabling the status line. There is only one viewport. Character cells to the left and right of the viewport are affected by scrolling the viewport. Character cells above and below the viewport are not affected.

Colours

The Spectrum +3 has eight colours corresponding to the eight possible combinations in which the three colour "guns" (Red, Green and Blue) can be on or off. It also allows an 8x8 pixel area of the screen to be bright (high intensity) or flash (alternate foreground and background colours).

ESC b and ESC c require a colour parameter. This has #20 subtracted and then is masked with #3F. The resulting value is treated as three 2 bit numbers each specifying the intensity of one of the three primary colours. Bits 0,1 for blue, bits 2,3 for red and bits 4,5 for green. The bit value is then treated as follows:
0gun off
1 or 2gun on, normal intensity
3gun on, high intensity provided both foreground and background are high intensity!

Colours can also (via TE SET INK) be set to flash. Only if both foreground and background colours are set to two different values will flashing actually take place.

Note that the Spectrum hardware restricts colour information so that it has to apply to a whole 8x8 pixel area. These areas only correspond exactly to screen characters in the 32 column mode.

Language dependent character sets

ESC 2 requires a language parameter. This is masked to give a value in the range 0..7. The screen character set is then altered so as to be suitable for the particular language chosen by swapping character matrices within the normal ASCII range (#20..#7F) with characters in the extended range (#A0..#FF).

24 x 80 mode

Some application programs may require a "standard" 24 x 80 screen. Sending ESC x will enable 24 x 80 mode regardless of the actual size of the screen. ESC y will restore the screen to its normal size, ie. 24 x 51. Both ESC x and ESC y will clear the entire screen. Turning 24 x 80 on and off has no effect on whether the status line is enabled.

Page mode

The screen may be put into "Page Mode". This affects the action of vertical movement commands. These no longer scroll the whole screen up and down, but instead "wrap around" at the top and bottom of the screen. This can be useful when it would take too long to scroll the whole screen - such as is often the case in "Comms" programs.

In Page mode the following commands are respecified:

Page mode has no effect on cursoring up and down, direct cursor movements or clearing areas of the screen.

Cursor

To prevent unsightly cursor flashing whilst outputting text to the screen the cursor is not turned on until 1/10 second after the last character was written. If the system is in 80 column mode when the cursor is turned on then the half of the screen which contains the cursor is displayed - unless this action has been disabled (using ESC 5).

Status Line

The bottom line of the screen may be used by the BIOS as a status line. On it are displayed disc error messages and, on single drive systems, instructions to change the disc and whether the drive is A: or B:. The status line can be disabled in which case the CRT device may use the bottom line and BIOS messages are displayed on the CONOUT: device.

If CONOUT: is not attached to the CRT device then status messages are sent to the CONOUT: message channel, and will not appear on the status line. (See Section VI.5 for details).

In 24x80 mode, messages will appear on the bottom line provided the status line is enabled.

Device 1 - LPT - Centronics port

A standard 8 bit Centronics interface (without an ACK signal).

Device 2 - SIO - Serial Interface

The device SIO drives the built-in serial channel. Because of the primitive nature of the serial interface it is not recommended that the input be driven in anything other than handshake mode. Even then, problems may occur with some devices if they are tardy in noticing changes in control signals.

The following parameters can be set by software:

Parity: none, odd, even
Number of stop bits: 1, 2
Number of receiver data bits: 5, 6, 7, 8
Number of transmitter data bits: 5, 6, 7, 8

Note that setting 1.5 stop bits is treated as if 2 were requested.

Note that it is not possible to set 8 data bits plus parity.

Baud rates

The receiver and transmitter baud rates can be set independently.

CP/M Plus expresses baud rates as a number in the range 1..15, the encoded baud rate. The table overleaf gives the encoded baud rate, and the baud rate.
150Note: this speed is not implemented
275
3110
4134.5102400
5150 113600
6300 124800
7600 137200
81200 149600
91800 1519200

The software wait loop is some 6.5 microseconds long - giving a maximum theoretical error of 6% at 9600 baud. Thus slower speeds are more likely to work than faster speeds..

Control signal handshaking

The SIO can be operated in one of two modes, handshake or non-handshake.

Handshake mode.
Input:
If there is no character available raise DTR.
DTR is dropped when a character is read.
Output:
Waits for CTS high and then sends the character
Non-handshake mode.
DTR is always high.
Input:
Wait for character to be received.
Output:
Sends the character

Output Device Sinks

A problem arises when one attempts to output to a device which either does not exist or is permanently busy - the system could wait forever. This typically occurs if the user accidentally types control P on a system without a printer. To solve this if an output device fails to respond within 30 seconds (approx) then the following message is displayed:

	device not ready - Retry, Ignore or Cancel?

where device is the actual name of the device, LPT, SIO etc.

Typing R for retry will try the operation again. Typing I for ignore will throw the character away. Typing C for cancel will set up a sink by removing the offending physical device from all of the output logical devices (CONOUT: AUXOUT: and LST:).

Thus in the above case of accidently typing Control-P the user should respond to the message with C for cancel, all further output to the printer will be thrown away. When a printer is actually available the user can redirect output there by using DEVICE in the normal manner.

Timeouts can be permanently disabled by using the program TIMEOUT.COM. This uses the 'spare' BIT 7 of the character device table mode byte to record the current state.

VI.2 Disc Driver

The disc driver supports one or two floppy disc drives. Three disc formats are supported: "system format" and "data only format" as per CP/M 2.2 on other AMSTRAD machines and the PCW format. The PCW8256 format is actually a family of formats since the precise details of the format are recorded on the boot sector as the "disc specification". "Spectrum +3" discs are one member of the PCW family of formats. Full details of the PCW family of formats can be found in the Spectrum +3 User Guide.

The Spectrum +3 can only be booted from a Spectrum +3 format disc. Once booted any sort of acceptable disc can be used.

If a second drive is fitted then it can be either single or double track, single or double sided. However, DISCKIT assumes that the second drive will be a standard single sided, single track unit.

Many of the disc driver routines are available to an application program via the extended BIOS jumpblock; for example the DISCKIT utility program has to use these routines since there is no standard CP/M Plus facility for formatting discs.

Single drive systems

On single drive systems two "logical" drives (A: and B:) are both mapped on to the one physical drive. The number of physical drives fitted is detected during cold boot.

The BIOS routine SELDSK deals with the logical to physical drive mapping. Whenever it is called it checks that the logical drive being selected is currently mapped onto the physical drive. If not a BIOS message is displayed to prompt the user to change the disc.

This enables disc to disc copying using a single drive with standard utilities such as PIP.

The current assignment of logical to physical is displayed on the status line (provided it is enabled) as "Drive is A:" or "Drive is B:".

The disc driver routine interfaces refer to the drives as "unit 0" and "unit 1". This terminology is introduced for reasons of upwards compatability with other products which may have to make a clear distinction between physical drives (units) and logical drives (drives A: B: C: ... P:).

Logical tracks and Sectors

The disc driver routines require "logical" tracks and sectors. These are used to hide information concerning the number of sides and the actual sector numbers from CP/M Plus, which knows nothing about them.

Logical track numbers on a single sided disc are the same as physical track numbers.

For double sided discs two options are available:
Flip sides side 0 track 0 = logical track0
side 1 track 0 1
side 0 track 1 2
side 1 track 1 3
...
side 1 last track
Up and overside 0 track 0 = logical track0
side 0 track 1 1
...
side 0 last track
side 1 last track
side 1 last track - 1
...
side track 0

Logical sectors hide the actual physical sector numbers. Logical sector numbers always start from 0.

Logical sector = physical sector - first physical sector.

BIOS disc error messages are in terms of logical track and sector numbers. All this information is held in an eXtended Disc Parameter Block.

Extended Disc Parameter Blocks (XDPB)

Associated with each (logical) drive is an extended disc parameter block. This contains a standard DPB as required by CP/M Plus and information required by the BIOS to support the different formats. It may be patched in order to use different format discs provided that the restrictions below are obeyed.

The structure of an XDPB is identical to that used for +3DOS, and is documented in the Spectrum +3 User Guide.

To find the XDPB for a particular drive use BDOS function 31.

The restriction on patching an XDPB is that the resulting disc structure must lie within the following maximum sizes:
Maximum 2 bit allocation vector = 91 bytes
Maximum checksum vector = 64 bytes
Maximum hash table size = 1024 bytes
Maximum sector size = 512 bytes

This corresponds to a disc of 160 tracks, 9 sectors per track, 512 bytes per sector, 1 reserved track, 256 directory entries, 2K block size. This is the recommended structure for a double sided, double track, double density disc.

Swapping discs between CP/M Plus and CP/M 2.2

CP/M 2.2 and AMSDOS system format and data only format discs from the CPC6128 can be used under CP/M Plus with no further ado.

CP/M Plus discs and be freely used under CP/M 2.2 or AMSDOS provided that:

This is the normal state of a CP/M Plus disc, and will remain as such unless the user explicitly enables any of the above features using INITDIR.COM and/or SET.COM.

If any of the above exist then both CP/M 2.2 and AMSDOS will become confused as to the amount of free space on the disc. Files may still be read but it is recommended that such discs are not written to under CP/M 2.2 or AMSDOS.

INITDIR.COM is used to reformat the directory for time and date stamping. SET.COM is used to create disc labels and enable and create passwords.

VI.3 Memory organisation

The Spectrum's memory is organised into three banks:

Bank 0 is the BDOS bank which contains the banked portions of the BIOS (Basic Input Output System) and BDOS (Basic Disc Operating System). It also contains the screen memory, some disc buffers and the extended BIOS jumpblock.

Bank 1 is the TPA bank in which all application programs are run.

Bank 2 contains a copy of the CCP (Console Command Processor), screen memory, the disc hash tables and data buffers.

The top 16K of memory is common to all banks; it contains the resident portions of the BIOS and BDOS and part of the TPA.

Memory map

Bank 0 - BDOSBank 1 - TPABank 2 - Extra
#C000
#8000
#4000
#0000
block 3
common
 
block 6
BIOS,BDOS
 
block 5
screen
 
block 4
BIOS
extended jumpblock
 
block 3
common
 
block 2
 
 
block 1
 
 
block 0
 
 
 
block 3
common
 
block 7
CCP, hash tables
data buffers, screen

The character matrix RAM is at #3000 in bank 0. It remains visible at #3000 when bank 2 is selected.

The Restart Instructions and Locations

The BIOS uses the restart instructions and their locations as follows:
RST 0used by CP/M
RST 1..RST 5 not used - reserved
RST 6used by SID
RST 7used for interrupts

The SID program supplied has been modified to use RST6 instead of the usual RST7.

The Z80 Alternate and Index Registers

An application program may use the Z80 alternate and index instructions without further ado. All BDOS and standard BIOS calls preserve these registers. Extended BIOS routines preserve the alternate register set but may use the index registers for passing parameters. See the routine definitions for details.

Note that using Z80 only instructions and/or registers will prevent an application from running on an 8080 or 8085 based machine.

Cold Boot

The term "cold boot" means invoking CP/M for the first time after the machine has been switched on or reset.

The boot program loads the first file it finds with type .EMS, this file should contain the CP/M Plus system.

The .EMS file replaces the CPM3.SYS file which is usually found on CP/M Plus implementations. The .EMS file contains CPM3.SYS, CCP.COM together with a loader and the BIOS.

Warm Boot

The term "warm boot" means reloading CP/M after a transient program has run or after the use has typed Control-C in order to change a disc.

A warm boot is invoked immediately after initialisation and thereafter by a jump to location zero, or by calling the BDOS System Reset function. The primary function of the warm boot is to load CCP into memory and then to enter the CCP. A copy of the CCP is stored in bank 2 so a warm boot does not require any disc access.

The warm boot routine initialises the jumps at #0000 and #0005

Time and Date Clock

The CP/M Plus time and date clock in the System Control Block (SCB) is updated every second. Hardware limitations mean that the clock will not be found to be very accurate if the machine is used heavily, or the disc accessed a lot.

VI.4 The BIOS

BIOS Messages

All BIOS messages are displayed on the status line, if enabled, otherwise they are sent to the CONOUT: device.

Some BIOS messages are followed by the question Retry, Ignore or Cancel?. The system then discards any outstanding characters, turns on the cursor and waits for the user to type R, I or C; anything else will cause a bleep.

Typing R for retry causes the BIOS to repeat the operation.
Typing I for ignore causes the BIOS to continue as if the problem had not occurred.
Typing C for cancel causes the BIOS to abandon the operation. This will often result in a BDOS error message.
On other national systems, local translations of R, I and C are used.

USERF

In the standard CP/M Plus BIOS jumpblock function 30 "USERF" is reserved for the system implementor. On the Spectrum +3 this function is used for calling the extended BIOS routines in bank 0.

To find USERF fetch the contents of locations 1 and 2, this contains the address of function 1 "WBOOT" in the BIOS jumpblock. Add 87 to give the address of the JMP USERF entry. It is a good idea to copy the USERF jumpblock entry into a fixed location and then call this fixed location.

Note that neither USERF nor the rest of the BIOS jumpblock is at the same address as on the PCW machines.

USERF takes the address of the required routine in bank 0 as an inline parameter, ie. as the two bytes following the CALL instruction. The registers AF BC DE HL IX IY are all passed to the routine and returned back to the caller as set by the routine. The alternate register set is preserved throughout the call, it can neither be used to pass parameters nor to return results.

BIOS Jumpblocks

The BIOS has two jumpblocks: the standard CP/M Plus jumpblock and an extended jumpblock. The word at location #0001 contains the address of the WBOOT entry in the standard BIOS jumpblock, function 1. The extended jumpblock starts at #0080 in bank 0 and contains jumps for additional facilities such as physical sector reading and writing.

Since the extended BIOS jumpblock is in bank 0 whereas the user program is in bank 1 it is not possible for an application program to call routines in the extended jumpblock directly, instead the BIOS function USERF must be used.

This jumpblock is almost the same as those on the CPC6128 and PCW CP/M Plus implementations. Differences are noted where appropriate. It has the format:
Disc Driver
#0080JMP DD INIT ;initialise disc driver
#0083JMP DD SETUP ;set disc parameters
#0086JMP DD READ SECTOR ;read a sector
#0089JMP DD WRITE ;write a sector
#008CJMP DD CHECK SECTOR ;check a sector
#008FJMP DD FORMAT ;format a track
#0092JMP DD LOGIN ;login a disc
#0095JMP DD SEL FORMAT ;select a standard format
#0098JMP DD DRIVE STATUS ;fetch drive status
#009BJMP DD READ ID ;read a sector ID
#009EJMP DD L DPB ;initialise a DPB
#00A1JMP DD L XDPB ;initialise an XDPB
#00A4JMP DD L ON MOTOR ;turn motor on, wait for timeout
#00A7JMP DD L T OFF MOTOR;set motor off timeout
#00AAJMP DD OFF MOTOR ;turn motor off
#00ADJMP DD L READ ;read type µPD765A command
#00B0JMP DD L WRITE ;write type µPD765A command
#00B3JMP DD L SEEK ;seek command
SIO Driver
#00B6JMP CD SA INIT ;initialise SIO channel A
#00B9JMP CD SA BAUD ;set SIO channel A baud rates
#00BCJMP CD SA PARAMS ;fetch SIO channel A parameters
Terminal Emulator
#00BFJMP TE ASK ;where is the cursor, what screen size
#00C2JMP TE RESET ;re-initialise the screen
#00C5JMP TE STL ASK ;is the status line enabled?
#00C8JMP TE STL ON OFF ;enable/disable the status line
#00CBJMP TE SET INK ;set the colour(s) for an ink
#00CEJMP TE SET BORDER ;set the colour(s) for the border
#00D1JMP TE SET SPEED ;set the ink flash speed
Keyboard
#00D4JMP KM SET EXPAND ;set the ink flash speed
#00D7JMP KM SET KEY ;set entry(s) for key translation
#00DAJMP KM KT GET ;get a key token
#00DDJMP KM KT PUT ;put a key token
#00E0JMP KM SET SPEED ;set key repeat speed
Miscellaneous
#00E3JMP CD VERSION ;get version numbers
#00E6JMP CD INFO ;get BIOS system information
#00E9JMP SCR RUN ROUTINE ;run a routine in screen environment
#00ECJMP FIND FID ;find a FID

All other entries in the jumpblock are reserved and must not be used. They can differ markedly from one version to the next.

The action performed by each entry in the extended jumpblock is detailed below, along with the entry and exit conditions of each.

0 DD INIT

#0080

Initialise the disc driver.

Action:

Initialises the disc driver, resets all disc parameters to their default values. Turns the motor off.

Entry conditions:

No conditions.

Exit conditions:

AF BC DE HL corrupt
All other registers preserved.

Notes:

The default disc parameters are:
Motor on timeout 1 second
Motor off timeout 5 seconds
Write current off time1.75 milliseconds
Head settle time 30 milliseconds
Step rate 12 milliseconds
Head unload time 480 milliseconds
Head load time 4 millisecond
Non-DMA mode

Related entries:

DD SETUP

1 DD SETUP

#0083

Reset various disc parameters.

Action:

Sets the values for the motor on, motor off, write current off and head settle times. Sends a SPECIFY command to the floppy disc controller.

Entry conditions:

HL = address of parameter block in common memory
Format of the parameter block:
byte 0motor on timeout in 100 millisecond units
byte 1motor off timeout in 100 millisecond units
byte 2write current off time in 10 millisecond units
byte 3head settle time in 1 millisecond units
byte 4step rate time in 1 millisecond units
byte 5head unload delay 32..480 ms in 32 millisecond units
byte 6bits 7..1: head load dely 4...508ms in units of 4ms;
bit 0: non-DMA mode (as per µPD765A SPECIFY command).

Exit conditions:

AF BC DE HL corrupt
All other registers preserved.

Notes:

The values given are used for both drives. When using two differing drives use the slower of the two times.

A motor off time of zero will never turn the motor off.

The default values are:
Motor on timeout 10
Motor off timeout 50
Write current off time 175
Head settle time 30
Step rate 12
Head unload time 15
Head load time + non DMA3
Non-DMA mode

2 DD READ SECTOR

#0086

Read a sector from disc.

Action:

Reads a sector from disc into any bank.

Entry conditions:

B = CP/M bank
C = unit
D = logical track
E = logical sector
HL = address of destination buffer in the bank in register B
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A corrupt
If failed:
Carry false
A = reason
0 => drive not ready
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
8 => media changed
9 => unsuitable disc for drive
Always:
Other flags BC DE HL corrupt
All other registers preserved.

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

The "media changed" error means that the sector numbers on the disc are different from those specified in the XDPB.

Related entries:

DD WRITE SECTOR, DD CHECK SECTOR

3 DD WRITE SECTOR

#0089

Write a sector to disc.

Action:

Writes a sector to disc from any bank.

Entry conditions:

B = CP/M bank
C = unit
D = logical track
E = logical sector
HL = address of source buffer in the bank in register B
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A corrupt
If failed:
Carry false
A = reason
0 => drive not ready
1 => write protected
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
8 => media changed
9 => unsuitable disc for drive
Always:
Other flags BC DE HL corrupt
All other registers preserved.

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

The "media changed" error means that the sector numbers on the disc are different from those specified in the XDPB.

Related entries:

DD READ SECTOR, DD CHECK SECTOR

4 DD CHECK SECTOR

#008C

Check that a sector on disc is the same as one in memory.

Action:

Compares a sector on disc with a store copy.

Entry conditions:

B = CP/M bank
C = unit
D = logical track
E = logical sector
HL = address of source buffer in the bank in register B
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK and the sector on disc = sector in store:
Carry true, zero true
A corrupt
If OK and the sector on disc &x2260; sector in store:
Carry true, zero false
A corrupt
If failed:
Carry false
A = reason
0 => drive not ready
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
8 => media changed
Always:
Other flags BC DE HL corrupt
All other registers preserved.

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

The "media changed" error means that the sector numbers on the disc are different from those specified in the XDPB.

Related entries:

DD READ SECTOR, DD WRITE SECTOR

5 DD FORMAT

#008F

Format a track.

Action:

Formats a track using a header information buffer in any bank.

Entry conditions:

B = CP/M bank
C = unit
D = logical track
E = filler byte, usually #E5
HL = address of header information buffer in bank in register B
IX = address of XDPB in common memory (#C000..#FFFF)

Format of header information:

sector entry for first sector
sector entry for second sector
...
sector entry for last sector

Sector entry format:

byte 0: track number
byte 1: head number
byte 2: sector number
byte 3: log2(sector size) - 7 (ie. 0 &x21D2; 128; 1 &x21D2; 256, etc.)

Exit conditions:

If OK:
Carry true
A corrupt
If failed:
Carry false
A = reason
0 => drive not ready
1 => write protected
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
8 => media changed
9 => unsuitable disc for drive
Always:
Other flags BC DE HL corrupt
All other registers preserved.

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

The "media changed" error means that the sector numbers on the disc are different from those specified in the XDPB.

6 DD LOGIN

#0092

Login a disc.

Action:

Attempts to determine the format of a disc. If successful initialises an XDPB. Does not affect or consider the freeze flag.

Entry conditions:

C = unit
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A = disc type
0 => PCW format,type 0 (including "Spectrum+3" discs)
1 => system format
2 => data only format
other values as read from disc specification
DE = size of 2 bit allocation vector
HL = size of hash table
XDPB initialised
If failed:
Carry false
A = reason
0 => drive not ready
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
6 => bad format
9 => unsuitable disc for drive
DE HL corrupt
XDPB corrupt
Always:
Other flags BC corrupt
All other registers preserved.

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

Related entries:

DD SEL FORMAT

7 DD SEL FORMAT

#0095

Select a standard disc format.

Action:

Initialises an XDPB for the required format regardless of the actual disc format. Normally the BIOS automatically determines the format of a disc when it is logged in, but for programs such as disc formatters it is necessary to pre-set the format. Does not affect or consider the freeze flag.

Entry conditions:

A = format required
0 => PCW format, type 0
1 => system format
2 => data only format
3 => PCW format, double sided, double track
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A = disc type
0 => PCW format,type 0 (including "Spectrum+3" discs)
1 => system format
2 => data only format
3 => PCW format, double sided, double track
DE = size of 2 bit allocation vector
HL = size of hash table
XDPB initialised
If failed:
Carry false
A = reason
6 => bad format
DE HL corrupt
XDPB corrupt
Always:
Other flags BC corrupt
All other registers preserved.

Notes:

This routine will only fail if an illegal disc type is given.

Related entries:

DD LOGIN

8 DD DRIVE STATUS

#0098

Fetch the drive status.

Action:

Fetch the drive status: ready, write protected etc.

Entry conditions:

C = bits 0,1: unit, bit 2: head

Exit conditions:

A = status
bit 7 : undefined
bit 6 : write protected
bit 5 : ready
bit 4 : track 0
bit 3 : undefined
bit 2 : head
bit 1,0 : unit
F HL corrupt
All other registers preserved.

Notes:

For unit 1 if bit 6 = 0 and bit 5 = 0 then the drive is not fitted.

9 DD READ ID

#009B

Read a sector ID.

Action:

Reads the ID from the first sector found.

Entry conditions:

C = unit
D = logical track
IX = address of XDPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A = sector number from ID
If failed:
Carry false
A = reason
0 => drive not ready
2 => seek fail
3 => data error
4 => no data
5 => missing address mark
Always:
HL = address of results buffer in common memory
Format of results buffers
byte 0 : number of results bytes received
bytes 1.. : results
F BC DE corrupt
All other registers preserved

Notes:

In the event of errors this routine trys and retrys a total of 15 times as follows:

    T T T R T T T T I T T T T R T T T T

where T means try, R realign, I move to innermost track.

10 DD L DPB

#009E

Initialise a standard DPB.

Action:

This routine initialises a standard CP/M Plus Disc Parameter Block for a given disc format.

Entry conditions:

HL = address of source disc specification in common memory (#C000..#FFFF)
IX = address of destination DPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A = disc type
If failed:
Carry false
A = reason
6 => bad format
Always:
Other flags BC DE HL corrupt
All other registers preserved

Notes:

The disc format is specified as follows:

Byte 0: disc type
Byte 1: sidedness
bits 0-6: arrangement
0 => single sided
1 => double sided, flip sides
2 => double sided, up and over
bit 7: double-track
Byte 2: number of tracks per side
Byte 3: number of sectors per track
Byte 4: Log2(sector size) - 7
Byte 5: number of reserved tracks
Byte 6: Log2(block size) - 7
Byte 7: number of directory blocks
Byte 8: gap length (read/write)
Byte 9: gap length(format)

Related entries:

DD L XDPB

11 DD L XDPB

#00A1

Initialise an XDPB.

Action:

This routine initialises an eXtended Disc Parameter Block for a given disc format.

Entry conditions:

HL = address of source disc specification in common memory (#C000..#FFFF)
IX = address of destination DPB in common memory (#C000..#FFFF)

Exit conditions:

If OK:
Carry true
A = disc type
If failed:
Carry false
A = reason
6 => bad format
Always:
Other flags BC DE HL corrupt
All other registers preserved

Notes:

See DD L DPB for a description of the disc format specification.

Related entries:

DD L DDPB

12 DD L ON MOTOR

#00A4

Turn the motor on.

Action:

If the motor is off then turn it on, wait for the motor on timeout.

Entry conditions:

No conditions

Exit conditions:

All registers and flags preserved.

Related entries:

DD L T OFF MOTOR
DD L OFF MOTOR

13 DD L T OFF MOTOR

#00A7

Start the motor off timeout.

Action:

Starts the motor off timeout, after which the motor will be turned off. Returns immediately, does not wait for the timeout.

Entry conditions:

No conditions

Exit conditions:

All registers and flags preserved.

Related entries:

DD L ON MOTOR
DD L OFF MOTOR

14 DD L ON MOTOR

#00AA

Turn the motor off.

Action:

Turns the motor off, kills the motor timeout if any.

Entry conditions:

No conditions

Exit conditions:

AF BC DE HL corrupt
All other registers preserved.

Related entries:

DD L ON MOTOR
DD L T OFF MOTOR

15 DD L READ

#00AD

µPD765A read type command driver.

Action:

Low level interface for the µPD765A floppy disc driver. Use for "READ DATA", "READ DELETED DATA", "READ A TRACK".

Sends required commands to the µPD765A, deals with bank switching, fetches results.

Motor must be running.

Entry conditions:

HL = address of parameter block in common memory (#C000..#FFFF)
Format of parameter block:
byte 0 : CP/M bank
byte 1,2 : address of buffer
byte 3,4 : number of bytes to transfer
byte 5 : number of µPD765A command bytes
byte 6.. : command bytes

Exit conditions:

HL = address of results buffer in common memory
Format of results buffer
byte 0 : number of results bytes received
bytes 1..: results
AF BC DE corrupt
All other registers preserved

Notes:

Detailed knowledge of the µPD765A is required in order to use this routine.

Related entries:

DD L WRITE

16 DD L WRITE

#00B0

µPD765A write type command driver.

Action:

Low level interface for the µPD765A floppy disc driver. Use for "WRITE DATA", "WRITE DELETED DATA", "FORMAT A TRACK", "SCAN EQUAL", "SCAN LOW OR EQUAL", "SCAN HIGH OR EQUAL".

Sends required commands to the µPD765A, deals with bank switching, fetches results.

Motor must be running.

Entry conditions:

HL = address of parameter block in common memory (#C000..#FFFF)
Format of parameter block:
byte 0 : CP/M bank
byte 1,2 : address of buffer
byte 3,4 : number of bytes to transfer
byte 5 : number of µPD765A command bytes
byte 6.. : command bytes

Exit conditions:

HL = address of results buffer in common memory
Format of results buffer
byte 0 : number of results bytes received
bytes 1..: results
AF BC DE corrupt
All other registers preserved

Notes:

Detailed knowledge of the µPD765A is required in order to use this routine.

Related entries:

DD L READ

17 DD L SEEK

#00B3

Seek to required track.

Action:

Realigns if required, seeks to given track.

Motor must be running.

Entry conditions:

C = bits 0,1: unit, bit 2: head
D = physical track
IX = address of XDPB in common memory

Exit conditions:

If OK:
Carry true
A corrupt
If failed:
Carry false
A = reason
0 => drive not ready
2 => seek fail
Always:
Other flags B corrupt
All other registers preserved

Notes:

Will try 10 times to seek or realign before returning an error.

This routine is not normally required since "DD READ SECTOR", "DD WRITE SECTOR" "DD CHECK SECTOR" and "DD FORMAT" all perform their own seeks.

18 CD SA INIT

#00B6

Initialise SIO

Action:

Initialise SIO channel A.

Entry conditions:

A = mode
#00no handshake
#FFhandshake
#FE"interrupt", no handshake (treated as #FE)
#FD"interrupt", handshake (treated as #FF)
or
A = command
#80drop DTR
#7Fraise DTR
#7E"drop RTS" - ignored
#7D"raise RTS" - ignored
D = number of stop bits (not required for commands)
0 => 1
1 => 1.5 (treated as if it were 2)
2 => 2
E = parity (not required for commands)
0 => none
1 => odd
2 => even
H = number of receiver data bits 5, 6, 7 or 8 (not required for commands)
L = number of transmitter data bits 5, 6, 7 or 8 (not required for commands)

Exit conditions:

AF BC DE HL corrupt
All other registers preserved.

Notes:

The parameters are not validated, silly values will give silly results.

It is not possible to select a mode with 8 data bits and odd or even parity as well.

19 CD SA BAUD

#00B9

Set the baud rates for the SIO.

Action:

Sets the receiver and transmitter baud rates for SIO.

Entry conditions:

H = encoded receiver baud rate
L = encoded transmitter baud rate

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

Notes:

The parameters are not validated, silly values will give silly results.

The encoded baud rates are given in Section VI.1.

Does not affect the CP/M Plus character I/O table.

20 CD SA PARAMS

#00BC

Get the current parameters for the SIO

Action:

Fetches the current mode, parity, baud rates etc for SIO.

Entry conditions:

No conditions

Exit conditions:

A = mode
#00 => no handshake
#FF => handshake
B = receiver encoded baud rate
C = transmitter encoded baud rate
D = stop bits
0 => 1
2 => 2
E = parity
0 => none
1 => odd
2 => even
H = receiver data bits 5, 6, 7 or 8
L = transmitter data bits 5, 6, 7 or 8
All other registers preserved

Notes:

The values returned reflect current settings, which may not be quite the same as those requested!

21 TE ASK

#00BF

Get the current viewport position and size and cursor position.

Action:

Fetches the current viewport position and size and cursor position.

Entry conditions:

No conditions.

Exit conditions:

B = top row of the viewport in physical screen coordinates
C = left column of the viewport in physical screen coordinates
D = height of viewport - 1
E = width of viewport - 1
H = cursor row in viewport coordinates
L = cursor column in viewport coordinates
All other registers preserved

Notes:

The top row is row 0, the left column is column 0.

22 TE RESET

#00C2

Re-initialise the terminal emulator.

Action:

Re-initialise the terminal emulator: clears the screen, homes and enables the cursor. For use by programs which have written to the screen by means other than using the CRT device.

Entry conditions:

No conditions.

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

23 TE STL ASK

#00C5

Is the status line enabled?

Action:

Asks if the status line is enabled. It will be enabled if it is not disabled and the screen is not in 24x80 mode.

Entry conditions:

No conditions.

Exit conditions:

If enabled and not in 24 x 80 mode:
Zero false
If disabled or 24 x 80 mode:
Zero true
Always:
Carry false
A = True enable/disable state
#00 &x21D2; Disabled
#FF &x21D2; Enabled
All other registers preserved

Related entries:

TE STL ON OFF

24 TE STL ON OFF

#00C8

Enable / disable the status line.

Action:

Enables or disables the status line. Disabling the status line gives an extra line to the terminal emulator. When disabled status line messages are sent to the CONOUT: device.

Entry conditions:

A = enable/disable
#00 => disable
#FF => enable

Exit conditions:

F BC DE HL corrupt
All other registers preserved

Related entries:

TE STL ASK

25 TE SET INK

#00CB

Set the colours in which to display an ink

Action:

Set which colour will be used to display an ink. If the flashing parameter in C is the same value as the colour in B then the ink will remain a steady colour. If the value in C is different then the ink may alternate between the foreground and background colours.

Entry conditions:

A contains an ink number
B contains the colour
C contains a flashing parameter

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

Notes:

Ink 0 is used for the background (which includes the border); ink 1 is used for the foreground. Inks > 1 are ignored.

The colour is masked with #3F and the resulting value is treated as three 2 bit numbers each specifying the intensity of one of the three primary colours. Bits 0,1 for blue, bits 2,3 for red and bits 4,5 for green. This bit value is then interpreted as follows:

00
colour is off
01 or 10
colour is on
11
colour is on. Ink is high intensity.

Note that the colour values given in Appendix III have had #20 added to them, whereas the values for this routine should not.

The value in C is masked with #3F before comparing it with B for equality. If the values differ, the ink is flashing.

If both inks 0 and 1 are high intensity then the colours will be set to be bright (over the whole 8x8 pixel cell). If both inks 0 and 1 are flashing then the colour will be set to flash (which makes the whole cell alternate between foreground and background colours).

Setting the colour of an ink does not affect the colour of the screen. It affects how characters will be written in the future.

26 TE SET BORDER

#00CE

Set the colour of the screen border

Action:

Set which colour will be used to display the border.

Entry conditions:

B contains the colour

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

Notes:

The border can also be changed by using TE SET INK to set ink 0.

The colour parameter is interpreted as described above for TE SET INK, except that it cannot be set bright or flashing.

27 TE SET SPEED

#00D1

Set the flash period.

Action:

This routine has no effect on a Spectrum +3 because all flashing is done by hardware.

Entry conditions:

No conditions

Exit conditions:

AF HL corrupt
All other registers preserved

28 KM SET EXPAND

#00D4

Set an expansion string

Action:

Set the expansion string associated with an expansion token.

Entry conditions:

B contains the expansion token for the expansion to set
C contains the length of the string
HL contains the address of the string in common memory (#C000..#FFFF)

Exit conditions:

If the expansion is OK:
Carry true
If the string was too long or the token was invalid:
Carry false
Always:
Other flags A BC DE HL corrupt
All other registers preserved.

Notes:

The characters in the string are not expanded (or otherwise dealt with). It is therefore possible to put any character into an expansion string.

If there is insufficient room in the expansion buffer for the new string then no change is made to the expansions.

If the string is currently being used to generate characters then the unread portion of the string is discarded. The next character will be read from the key buffer.

29 KM SET KEY

#00D7

Set entry(s) in key translation table(s)

Action:

Set what character or token a key will be translated to when Caps, Symb, Extend or none of these is pressed.

Entry conditions:

B contains the new translation
C contains a key number
D contains bit mask indicating which table(s) are to be changed
bit 0 => normal translation
bit 1 => CAPS translation
bit 2 => SYMB translation
bit 3 => EXTEND translation
bit 4 => should be set to 0
other bits ignored

Exit conditions:

AF and HL corrupt
All other registers preserved.

Notes:

If the key number is invalid then no action is taken.

Most values in the table are treated as characters and are passed back to the user. However, there are certain special values:

#80..#9E
are the expansion tokens and are expanded to character strings.
#9F
is the ignore token and means the key should be thrown away.

30 KM KT GET

#00DA

Get a key token.

Action:

Try to fetch a key token from the keyboard.

Entry conditions:

No conditions.

Exit conditions:

If got a token:
Carry true
C = key number
If not got a token:
Carry false
C corrupt
Always:
B = shift state
bit 0 => not defined
bit 1 => not defined
bit 2 => repeat
bit 3 => caps (upper case) lock
bit 4 => num lock
bit 5 => CAPS
bit 6 => CAPS shift lock
bit 7 => SYMB
Other flags A corrupt.
All other registers preserved.

Notes:

The shift state defines which locks and/or shift keys were pressed. The repeat bit indicates that the key was generated by a repeat.

31 KM KT PUT

#00DD

Put a key token.

Action:

Insert a key token into the keyboard buffer so that the next "KM KT GET" will fetch it.

Entry conditions:

B = shift state
bit 0 => not defined
bit 1 => Extend
bit 2 => repeat
bit 3 => caps (upper case) lock
bit 4 => num lock
bit 5 => CAPS
bit 6 => CAPS shift lock
bit 7 => SYMB
C = key number

Exit conditions:

All registers and flags preserved.

Notes:

More than one key token may be put, however, buffer overflow is not reported and will result in key tokens being lost. The buffer is at least 10 tokens long. If a great deal of text is to be generated by KM KT PUT the use of expansion tokens is recommended.

To change any of the lock states call "KM KT PUT" with the required state with an innocuous key number then call "KM KT GET" to remove it.

32 KM SET SPEED

#00E0

Set key start up delay and repeat speed.

Action:

Set the time before keys first repeat (start up delay) and the time between repeats (repeat speed).

Entry conditions:

H contains the new start up delay.
L contains the new repeat speed.

Exit conditions:

AF corrupt.
All other registers preserved.

Notes:

Both delays are given in scans of the keyboard. The keyboard is scanned every fiftieth of a second.

A start up delay or repeat speed of 0 is taken to mean 256.

The default start up delay is 30 scans (0.6 seconds) and the default repeat speed is 2 scans (0.04 seconds or 25 characters a second).

Note that a key is prevented from repeating (by the key scanner) if the key buffer is not empty. Thus the actual repeat speed is the slower of the supplied repeat speed and the rate at which characters are removed from the buffer. This is intended to prevent the user from getting too far ahead of a program that is running sluggishly.

The start up delay and repeat speed apply to all keys on the keyboard that are set to repeat.

33 CD VERSION

#00E3

Get version numbers.

Action:

Fetches machine type, BIOS version numbers and machine specific version numbers.

Entry conditions:

No conditions.

Exit conditions:

A = machine
0 => CPC6128
1 => PCW8256, 8512 and 9512
3 => Spectrum +3
B = BIOS major version number
C = BIOS minor version number
DE reserved
HL = machine specific version number
CPC6128 => ROM number
other machines, not defined.
F corrupt.
All other registers preserved.

34 CD INFO

#00E3

Get BIOS system information.

Action:

Fetches BIOS system information, number of disc drivers, address of buffer table, number of memory blocks etc.

Entry conditions:

No conditions.

Exit conditions:

A = number of disc drives
#00 => 1 disc drive
#FF => 2 disc drives
B = number of memory blocks
C = serial interface status
#00 => not fitted
#FF => fitted
HL = address of buffer table in common memory (#C000..#FFFF)
Format of buffer table
entry for buffer area 0
entry for buffer area 1
...
#FF
Entry format:
byte 0: CP/M bank
byte 1,2: start address
byte 3,4: size in bytes
F, DE corrupt.
All other registers preserved

Notes:

The buffer table gives details of where the data and directory buffer areas are.

35 SCR RUN ROUTINE

#00E9

Runs a routine in the screen environment

Entry conditions:

BC = address of routine to call, in common memory (#C000..#FFFF)
DE HL IX IY as required by routine

Exit conditions:

AF DE HL IX IY as returned by called routine
BC corrupt
All other registers preserved

Notes:

Ths routine is provided for compatability with the PCW implementation of CP/M Plus. This routine is not implemented on the CPC6128. The main screen exists in CP/M bank 0, and the second screen (used for the right hand side of the 80 column mode) exists in CP/M bank 2. It is therefore possible to access them using standard bank switching routines.

The screen is laid out in the normal Spectrum way, except that both screens live at address #4000 in their respective banks, with their attribute RAM at #5B00.

The eight bit character matrices used for the 32 column mode can be found at #3000, and the five bit character matrices used for the other modes can be found at #3800. In each case:

bytes 0-7
character 0
bytes 8-15
character 1
...
bytes 2040-2047
character 255

Each character entry has the following format:

byte 0
top row
...
byte 7
bottom row

The 5x8 matrices are left aligned, ie. bits 7..2 contain the character shape.

It is permissible to alter the screen or matrix tables as required - but note that there is no mechanism for restoring the original contents.

36 FIND FID

#00EC

Find the address of a particular FID

Entry conditions:

DE = address of first 8 characters of filename in common memory (#C000...#FFFF)

Exit conditions:

If FID is found
Carry true
HL = address of FID (ie. of first byte in FID header)
If FID cannot be found
Carry false
HL corrupt
Always
A other flags BC DE corrupt
All other registers preserved.

Notes:

This routine is not generally available on CPC6128 and PCW versions of CP/M Plus.

VI.5 Field Installable Device Drivers

This section describes the structure and interface of Field Installable Device Drivers (FIDs), for the Spectrum +3 computer running under CP/M Plus. It assumes you are familiar with the internals of CP/M Plus. Writing FIDs is an unlikely occupation for most CP/M Plus users and software developers. This information will mainly be of use to the developers of Spectrum +3 hardware add-ons who want their hardware to be supported by CP/M Plus in an integrated way.

Hex numbers are preceded by #, all other numbers are in decimal.

CAUTION: If you use unpublished information about the implementation of CP/M Plus on the Spectrum +3 (such as might be obtained by disassembling existing software) you run the risk that your software will not work with software (or hardware) revisions. Only interfaces documented by Locomotive Software will necessarily be supported in the future!

Overview

During cold boot drive A: user 0 will be searched for FIDs, that is any file with a type part of .FID and a suitable file header. Each FID file is in Page ReLocatable (PRL) format and is loaded into memory somewhere in CP/M bank 0. The FID's Early Morning Start (FID_EMS) routine is then called. The FID can now hook itself into the BIOS in a number of different ways, for example: as one or more disc drives, as one or more character devices, as a non CP/M device such as a mouse, or as a combination of these. The FID is now a part of the BIOS and will remain as such until the next cold boot.

There is no explicit limit on the number of FIDs, however the number is limited by available resources, particularly memory. After loading and initialising all FIDs any remaining memory is used for CP/M's disc cache.

A FID may use the operating system facilities by means of SuperVisor Calls (SVCs). A FID may NOT call the CP/M BDOS or BIOS directly, however an application program may do so on a FID's behalf. The BIOS calls a FID through jumpblocks, the addresses of which are passed to the BIOS during EMS.

If required a CP/M application program or Resident System eXtension (RSX) running in the Transient Program Area (TPA) can communicate directly with a FID. Each FID is named uniquely by its filename, which is stored in the FID header. The application program calls "FIND_FID", in the extended BIOS jumpblock, with a pointer to the FID's filename in common memory. If found the address of the FID in bank 0 is returned, from which any absolute address in the FID can be calculated. Routines in a FID can then be called from the TPA by using USERF.

Resources

On the Spectrum +3 there are over 12K bytes available in bank 0 and 1.5K bytes in bank 2 for FIDs and their disc data structures and about 128 bytes in common memory for their character device table and DPBs.

A FID should not rely on these figures since firstly other FIDs may be loaded and, secondly, any future enhancements to the BIOS may alter these figures. In order to minimise the risk of running out of memory, a FID should be as small as possible and only use the resources it requires. Since FIDs use memory which would otherwise be used to cache floppy disc sectors, FIDs will tend to adversely affect floppy disc performance.

FID Program Format and Ground Rules

The FID program may be regarded as having an origin of #0100, of length P bytes followed by an uninitialised variable area of length V bytes:
#0100 First byte of program
#0100 + P - 1 Last byte of program
#0100 + P First byte of uninitialised variable area
#0100 + P + V - 1Last byte of uninitialised variable area

Whilst loading the FID program is relocated onto a 256 byte boundary and all addresses are changed accordingly.

All relocatable bytes value #FF are assumed to be the most significant byte of a SVC. Both it and the preceding byte are replaced by the actual address of the SVC. A list of SVCs is given below.

It is strongly recommended that all uninitialised variables, buffers etc are placed in the uninitialised variable area rather than in the program file image itself. This can drastically reduce the file size.

Throughout this section, all routines whose names start FID_ are provided by the FID itself, all other routines and variables are system provided by SVCs.

Each routine is documented with its register entry and exit conditions. These must be followed exactly. In particular the phrase "All other registers preserved" means any register that is not explicitly mentioned as returning a value or being corrupt (ie. indeterminate) must be preserved.

All FID routines will be called using a stack in common memory, a local stack should not be necessary.

Interrupts should not be disabled unless absolutely necessary, and then only for short periods, i.e. tens of microseconds. If you cannot avoid disabling interrupts then you will cause the system clock to run slow, and the keyboard to be scanned erratically. A FID can go some way to alleviating this by using the routine SVC_CATCHUP.

FIDs may bank switch if they require to do so, but must restore the calling environment before returning, or allowing interrupt code to run. When a FID is called the memory arrangement is 4-5-6-3. The stack is in bank 3. The FID must arrange to be totally contained within bank 5 by setting its loading bound values correctly (see below). Thus if a FID requires to switch in a ROM it will have to change to the environment R-5-2-3. In order to restore this environment the FID may make use of the system variables SVC_BANK_68 and SVC_BANK_05 which contain the current contents of the hardware latches on channels #1FFD and #7FFD respectively. If the FID wishes to allow normal interrupt code to run it should first write to these variables to reflect the new state of the latches... which must be for the 4-5-6-3 memory arrangement. FIDs cannot rely upon the contents of these latches or variables being maintained from one call of the FID to the next.

The first 32 bytes of a FID is the FID header and must have the following standard form:
#0100..#0102JMP FID_EMS;jump to EMS routine
#0103..#010A'SPECTRUM' ;FID identity, overwritten by actual filename
#010B..#010D'FID' ;type extension
#010E..#010F ;version number (user specific)
#0110..#0111 ;checksum
#0112 ;lower bound on loading
#0113 ;upper bound on loading
#0114..#011F ;reserved for Locomotive, initialise to 0

The version number field is reserved for use by the user. It is recommended that it is incremented for each new issue of the FID.

The checksum field is the sum of all bytes in the FID file from byte 0 to the last byte in the PRL relocation map, excluding the checksum field, modulo #10000. The Mallard BASIC program FIDCSUM.BAS will plant the correct checksum into a FID file.

The lower bound on loading is the most significant byte of the first address which will be considered for loading the FID. The upper bound on loading is the most significant byte of the first address which cannot be allocated to this FID. Thus a FID which wishes to bank switch should set these values to #40 and #80 respectively; this will guarantee that it will be loaded somewhere between #4000 and #7FFF.

The FID loader will check the checksum and that the name and type of the file are as above, if not then it is assumed that the file is not a FID, otherwise the name and type fields are overwritten by the actual name and type of the file (including any attribute bits). The name field is used by the routine FIND_FID to locate a particular FID in memory.

A FID which requires more memory than is available, or which cannot be loaded into its requested area will not be loaded at all.

Bytes #0114..#011F are reserved for use by the BIOS and must not be written to by the FID.

Subject to availability, the FID_EMS routine may allocate the FID some extra memory over and above the area which was automatically allocated when the FID was loaded. The routine SVC_ALLOCATE will allocate a fixed size area and SVC_MAX_ALLOCATE will allocate the largest available area. It is not possible to restrict the allocation to, for example, the area #4000..#7FFF. FIDs should use SVC_ALLOCATE whenever possible, in preference to SVC_MAX_ALLOCATE.

The FID_EMS routine can also return any memory which it does not require by using the routine SVC_DEALLOCATE. This memory MUST BE an area or part of an area which has been allocated by SVC_ALLOCATE or SVC_MAX_ALLOCATE. It is also permissible to return part of the area which was allocated when the FID was loaded PROVIDED that the FID_EMS routine is not going to return with carry false - in which case the entire area will be automatically deallocated anyway. Please note that deallocating memory which does not belong to a FID is very likely to cause CP/M Plus to behave incorrectly.

FID_EMS

Early morning start.

Entry conditions:

DE = FID environment version
C = country code

Exit conditions:

If carry true then OK
HL = address of sign on message terminated by #FF
If carry false then error
HL = address of error message terminated by #FF
Always
Other flags A BC DE IX IY corrupt
All other registers preserved

If this routine returns with carry false then the FID is discarded and the memory allocated when it was loaded will be re-used. This routine must NOT return with carry false once it has successfully called FID_C_HOOK or FID_D_HOOK, since the FID is now part of the BIOS and will be called upon to drive the hooked-in devices. If this routine is to return carry false then it must first use SVC_DEALLOCATE to return any memory which it has specially acquired using SVC_ALLOCATE or SVC_MAX_ALLOCATE.

The environment version number is to allow for future exapnsion of the FID system. The FID can choose whether or not to proceed in the given environment. The most significant byte is the major version number, currently #00. This is incremented with each non-upwards compatible change to the FID environment (although none are envisaged). A FID should refuse to run unless it recognises the major version number. The least significant byte is the minor version number, currently #00. This is incremented with each upwards compatible release of the FID environment. A FID should check that the minor version number is sufficiently high to provide the facilities which it requires.

The returned messages are displayed on the screen and should be in the language of the country specified. The country codes are:
0:U.S.A.6:Italy
1:France7:Spain
2:Germany20:Holland
3:U.K.21:Finland
4:Denmark and Norway23:Wales
5:Sweden24:Portugal

For neatness, messages should be designed to fit on one line, but they may use escape sequences if they wish to do otherwise. Since more than one FID may be loaded, a FID should avoid clearing the screen, or assuming that a particular area of the screen has not yet been written to.

SVC_ALLOCATE

Allocate fixed amount of extra memory.

Action:

This routine allocates the FID a fixed size area of memory.

This routine must only be called from FID_EMS.

Entry conditions:

DE = number of bytes of memory required

Exit conditions:

If carry true then OK
HL = address of first byte of memory allocated
If carry false then no area of this size can be found
HL corrupt
Always
Other flags A BC DE corrupt
All other registers preserved

SVC_DEALLOCATE

Return unwanted memory.

This routine returns an area of memory to the free pool.

This routine must only be called from FID_EMS.

Entry conditions:

HL = address of first byte of area
DE = number of bytes in the area

Exit conditions:

Always
All flags, A BC DE HL corrupt
All other registers preserved

Note that the data structures recording the locations of free memory are of limited size. FIDs should avoid fragmenting free memory unnecessarily.

SVC_MAX_ALLOCATE

Allocate maximum possible area of extra memory.

This routine allocates the FID the maximum currently available area of memory.

This routine must only be called from FID_EMS.

Entry conditions:

No conditions

Exit conditions:

If carry true then OK
HL = address of first byte of memory allocated
DE = length of area in bytes
If carry false then no area of this size can be found
DE HL corrupt
Always
Other flags, A BC corrupt
All other registers preserved

Character devices

CP/M Plus supports up to 13 character devices, numbered 0..12. Each device is identified by its number and a six character name. Associated with each evice is an entry in the CP/M character device table. This entry gives the name of the device together with its mode and baudrate. The CP/M DEVICE utility can display and change a device's mode and baudrate.

Character device table entry format:
bytes 0..5name upper case, left justified, space filled
byte 6 mode device's characteristics
byte 7 baudrateencoded baudrate

The mode byte defines the device's characteristics, it is bit significant:
bit 0 input can get bytes from the device
bit 1 output can put bytes to the device
bit 2 soft baudsupports software selectable baudrates
bit 3 serial may use XON/XOFF protocol
bit 4 xon xoff enables XON/XOFF protocol
bits 5..6reserved set to 0
bit 7 timeout set to 1 to disable the timeout for this device.

The baudrate is encoded as:
0 no baudrate
1 50 baud
2 75 baud
3 110 baud
4 134.5 baud
5 150 baud
6 300 baud
7 600 baud
8 1200 baud
9 1800 baud
102400 baud
113600 baud
124800 baud
137200 baud
149600 baud
1519200 baud

CP/M offers no support for parity, control line handshaking etc. To support these non-CP/M characteristics the user must supply their onw .COM program which communicates directly with the FID. For example the SETSIO.COM program is used to set the parity and handshaking of the SIO device.

For each new character device required the FID calls SVC_C_HOOK passing the address of a jumpblock and the address of a copy of the character device table entry. SVC_C_HOOK will allocate the next available character device and return its number and a pointer to its CP/M character device table entry.

A FID may not use the names of the resident device drivers. These are:
CRTscreen and keyboard
LPTprinter
SIOserial port

System messages have special support. This is to allow devices such as alternative screens to separate out system messages from program messages - much as the built-in screen is prepared to place them onto the rotating status line.

If CRT is one of the devices which is currently attached to CONOUT: then the system will attempt to place its messages onto the rotating status line. If the rotating status line is disabled or CRT is not attached to CONOUT: then the system message will be sent to the special message output routines of all CONOUT: devices.

Devices (such as serial port drivers) which do not wish to distinguish between system messages and other messages should arrange for FID_C_M_STATUS to be the same as FID_C_O_STATUS, and FID_C_MESSAGE to be the same as FID_C_OUTPUT.

A character device may use routines provided by other character devices. This allows a FID to be written as a "filter" to edit characters before passing them on to another character device FID or to a standard built-in device. One could for example build a FID to discard LF characters before passing them on to the standard LPT device. This avoids having to build duplicate hardware device driving code within the FID. The FID should use the routine SVC_C_FIND in order to locate other character device jumpblocks.

SVC_C_HOOK

Hook in a new character device.

Action:

Returns the address of the character device table entry.

This routine must only be called from FID_EMS.

Entry conditions:

DE = address of character device jumpblock
HL = address of copy of character device table entry

Exit conditions:

If carry true then device allocated
B = device number
DE = address of actual character device table entry
A corrupt
If carry false then error
A = error code
1 => no more devices available
2 => name in use already
B DE corrupt
Always
Other flags A HL corrupt
All other registers preserved

Format of character device jumpblock:

+0 JMP FID_C_INIT ;initialise
+3 JMP FID_C_I_STATUS;input status
+6 JMP FID_C_INPUT ;input a character
+9 JMP FID_C_O_STATUS;output status
+12JMP FID_C_OUTPUT ;output a character
+15JMP FID_C_M_STATUS;system message output status
+18JMP FID_C_MESSAGE ;output a system message character

FID_C_INIT

Initialise character device according to character device table entry, (eg. baud rate). This routine is called once when the device is first being hooked in, then again every time the device's baudrate is changed.

Entry conditions:

B = device number
DE = address of character device table entry

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

FID_C_I_STATUS

Is there a character available for input?

Entry conditions:

B = device number
DE = address of character device table entry

Exit conditions:

If carry true then character available for input
If carry false then character available for input
Always
Other flags A BC DE HL corrupt
All other registers preserved

FID_C_INPUT

Input a character, if none available wait until there is.

Entry conditions:

B = device number
DE = address of character device table entry

Exit conditions:

A = character
F BC DE HL corrupt
All other registers preserved

FID_C_O_STATUS

Is the character device ready for output?

Entry conditions:

B = device number
DE = address of character device table entry

Exit conditions:

If carry true then ready for output
If carry false then not ready for output
Always
Other flags A BC DE HL corrupt
All other registers preserved

FID_C_OUTPUT

Output a character, if not ready wait until it is.

Entry conditions:

B = device number
C = character
DE = address of character device table entry

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

FID_C_M_STATUS

Is the character device ready for system message output?

Entry conditions:

B = device number
DE = address of character device table entry

Exit conditions:

If carry true then ready for output
If carry false then not ready for output
Always
Other flags A BC DE HL corrupt
All other registers preserved

FID_C_MESSAGE

Output a system message character, if not ready wait until it is.

Entry conditions:

B = device number
C = character
DE = address of character device table entry

Exit conditions:

AF BC DE HL corrupt
All other registers preserved

SVC_C_FIND

Find the character device jumpblock of another character device.

Action:

Returns the address of the character device jumpblock. It is then possible to call the jumpblock entries and thus use the facilities of another character device. It should not be assumed that the jumpblock consists of entries of the form JMP <routine> and the routine address extracted; instead control should be transferred to the relevant offset within the jumpblock.

This routine must only be called from FID_EMS. The jumpblock routines may be called at any time when the FID has been called via its own character device jumpblock.

Entry conditions:

HL = address of six byte character device name

Exit conditions:

If carry true then device found
DE = address of jumpblock
If carry false then device was not found
DE corrupt
Always
Other flags A BC HL corrupt
All other registers preserved

Disc Devices

For each new disc device required the FID calls SVC_D_HOOK passing the drive required, the maximum sizes of allocation vector, checksum vector and hash table and the address of a jumpblock. SVC_D_HOOK will allocate a DPB, DPH, allocation vector, checksum vector and hash table. If there is not enough room for the hash table then hashing is disabled.

During EMS a number of separate pools of available memory are maintained. The required disc data structures are allocated from the most suitable pool subject to the CP/M restrictions that a DPB must be in common, allocation vectors and checksum vectors must be in common or bank 0. Hash tables can be in any bank except bank 1.

The logical sector size must be 512 bytes. If the actual disc has a physical sector size greater than 512 bytes then the FID must perform the necessary blocking and deblocking. This is not recommended.

Logical sector numbers are 0 based. Logical to physical sector mapping must be performed by the FID itself. Logical track numbers are also 0 based, and again logical track to physical cylinder/head/track mapping must be performed by the FID itself..

A FID may request a particular drive letter or use the next one available. A FID cannot use a drive letter that has already been allocated. The resident disc drivers will have allocated drives A: and B: before any FID is called, therefore a FID cannot use these letters. It is recommended that a FID uses the next available drive letter rather than specifying a particular one.

The FID may not access the drive table, DPH, allocation vector, checksum vector or hash table, this is because with bank switching they may not be visible to the FID_D_ routines.

All bank switching will be performed before the FID_D_READ or FID_D_WRITE routines are called so these routines need not concern themselves with bank switching.

When a routine returns an error code the BIOS will, if required, issue an error message on the status line followed by Retry, Ignore or Cancel? (or the local language equivalent). The BIOS will also deal with the user's response (and the language variations of that). The error message if fetched via FID_D_MESS using message numbers returned by the FID, these message numbers may take any value in the range #00..#FF. They will not clash with internal message numbers or with those used by other FIDs. The status line message is not issued if the BDOS is in error return mode.

The error message should be at most 50 characters long and must not contain any control codes. For example:

Drive C: track 5, sector 7 read fail

This will be displayed as:
Drive C: track 5, sector 7 read fail - Retry, Ignore or Cancel?

SVC_D_HOOK

Hook in a new disc device.

Action:

Hooks in a new disc device. The DPB can be initialised now or left until FID_D_LOGIN is called.

This routine must only be called from FID_EMS.

Entry conditions:

B = drive
#00 => A:
#01 => B:
..
#0F => P:
#FF => first available drive
DE = address of disc device jumpblock
HL = max size of 2 bit allocation vector in bytes
IX = max size of checksum vector in bytes, 0 if none
IX = max size of hash table in bytes, 0 if none

Exit conditions:

If carry true then device allocated
B = device number
IX = address of DPB
A corrupt
If carry false then error
A = error code
1 => not enough memory
2 => drive already allocated
B IX corrupt
Always
Other flags C DE HL IY corrupt
All other registers preserved

Format of disc device jumpblock:

+0 JMP FID_D_LOGON;logon disc
+3 JMP FID_D_READ ;read a sector
+6 JMP FID_D_WRITE;write a sector
+9 JMP FID_D_FLUSH;flush buffers
+12JMP FID_D_MESS ;get a disc error message string

FID_D_LOGON

Logon a drive and initialise the DPB as required. The required sizes of allocation vector, checksum vector and hash table must not exceed those allocated by SVC_D_HOOK.

Entry conditions:

B = drive
IX = address of DPB

Exit conditions:

If carry true then OK
A = return code
#00 => OK
B corrupt
If carry false and zero true then error but no retry
A = CP/M return code
#01 => non-recoverable error condition
#FF => media changed
B corrupt
If carry false and zero false then error; ask user, retry if requested
A = CP/M return code
#01 => non-recoverable error condition
#FF => media changed
B = message number
Always
C DE HL IX IY corrupt
All other registers preserved

FID_D_READ

Read a 512 byte sector.

Entry conditions:

B = drive
DE = logical sector
HL = logical track
IX = address of DPB
IY = address of destination

Exit conditions:

If carry true then OK
A = return code
#00 => OK
B corrupt
If carry false and zero true then error but no retry
A = CP/M return code
#01 => non-recoverable error condition
#FF => media changed
B corrupt
If carry false and zero false then error; ask user, retry if requested
A = CP/M return code
#01 => non-recoverable error condition
#FF => media changed
B = message number
Always
C DE HL IX IY corrupt
All other registers preserved

FID_D_WRITE

Write a 512 byte sector.

Entry conditions:

B = drive
C = deblocking code
0 => deferred write
1 => non-deferred write
2 => deferred write to first sector in block
DE = logical sector
HL = logical track
IX = address of DPB
IY = address of source

Exit conditions:

If carry true then OK
A = return code
#00 => OK
B corrupt
If carry false and zero true then error but no retry
A = CP/M return code
#01 => non-recoverable error condition
#02 => disc is read-only
#FF => media changed
B corrupt
If carry false and zero false then error; ask user, retry if requested
A = CP/M return code
#01 => non-recoverable error condition
#02 => disc is read-only
#FF => media changed
B = message number
Always
C DE HL IX IY corrupt
All other registers preserved

FID_D_FLUSH

Flush any internal buffers; ie write to disc such things as deblocking buffers whose write has been delayed.

Entry conditions:

B = drive
IX = address of DPB

Exit conditions:

If carry true then OK
A = return code
#00 => OK
B corrupt
If carry false and zero true then error but no retry
A = CP/M return code
#01 => non-recoverable error condition
#02 => disc is read-only
#FF => media changed
B corrupt
If carry false and zero false then error; ask user, retry if requested
A = CP/M return code
#01 => non-recoverable error condition
#02 => disc is read-only
#FF => media changed
B = message number
Always
C DE HL IX IY corrupt
All other registers preserved

FID_D_MESS

Return a message string in the language of the country specified to FID_EMS.

Entry conditions:

B = message number (as returned by the other FID_D_ routines)
IX = address of DPB

Exit conditions:

If carry true then message text is available
HL = address of message terminated by #FF
If carry false then error, no message
HL corrupt
Always
Other flags A BC DE corrupt

Disc Change Detection

If the disc driver supoprts removable media then the following routine can be called to flag that the media has changed. This routine can be called from within interrupt code if required.

SVC_D_CHANGED

Flag that the disc has changed.

Action:

Flags that the disc has changed

May be called from within interrupt code

Entry conditions:

B = drive

Exit conditions:

All registers and flags preserved

Disc Driver Data Structures

This section summarises the format of the Disc Parameter Block and the various sizes of the disc drive data structures.

Functions

Floor(x) = largest integer <= x.
Ceiling(x) = smallest integer >= x.
Log2(x)= logarithm to the base 2 of x.
High(x) = Floor(x/256), i.e. most significant byte of 16 bits.
Low(x) = x - 256 * High(x), i.e. least significant byte of 16 bits.

Let
BLOCK_SIZE = allocation block size in bytes 1K,2K,4K,8K or 16K
OFF = number of reserved tracks
NUM_TRACKS = total number of tracks
NUM_SECTORS = number of sectors per track
DIR_BLOCKS = number of directory blocks 1..16
SEC_SIZE = 512, sector size in bytes
REC_SIZE = 128, record size in bytes
DIR_ENTRY_SIZE= 32, directory entry size

Records per track

SPT = NUM_SECTORS * SEC_SIZE / REC_SIZE

Block shift

BLS = Log2 (BLOCK_SIZE / REC_SIZE)

Block mask

BLM = (BLOCK_SIZE / REC_SIZE) - 1

Highest block number

DSM = Floor((NUM_TRACKS - OFF) * NUM_SECTORS * SEC_SIZE / BLOCK_SIZE) - 1

Highest directory entry number

DRM = (DIR_BLOCKS * BLOCK_SIZE / DIR_ENTRY_SIZE) - 1

Directory allocation map, least significant

AL0 = High (216 - 2(16 - DIR_BLOCKS))

Directory allocation map, most significant

AL1 = Low (216 - 2(16 - DIR_BLOCKS))

Extent mask

EXM = if DSM < 256

then (BLOCK_SIZE / 210) - 1
else (BLOCK_SIZE / 211) - 1

Checksum vector size

CKS = (DRM + 1) / 4

Physical sector shift

PSH = Log2 (SEC_SIZE / REC_SIZE)

Physical sector mask

PHM = (SEC_SIZE / REC_SIZE) - 1

Allocation vector size

ALS = Floor(DSM / 4) + 2

Hash table size size

HASH = 4 * (DRM + 1)

DPB format:

bytes0..1 SPTrecords per track
byte 2 BSHblock shift
byte 3 BLMblock mask
byte 4 EXMextent mask
bytes5..6 DSMlast block number
bytes7..8 DRMlast directory entry number
byte 9 AL0directory allocation
byte 10 AL1directory allocation
bytes11..12CKSchecksum vector size, set bit 15 if fixed disc
bytes13..14OFFreserved tracks
byte 15 PSHphysical sector shift
byte 16 PHMphysical sector mask

Note for a fixed disc set CKS = bit 15, this disabled directory checksumming and indicates that the disc cannot be removed.

If hashing is not required set the hash table size to 0.

Other Devices

A FID is not obliged to be a character device and/or a disc device although these are the most likely applications.

A FID may not call the BIOS or BDOS directly. A FID may call the documented SVCs, see list below.

SuperVisor Calls

The following routines are available to a FID. The most significant byte of their addresses is #FF and must be marked as relocatable in the PRL file. Both bytes of the address are replaced by the FID loader.

Bank Switching Support
#FF00SVC_BANK_05 ;last value sent to channel #7FFD
#FF01SVC_BANK_68 ;last value sent to channel #1FFD
#FF02SVC_CATCHUP ;execute (some) interrupt actions

The System Control Block
#FF03SVC_SCB ;address of SCB

Hook routines, only available to FID_EMS
#FF04SVC_C_HOOK ;hook character device
#FF05SVC_D_HOOK ;hook disc device

Disc Device Support Routines
#FF06SVC_D_CHANGED ;flag that a disc has changed

EMS Memory allocation and Deallocation
#FF07SVC_ALLOCATE ;allocate fixed size of extra memory
#FF08SVC_MAX_ALLOCATE ;allocate max. area of extra memory
#FF09SVC_DEALLOCATE ;return unwanted memory

Filter support
#FF0ASVC_C_FIND ;locate character device jumpblock

SVC_CATCHUP

Perform actions as if a timer interrupt has just occurred.

Action:

This routine should be used when interrupts have been disabled for a considerable time and it is very likely that some timer interrupts have been missed. It can cause the cursor to flash, the keyboard to be scanned and the system time to be advanced.

Entry conditions:

No register conditions
Interrupts must be disabled

Exit conditions:

Interrupts remain disabled
AF BC DE HL corrupt
All other registers preserved

Page ReLocatable (PRL) File Format

PRL is a standard CP/M file format and is supported by the CP/M linker LINK-80. The switch [op] generates a .PRL file, the switch [Mxxxx] specifies the size of the uninitialised variable area (hex xxxx bytes).

Let H = #0100, size of the PRL header in bytes

Let P = size of the program in bytes

Let M = ceiling (P/8), size of the relocation map in bytes

Let V = size of uninitialised variable area in bytes
bytes 0 .. H - 1PRL header
bytes 1 .. 2 program size in bytes = P
bytes 3 .. 4 uninitialised variable area size in bytes = V
bytes H .. H + P - 1 program binary image
bytes H + P .. H + P + M - 1relocation map

The program binary image has an origin of #0100.

The relocation map contains one bit per byte of program.
MapProgram
byte 0 bit 7byte 0
byte 0 bit 6byte 1
... ...
byte 0 bit 0byte 7
byte 1 bit 7byte 8
... ...

The uninitialised variable area is allocated immediately after the program, ie. at relocatable address #0100 + P .. #0100 + P + V - 1.
The source for the operating system extensions RAMDISC.FID and SERIAL.FID are supplied in the two files, RAMDISC.ASM and SERIAL.ASM on Side 2 of the disc. These source files provide useful examples on which to base further FID files. RAMDISC.ASM provides an example of a Disc Device FID; SERIAL.ASM provides an example of a Character Device FID.

Note: These files are set up for tabs every eight characters: however, if you edit them using RPED, this layout will be thrown away.


Appendix V Index Appendix VII