CP/M pages
Home -> CP/M -> Amstrad XBIOS Internals

Amstrad Extended BIOS Internals

Any Amstrad CP/M Plus system (meaning those created by Locomotive Software for the CPC, PCW and Spectrum +3) has an XBIOS, which lives at 80h in CP/M bank 0. It exposes a number of functions for things like reading, writing and formatting floppy discs, reassigning keys on the keyboard, setting options for the serial port, and so on.

In a conventional banked CP/M Plus system, bank 0 holds only the banked BDOS, a BIOS to manage the hardware, and possibly some disc buffers. In an Amstrad/Locomotive CP/M Plus system, the banked BIOS is small and pretty much hardware-independent; all it does is wrap the XBIOS calls in CP/M character and disc devices.

Internally, the XBIOS is divided into modules (this is why some functions in the jumpblock have names starting DD, some starting CD, some starting TE and so on). I have given these modules names based on the functions in them, on other sources where available, and invented my own names if necessary. When disassembling, it's often quite easy to tell where one module stops and another starts (at least in early versions) because the modules are aligned on 8- or 16- byte boundaries.

Sources

I have derived the information in this document from the following CP/M boot files:

System fileMachineVersion
C10CPM3.EMSCPC1.0
CPM3SEMI.EMSCPC1.0 (patched to include driver for Dk'tronics Silicon Disc)
S10CPM3.EMSSpectrum +31.0
J11CPM3.EMSPCW 80001.1
J12SCPM3.EMSPCW 80001.2 (Spanish)
J14CPM3.EMSPCW 80001.4
J14FCPM3.EMSPCW 80001.4 (French)
J14GCPM3.EMSPCW 80001.4 (German)
J14SCPM3.EMSPCW 80001.4 (Spanish)
J15ACPM3.EMSPCW 80001.5 (US)
J17CPM3.EMSPCW 80001.7
J17CPM3.EMSPCW 80001.7H
J11CPM3.EMTPCW 8000, 9256, 101.11
J12CPM3.EMSPCW 8000, 9256, 101.12
J14CPM3.EMTPCW 8000, 9256, 101.14
J15CPM3.EMTPCW 8000, 9256, 101.15
J21CPM3.EMSPCW 95122.1
J29CPM3.EMSPCW 95122.9
J21CPM3.EMTPCW 9512, 9512+2.11
J22CPM3.EMSPCW 9512, 9512+2.12
J25CPM3.EMS PCW 9512, 9512+ 2.15
J25CPM3.EMT

If you have any other versions of the system file, feel free to share them with me.

The CPC

On the CPC, CP/M Plus runs as an application under the CPC's ROM firmware. This is the memory layout of bank 0 (with areas owned by the XBIOS shown thus):

0000-007FUsed by CPC firmware
0080-01FF

The XBIOS jumpblock.

Also contains initialisation code and displays the sign-on message.

0200-03FF

CD: Character Devices (code segment)

Routes character I/O to/from the screen, keyboard, printer and serial interface. Drives the serial interface at the hardware level; the printer and keyboard are handled by the firmware, and the screen by TE below.

0400-0AEF

TE: Terminal Emulator (code segment)

Interprets ESCape sequences sent to the screen device and converts them into SCR_* calls to the CPC firmware.

0AD0-0EFF DD: Disk Devices (code segment)
All floppy access, including reading, writing and formatting discs.
0F00-3CFFCP/M banked BDOS
3D00-3FFFCP/M banked BIOS
4000-7FFFVideo RAM
8000-87FFFont
8800-89FFSystem message table.
8A00-B0CFCP/M disc buffers
B0D0-B0FFDD data segment.
B100-BE3FUsed by CPC firmware.
BE40-BEEFCD and TE data segments.
BEF0-BFFFUsed by CPC firmware.
C000-F5FFThe top of the TPA.
F600-FBFFCP/M resident BDOS.
FC00-FF9FCP/M resident BIOS.
FFA0-FFDF

Floppy transfer code.

Used when reading and writing floppy data. This code has to be in common memory because the data may be coming from or going to any CP/M bank.

FFE0-FFFF

XBIOS data transfer area.

Used by XBIOS calls to return data structures to code that might be in different banks. Used to return floppy results (for DD_L_* calls), addresses of data buffers (for CD_INFO call) and so on.

Also contains a flag to say whether keyboard input is ready. This allows a fast check by the resident BIOS, without needing to do a bank switch.

The PCW (Old World)

The original versions of CP/M supplied with the PCW8256 (versions 1.2, 1.4) have a similar layout to the CPC version (give or take the constraints of hardware, such as the position and size of video RAM). However, the PCW doesn't have a firmware ROM, so it has to provide modules of its own to do the same job.

The addresses in this table come from BIOS 1.2. Those in the more common 1.4 are slightly different, but the arrangement of modules is the same.

0000-007F

Zero Page (see below).

Contains the Z80 Restart vectors, which on the PCW are used to call code in other memory banks. Also contains current memory paging status and memory size.

0080-022FXBIOS jumpblock and initialisation code.
0230-043FCD: Character Devices
0440-08CFTE: Terminal Emulator
08D0-0ACF

MSG: Displays system messages.

In the CPC version, this is found about halfway through TE rather than being a separate module.

0AD0-100F DD: Disk Devices
1010-13EF

KM: Keyboard Manager

This handles scanning the PCW's memory-mapped keyboard, allows keys to be redefined, and provides the required KM_* functions that (on a CPC) would be provided by the firmware.

13F0-168F

SCR: Screen

As with KM, this performs screen access (drawing characters, scrolling or blanking areas, and so on) that on the CPC would have been provided by the firmware.

1690-182FDefault keyboard layout.
1830-1B2FSystem messages. As for the CPC, but also adds all the printer status messages.
1B30-1D3F

NMI: Non-Maskable Interrupt handler

The PCW uses the Z80 Non-Maskable Interrupt to handle floppy disc reads and writes. This module contains the NMI handlers, and code to notify the rest of the system when disc access is about to begin or end.

1D40-203F KL: Kernel (Part 1)
2040-261F

KL: Kernel (Part 2)

As with SCR_ and KM_ functions, the KL module provides the kernel functions used by CP/M. In fact, it provides more than are used by CP/M; CPC functions such as KL_TIME_PLEASE and KL_TIME_SET are present even though CP/M doesn't use them.

The kernel is divided into two modules. The first one handles interrupts, processes and semaphores; the second one manages events, tickers and kernel memory allocation.

2620-286F Data segments, in the order KL, CD, TE, MSG, NMI, DD, KM, SCR.
2870-58FFCP/M disc buffers
5900-5903Buffer control block linked list: head pointers.
5904-592FUnused
5930-7FFF

Video RAM.

Though the screen can display 32 lines of text, the XBIOS allocates memory for 33. The extra line is used to display the printer status bar; using the Roller-RAM system, it gets swapped in/out instead of the bottom line of the screen.

Not all the video RAM is visible in bank 0. The remainder, plus the font, are only visible in the 'screen environment' memory configuration.

8000-8BFFCP/M disc buffers
8C00-B9FFCP/M banked BDOS
BA00-BFEFCP/M banked BIOS
BFF0-BFFF Memory-mapped keyboard.
C000-F5FFThe top of the TPA.
F600-FBFFCP/M resident BDOS.
FC00-FF9FCP/M resident BIOS.
FFE0-FFFF XBIOS data transfer area.

The process and semaphore features in the kernel are used for dot-matrix printer support. Semaphores are used to block disc access while the printer is active (or vice versa), while processes manage different aspects of the printer. In a PCW8256 CP/M system, four processes will be in existence, at different priority levels:

Priority 32: CP/M
Priority 48: Printer control state
Priority 56: Screen dump
Priority 60: Printer hardware driver

Normally, the three printer processes will be blocked on semaphores. If they are invoked (eg, by the right key combination, or by sending output to the printer) the appropriate semaphore will be raised. The corresponding process will then have a higher priority than CP/M, and so will run until it next becomes blocked.

The PCW (New World)

By the time of 8512 CP/M version 1.7, the XBIOS memory map had been rearranged to support loadable drivers. This organisation was subsequently used by BIOS 2.1 on the PCW 9512 and all subsequent releases.

I have seen two versions of 1.7: 1.7, which can't load drivers, and 1.7H, which can. The two are identical in all other aspects. My guess is that at this time, the BIOS could be built in two versions: vanilla, which couldn't load drivers, and 'H', which could. Likewise, BIOS 2.1 can't load drivers; according to my theory, 2.1H, if it ever existed, would have been able to.

The addresses in this table come from BIOS 2.9. Other versions differ.

0000-007F

Zero Page (see below).

0080-01D7XBIOS jumpblock and initialisation code.
01D8-024FHelper code for the daisywheel printer.
0250-067F

System messages.

On a daisywheel system, the message area is 256 bytes larger than on a dot-matrix system. So NMI is at 0680h in version 2.9, but at 0580h in version 1.9.

0680-08BFNMI code segment
08C0-114FKL code segment
1150-1213Data segments for KL and NMI
1214-3D7FAvailable for loadable drivers or disc buffers.
3D80-3F4F

CD: General character device management.

3F50-41A7

CD: CPS8256 serial port driver

41A8-4567KM: Keyboard Manager

4568-485FSCR: Screen functions

4860-49FFDefault keyboard layout

4A00-4ECFTE: Terminal emulator

4ED0-5137MSG: System message display

5138-56FFDD: Disk Devices
5700-58DF Data segments for the internal device drivers, in the order MSG, KM, SCR, TE, DD, CD
58E0-5907Device driver tables
5904-592FUnused
5930-7FFFVideo RAM.
8000-8BFFAvailable for loadable drivers or disc buffers.
8C00-B9FFCP/M banked BDOS
BA00-BEFFCP/M banked BIOS
BFF0-BFFF Memory-mapped keyboard.
C000-F5FFThe top of the TPA.
F600-FBFFCP/M resident BDOS.
FC00-FF50CP/M resident BIOS.
FF51-FFDFAvailable for loadable drivers: additional disc DPBs.
FFE0-FFFF XBIOS data transfer area.

The new layout keeps the kernel in the bottom 16k (where it needs to be in order to access other memory banks) but pushes all the hardware drivers up so that they grow down from 58E0h — and, importantly, come down below 4000h. This is because, when disc transfers are done, the area from 4000h-7FFFh contains the source/target memory. Since any bank could be present in that area when the driver is called, the driver itself can't be allowed to occupy that space. Having the built-in CP/M drivers live there gets round the problem nicely.

(Note that NMI, which handles data transfers for the built-in floppy driver, is not in that area. It's down at the bottom of memory with the kernel).

Loadable device drivers cause a big change to the undocumented areas of the XBIOS jumpblock. Previously, the BIOS would do character I/O by calling the jumpblock with a device number. Now, it looks up the device's character device jumpblock in the table of character devices at 58E0h, and jumps straight to it.

At boot time, a loadable device manager is present in memory (for BIOS 2.9, at 0EDA0h; this varies depending on version). The device manager searches the boot disc for files ending in .FID. If any are found, it loads them into memory and allows them to add themselves to the BIOS as a character or disc device. The XBIOS registers its built-in character devices (screen, keyboard etc.) with this manager at boot time.

The Spectrum +3

Spectrum +3 CP/M appears to have branched off the PCW tree at about version 2.8 (with a few fragments, such as scrolling error messages, coming from CPC CP/M).

Like the 'new-world' PCW XBIOS, the Spectrum XBIOS comes in two parts, with the first half containing the jumpblock and messages, and the second half holding device drivers. Though the two parts are stored separately in the EMS file, they end up in a contiguous block of memory.

The major difference in +3 CP/M is that there is no KL (kernel) module. Rather than individual modules registering timers, event blocks, interrupt handlers and so forth, the 50Hz ticker interrupt handler is hardwired to perform the necessary functions.

0000-007F

Zero Page (see below).

Contains the hard-wired timer interrupt handler.

0080-016FXBIOS jumpblock and initialisation code.
0170-01EFThe same library of helper routines as on the PCW9512. This despite the fact that they're only used by the 9512 daisywheel printer driver, which the Spectrum doesn't have.
01F0-039F

System messages.

03A0-04CF

CD: General character device management.

04D0-05AF

CD: RS232 serial port driver

05B0-08FFKM: Keyboard Manager

0900-0F5FSCR: Screen functions

0F60-100FDefault keyboard layout

1010-1517TE: Terminal emulator

1518-178FMSG: System message display

1790-1DDFDD: Disk Devices
1DE0-21C7 Data segments for the internal device drivers, in the order MSG, KM, SCR, TE, DD, CD
21C8-21EDDevice driver tables
21EE-2417Unknown
2418-2FFFAvailable for loadable drivers or disc buffers.
3000-3FFFFonts
4000-5AFF

Video RAM.

There is a second page of video RAM in CP/M bank 2 for the right-hand half of the 24×80 video mode.

5B00-8BFFAvailable for loadable drivers or disc buffers.
8C00-B9FFCP/M banked BDOS
BA00-BFFFCP/M banked BIOS
C000-F3FFThe top of the TPA.
F400-F9FFCP/M resident BDOS.
FA00-FD4CCP/M resident BIOS.
FD4D-FDDBAvailable for loadable drivers: additional disc DPBs.
FDDC-FF53Serial port driver routines. These are timing-critical and need to run in uncontended memory.
FF54-FFDFFloppy transfer code.
FFE0-FFFF XBIOS data transfer area. On the +3 this contains, inter alia, the BANKM and BANK678 variables.

Zero Page

In PCW CP/M, the first 128 bytes of bank 0 are laid out like this:

0000RST 0: Jump to a function in the bank 2 environment. Takes the address as an inline parameter.
0003(2.9+) Jump to the address in BC
0005(2.9+) Jump to the address in DE
0007(2.9+) Jump to the address in HL
0008RST 8: Jump to a function in the screen environment. Takes the address as an inline parameter.
000B(2.9+) Jump to the address in IX
000D(2.9+) Jump to the address in IY
0010(2.1+) RST 10. Does nothing.
0018(2.1+) RST 18. Does nothing.
0020RST 20. Jump to code in an arbitrary memory environment. Takes a word as an inline parameter, which points to a 4-byte structure:
	DEFW	address
	DEFB	bank to page in at 4000h
	DEFB	bank to page in at 8000h
0028RST 28. As RST 20, but the 4-byte structure directly follows the RST.
002BAs RST 20, but the address of the 4-byte structure is passed in HL.
0030(1.4+) RST 30: Kernel panic. Disables interrupts and enters a tight loop. Presumably used when debugging new versions.
0033Ascii 'BREAK'
0038RST 38: Z80 mode 1 interrupt handler.
003BAscii 'Patch'
0040(2.1+) Hardware bitflags (returned in L by CD INFO)
0042(2.1+) 0FFh if second drive present, otherwise 0. In version 1.7H (only) this is at 40h.
004C(2.11+) Bit 5 set if Shift+Extra+Relay pressed.
0060Memory bank paged in at 0000h
0061Memory bank paged in at 4000h
0062Memory bank paged in at 8000h
0063Memory bank paged in at C000h
0064Frame flyback counter
0065Frame flyback maximum (6 on a UK PCW, 5 on a US model)
0066NMI handler
007FNumber of 16k memory banks in system

For comparison, the same area of memory in Spectrum +3 CP/M looks like this:

0000RST 0: Default timer interrupt handler.
0020RST 20: SVC_CATCHUP — perform a subset of timer interrupt functions.
0030RST 30: Kernel panic
0038IM 1 interrupt handler.
003BStep down the 50Hz system clock to a 1Hz ticker for CP/M.
0054Set the border colour to A.
00750FFh if a second floppy drive is present, else 0.

Disk Parameter Headers

Amstrad CP/M uses an extended version of the standard CP/M Plus DPH. There are two variants, for Old World and New World memory layouts:

Old World

In an Old World memory layout, the standard DPH is preceded by ten bytes. These match the Extended Disk Parameter Header in the Digital Research sample BIOS, described in ยง4.7.3 of the CP/M 3 System Guide:

-00A		Word	Write function
-008		Word	Read function
-006		Word	Select function
-004		Word	Init function
-002		Byte	Physical drive number (floppies only)
-001		Byte	Drive type (always 0?)
0000-0018		Standard CP/M Plus DPH

The first three functions should return results in the same format as their BIOS counterparts WRITE, READ and SELDSK respectively. On entry IX will hold the address of the drive DPB, and DE will hold the address of the DPH.

The fourth function appears to have been intended for drive initialisation, but is never called.

The track, sector and transfer address used by these functions are in a data structure at 0BEF0h [CPC] or 0BDF0h [PCW]. This corresponds to the structure beginning @adrv in the CP/M 3 sample BIOS:

0	DB	@adrv	;Selected logical drive
1	DB	@rdrv	;Selected physical drive (from the byte at DPH-2)
2	DW	@trk	;Track passed to SETTRK.
4	DW	@eect	;Sector passed to SETSEC.
6	DW	@dma	;DMA address passed to SETDMA.
8	DB	@cnt	;Multiple I/O count passed to MULTIO
9	DB	@dbnk	;DMA bank passed to SETBNK

New World

In the New World memory layout, the list of functions is replaced by a single reference to a jump table:

-004		Word	Address of Block device jump table
-002		Byte	Physical drive number (floppies only)
-001		Byte	Drive type (always 0FFh?)
0000-0018		Standard CP/M Plus DPH

A rough outline of development

Based on the CP/M versions I have, here are my best guesses at the order of the versions, and what changed when:

CPC 1.0: 1985
The original.
PCW 1.1: 1985
The first version I have for the PCW, though it's likely that version 1.0 existed as well.
PCW 1.2: 1985
Most of the changes appear to be to support the creation of foreign-language CP/M versions:
  • The keystrokes used for Retry, Ignore and Cancel can now be localised; in 1.1 they were hardcoded.
  • The default screen language and the default matrix printer language are localised, rather than hardcoded.
  • System messages can contain accented characters.
  • Some changes to the keyboard layout. Shift+STOP now generates expansion token 80h, not a literal Control-C; and Alt+STOP is ignored, rather than generating ESCape.
  • Some tweaks to paper feeds in the dot-matrix printer driver.
PCW 1.4: 1985
The most common version for the 8256/8512, supplied with many of the machines, and was used to provide illustrations for the manual.
  • Change to the dot-matrix printer font: The " and ' characters are chisel-like, not curly.
  • The CPS8256 can now be driven in interrupt mode.
  • The floppy driver uses a semaphore to signal completion, not a simple on/off flag.
  • There is a kernel panic (RST 30h) which is invoked if the kernel runs out of memory or a process descriptor gets corrupted.
PCW 1.5: ~1985
This has only been seen in localised form for the USA - although it signs on as 1.5, the version number returned by CD VERSION is still 1.4. The only differences from stock 1.4 are data changes giving a modified keyboard layout and dot-matrix printer settings for the US, and changing the spelling of 'disc' to 'disk'.
PCW 1.7: 1986
  • New World memory layout.
  • New escape code in the terminal emulator: ESC @ (insert character)
PCW 1.7H: 1986
Supplied with the ASD HD20 hard drive.
  • Includes code to load external device drivers (*.FID)
  • In all other respects identical to 1.7.
PCW 2.1: 1987
Supplied with the PCW9512.
  • Support added for the PCW9512 and its daisywheel printer.
  • Does not include code to load *.FID drivers.
  • Floppy drive support altered to support a system with a 720k drive A:. Also some tweaks made to floppy interrupt handling, perhaps to cope with slight hardware differences.
Spectrum 1.0: 1988
My best guess is that +3 CP/M was derived from the PCW CP/M codebase somewhere around here.
PCW 2.9: 1989
  • Now supports the PCW9512 sheet feeder.
  • FIDs now enabled again.
  • The "Drive is A:" message moves four characters to the left (!)
  • New ESC 4 1 / ESC 4 0 sequences to disable / enable screen scrolling.
PCW 1.11 / 2.11: 1991
Supplied with the PcW 9256 (1.11) and the PcW 9512+ (2.11).
  • Somewhere between 1.7 and 1.11, the 1.x and 2.x version numbers were synchronised. This probably means some 2.x versions were skipped. My guess is that 1.7 corresponds to 2.0, 1.8 to 2.1, and the sync was done at 1.9 = 2.9.
  • The dot-matrix printer driver allows redefinition of arbitrary characters, using jumps at FFF9h and FFFCh.
  • Support added for second-generation PCWs: 9256, 9512+.
  • Support added for booting the LocoScript installer (insert the boot floppy and press SHIFT+EXTRA+RELAY. Only applies to second-generation PCWs).
  • Allows the floppy drives to be stepped at different rates.
PCW 1.12 / 2.12: 1992
Supplied with the PcW10.
  • Support added for the standalone Centronics port accessory.
PCW 1.14 / 2.14: 1993
  • Support for loading drive parameters from *.FIB files.
  • Improved Centronics port detection.
  • If the memory test reports 256k, reset the memory read/write register and try again.
  • SHIFT+EXTRA+RELAY now works on first-generation PCWs as well.
PCW 1.15 / 2.15: 1993
  • A single change: When doing a SHIFT+EXTRA+RELAY reboot, takes all devices out of interrupt mode so that they don't interrupt at an awkward moment.

Some versions I haven't got, but which probably existed:

The modules in other systems

At least some of the modules are shared between CP/M and other Amstrad / Locomotive operating systems. LocoScript 1.20, for example, has its own versions of NMI, KL and DD, and so does the LocoLink server for the PCW. The main difference is that another module or set of modules (probably called "DOS") handles file access. The code in this module resembles +3DOS on the Spectrum +3 — or, rather, +3DOS was created from DOS, DD, and a few bits of MSG (and modified not to use the IY register, since on the Spectrum that's used for other purposes).

In LocoScript 1, at least, DOS appears to be split into two modules: one handling file operations (open, close, read, write etc) and the other abstracting the floppy drives and RAMdisc into generic block devices. Unlike CP/M it only supports three devices, hardcoded as A: B: and M:.

I suspect that SCR is also shared with LocoScript, since it has at least one feature (right-to-left text output) which is completely unused in CP/M.

The LocoLink server in LocoLink 2.02 is somewhere between versions 2.9 and 2.11 of the CP/M BIOS; it supports separate drive parameters for each drive, but not the Shift+Extra+Relay boot sequence.


John Elliott, 9 May 2018