Torch CPN
CPN (with no slash) is a CP/M workalike distributed by Torch Computers, usually found running on a Z80 plug-in processor for the BBC Micro. It stands for "Control Program Nucleus".
While an Internet search can locate a fair amount of information about it, it's not all in easily accessible formats. So this is my attempt to gather together the points of interest to me.
Peripherals
The Torch card relies on its host BBC micro for most I/O. The Z80 I/O ports are listed at TorchIO:
00h-07h: Communication with the host / boot ROM paging (bit 2 of address is 0 to page ROM, 1 to page RAM). 00h / 04h: Send to BBC host 01h / 05h: Receive from BBC host 02h / 06h: Status. Bit 7 set if host is ready to receive. Bit 1 is set if host is ready to send. 10h-17h: Z80 SIO 18h-1Fh: Serial port baud rate setting
Boot ROM
On startup, CPN is loaded from an 8K ROM - usually version 1.02 (CCCP102). This copies itself to RAM and then pages itself out.
At offset 6 in the ROM is a pointer to a table of blocks, each one formed:
DB type ;Block type DW length ;Length of block DW address ;Destination address in Z80 memory DB ... data ...
Block types are:
00h Operating system code (BIOS / BDOS / CCP equivalents) 01h Zero page / jump blocks 0FEh Version text 0FFh End of table
API
The CPN API is patterned on CP/M 2, but with some differences:
BDOS
There is an additional method of calling the BDOS - use RST 30h
with the function as an inline parameter rather than passed in C.
The BDOS entry point is at 0F840h, where on real CP/M it would be at xx06h.
The following BDOS functions are worthy of note:
- C=03h (A_READ)
- On CP/M this reads auxiliary input (eg, a paper tape reader or serial port). On CPN this reads console input without parsing or echoing the character returned.
- C=04h (A_WRITE)
- On CP/M this writes to auxiliary output (eg, a paper tape punch or serial port). On CPN this writes to the console without interpreting the output character (so tabs are not expanded, output is not echoed to the printer, and escape codes are not parsed).
- C=06h (C_RAWIO)
- This function has CP/M 3 semantics:
- When called with E=0FFh it polls for input and returns 0 if there is no character, or the character if there is one.
- When called with E=0FEh it returns console input status.
- When called with E=0FDh it waits for a character and returns it without echoing it.
- C=07h / 08h (get / set IOByte)
- CPN does not implement the I/O byte. These calls successfully retrieve and store the byte at address 3, but that byte does not control where input comes from or output goes.
- C=0Eh (DRV_SET)
- The drive selected by this function is stored at address 4, which on a real CP/M 2 system holds the CCP current drive and user number.
- C=1Bh (DRV_ALLOCVEC)
- The allocation vector returned is a copy, stored in a temporary buffer.
- C=1Fh (DRV_DPB)
- A single fixed DPB is returned, corresponding to a drive with 32 128-byte sectors per track, 16k blocks, 257 data blocks and 256 directory entries.
- C=20h (F_USERNUM)
- According to the manual, user number can be 0-31.
- C=28h (F_WRITEZF)
- Is identical to function 22h (F_WRITERAND)
In addition to the CP/M 2 functions, CPN also implements functions numbered 6Bh-7Fh. These appear to be used for TORCHNET operations.
BIOS
The BIOS jump block is at address 0FFCCh, so unlike a real CP/M system it is not page aligned. It is followed (at 0FFFFh) by a single byte which is documented as the CPN internal version number.
The following functions are not implemented: SETTRK, SETSEC, READ, WRITE and SECTRAN.
Before the BIOS jump block, at address 0FFB4h, are some additional jumps:
FFB4 JP CCCPCS ;Internal use only, forwards to BOOT in 1.02 FFB7 JP TubeStat ;Get tube status. ;Bit 0 of A set if OK to send character. ;Bit 2 of A set if character waiting to be read. ;Carry matches bit 0 of A ;Zero is inverse of bit 2 of A FFBA JP CInstall ;For internal use by the command processor. FFBD JP LdFile ;Loads and executes a COM file. ;Enter with BC = FCB address. Returns on ;failure with A=error code FFC0 JP UsrImm ;Sends 0Fh to the host system (User call) ;followed by the byte after this call. eg: ; ; CALL 0FFC0h ; DB 10h ; FFC3 JP PutImm ;Send the byte after this call to the host FFC6 JP GetByte ;Wait for a byte from the host and return it in ;A. FFC9 JP PutByte ;Send the byte in C to the host.
Internal API
Most of CPN is implemented on the host BBC computer, with the Z80 system mainly providing wrappers around host functionality. For example, the F_OPEN call is implemented by sending byte 24h to the host, followed by 13 bytes of FCB. The host replies with a result byte and (if successful) 15 bytes of updated FCB.
In FCBs sent to the host, the first byte is 00-0Fh for the drive number rather than the usual CP/M values of 01-10h. The top bit of the drive number is set if the first byte of the passed FCB was '?'.
The commands sent to the host include:
00 Reboot (triggered by BIOS BOOT call). 01 nn Send byte to printer 02 fcb Open file (12-byte FCB). 03 fcb Close file. Followed by 12 bytes of FCB. 04 fcb Search first (12-byte FCB). 05 Search next 06 fcb Delete file. Followed by 12-byte FCB. Returns 1 byte result. 07 Read record 08 Write record 09 Create file (12-byte FCB). 0A Rename file. Followed by 12-byte FCB, then zero, then 11-byte new name. Returns 1 byte result. 0B Set file attributes. Followed by 12-byte FCB. Returns 1 byte result. 0C Get file size. Followed by 12-byte FCB. Returns 1 byte status / high byte of record count. If bit 7 of status not set, a word follows (low word of record count). 0D Read host memory. Followed by a word giving address. Returns a byte. 0E Write host memory. Followed by a word giving an address and a byte with the new value. 0F nn Execute user function. Followed by function number. 10 Get/set user number. Followed by one byte (user number or 0FFh). If byte was 0FFh, returns one byte (user number). 11 Poll keyboard. Returns 1 byte (status) and then one byte of data if status was nonzero. 12 Select input device. Followed by a byte: 0 Keyboard 1 Serial port 2 Keyboard and serial port 13 Select output device 0 Screen 3 Serial port 14 Call communications address. Followed by a command byte. 0 Poll interface 1 Interrupt 15 nn Send byte to console 16 Query keyboard status. Returns 1 byte of status. 17 Get extent size. Pass a file handle and an extent number. The number of records in that extent will be returned. 18 fcb Search first. Followed by 13 bytes of FCB. 19 fcb Search next. 1C Get disc configuration. Followed by a drive number. Returns 1 byte result code. If it is 0, a configuration word follows: Bits 3-0: Physical sectors per track Bits 13-4: Tracks Bit 14: Set if drive is a network drive Bit 15: Set if drive is a fixed disc. 1D fcb Create file. Followed by 13 bytes of FCB. The first byte (drive) is 00-0Fh for drive A:-P:. Returns a result byte; if that is zero, then 15 bytes of opened FCB follow. 1E Torchnet function. 24 fcb Open file. Followed by 13 bytes of FCB. The first byte (drive) is 00-0Fh for drive A:-P:. Returns a result byte; if that is zero, then 15 bytes of opened FCB follow. 28 Make drive read-only. Followed by one byte, 0-0Fh. 29 Get drive read-only bitmap. Returns two bytes.
User functions sent by command 0Fh include:
00 Read sector 01 Write sector 02 Format track 03 Select disc 05 Get escape status 06 Copy 40 bytes 07 Get version number 08 Format disc 09 Unslave disc caches 0A Set debug status 0B Get character definition 0C Call OSWORD 0D Read scratchpad 0E Write scratchpad 0F Call OSBYTE 10 Toggle printer status 11 Get printer status. Returns 1 byte. 12 Get retry count 13 Reset drives. Takes 2 byte (bitmap of drives to reset). 14 Get drive allocation vector. Takes 1 byte (drive number). Returns 32 bytes. 15 Call host processor function. 16 Call host processor subroutine. 17 Reset handle table (close all files). 18 Get boot control byte.
Filesystem
The on-disc format of the filesystem used by CPN differs considerably from that used by CP/M.
The manual describes the filesystem on a floppy disc with 80 cylinders, 2 heads, 10 256-byte sectors. Cylinders are mapped to tracks in 'alternating sides' order, as on a PC. Since the BBC Micro normally uses 'out and out' order, you may well encounter Torch disc images online where the tracks are in non-obvious order.
Internally, sectors are numbered (track << 4) | (sector). This means that on a disc with fewer than 16 sectors per track, the numbers will not be contiguous. For example, the directory occupies the first 16 sectors of the disc but these are numbered 00h-09h and 10h-15h (track 0, sectors 0-9 and track 1 sectors 0-5).
As mentioned, the first 16 sectors contain the directory. Each entry is 16 bytes:
DW allocation ;0000h : End of directory ;0001h-03FFFh : Sector number of L3 block ;8001h-0BFFFh : Sector number of L2 block ;0FFFFh : Empty entry DW length ;File size in records DB user ;User number DB filename ;In CP/M FCB format. Attributes on high bits ;per CP/M, except that the 'Archive' attribute ;is replaced with 'Execute only'.
Files of up to 255 records store their allocation in an L3 block. This contains 128 words, giving the sector numbers containing those records.
Files of 256 or more records store their allocation in an L2 block. This contains 128 words, each giving the sector number of an L3 block describing that section of the file. This system should be familiar to those who have studied UNIX / Minix filesystems.
The use of a 14-bit sector number in the allocation field suggests that the absolute maximum size of a CPN filesystem is 4Mb (16384 256-byte sectors).
The directory is followed by two sectors of allocation bitmap, containing 256 words for 256 tracks. Bit 0 of each word corresponds to sector 0 of the track, bit 1 to sector 1, and so on. Sectors that don't exist (such as sectors 10-15) will have the corresponding bit set to 1.
The largest filesystem that could be supported by an allocation bitmap of this size is 1Mb: 256 tracks of 16 sectors.
Following the allocation bitmap is a sector containing test data, and another sector containing "various other information (see Torch Systems Department document Sys-B-02)". The remainder of the disc then contains the data blocks referred to by the directory.