CP-MAGIC: Nibbling Away at CPM

Murray Arnow/Call -A.P.P.L.E. staff writer
June 1983 / PP 49-52

Apple CP/M is probably the least documented of the operating systems available to Apple users. With this in mind, I hope to share some of the information I have been able to gather on the 2-80 Soft Card and the associated Microsoft implementation of CP/M. Before I begin, let me urge the reader to refer to the series of articles by Gregg Tibbetts in Softalk magazine titled SoftCard Symposium. The latter series is a mine of information if the reader has the patience to pursue the clues on the internal CP/M workings so enticingly strewn about by Mr. Tibbetts.

Probably the most confusing aspect of CP/M to individuals who are familiar with Apple DOS is the way the disk is accessed. This article will attempt to give some insight into CP/M disk 110 through descriptions and examples. CP/M was first created to run on standard IBM 8″ floppy disks. These disks have 77 tracks with 26 sectors per track. Each sector contains 128 bytes, as opposed to the Apple disks whose sectors contain 256 bytes. To maintain compatibility between the CP/M versions and the various CP/M implementations, all disk operations are performed using a “logical sector” containing 128 bytes. Since most 5.25″ disks use 256 byte sectors, the BIOS (basic input/output system) must make the translation between the “physical” and logical sectors.

A historical artifact appears on CP/M disks. The disk when formatted has the empty sectors filled with E5’s. You may recall that when Apple DOS formats a disk the empty sectors are filled with the null character 00. The E5 of CP/M is the IBM EBCDIC symbol for a space. CP/M uses E5’s in other areas as null characters.

As mentioned above, CP/M reads and writes to the disk in terms of 128 byte logical sectors. The Apple disk, however, is formatted in 256 byte physical sectors. The way that the Apple CP/M BIOS handles this is to use 2-80 code to manipulate the logical sectors and do the physical sector disk 110 in 6502 code; i.e., the RWTS subroutine is written in 6502 code. The RWTS subroutine in all the versions of Microsoft CP/M is located at $E03 (Note: addresses used by the 6502 will be prefixed by a $; addresses used by the 2-80 will be suffixed by an H, such as, OFF380H). The CP/M RWTS subroutine is very similar to the one used by DOS 3.3 with the major difference in the sector skewing.

Sector skewing, also called sector interleaving, is a method to reduce the time of disk I/O. Some time is required after a sector is read to translate the encoded disk nibbles and store the results in the memory. If the sectors were sequential, the disk will have passed the next sector to be read resulting in one disk revolution for each sector reading. By changing the sector sequence such that when the computer has completed storing the data from the previous read, the disk head is over the next sector to be read, more than one sector can be read before the disk makes one revolution. Skewing also reduces disk write time because before data can be written to a sector it must be encoded, another time consuming process. Skewing permits the disk head to be over the next sector to be written to before the disk makes one revolution. Apple disks are formatted with the physical sectors appearing sequentially on the disk. The sector skewing is done in software. This means that if you ask DOS 3.3 for sector 1, you will actually be reading physical sector 7. The CP/M RWTS subroutine will give you physical sector 8 if you ask for sector 1. Table 1 relates the variety of Apple sectoring.

Apple CP/M uses the RWTS subroutine directly in warm and cold boots. After booting, the RWTS subroutine is used as part of a more complex disk operating system. A detailed description of how to use the CP/M RWTS subroutine for disk 110 is included in the program CPMRWTS.

CPMRWTS is intended for instructional purposes and is not recommended for use in disk access. The reason is that Microsoft is under no obligation to locate the RWTS subroutine at $E03 or are they obligated to maintain its structure m later CP/M versions. This is no real problem because direct disk access can be attained through the BIOS entry points. These entry points are standardized between all CP/M versions, and there is no need to fear alterations. CP/M uses the BIOS entry points when doing standard disk 110 to the data tracks. A description of the subroutines used follows:

  • SETDMA: The DMA (disk memory access) address is set. This is the 128 byte read/ write buffer.
  • SELDSK: The drive is selected for the next 110.
  • SETTRK: The disk track is selected for the next 110.
  • SETSEC: The disk sector is selected for the next I/O. The sector is usually the physical sector, but for the case of Apple CP/M it is the logical sector.
  • SECTRAN: This subroutine calculates the skew relating logical to physical sectors. The input of a logical sector results in the output of the appropriate physical sector for use in SETSEC. For the Apple CP/M, the output is the same as the input.
  • READ: The selected track/ sector is read from the disk in the selected drive to the DMA buffer.
  • WRITE: The DMA buffer is written to the selected track/sector on the disk in the selected drive.

A more complete description of the BIOS entry points may be found in the CP/M Alteration guide available from Digital Research.
Before giving an example of disk 110 using the BIOS entry points, a brief description of the disk formatting and the internal operations of the READ and WRITE subroutines will be found useful. Apple CP/M track numbers are identical to the DOS 3.3 track numbers, but the sector numbering needs some elaboration.

There are 32 logical sectors on each track. The even numbered logical sectors occupy the first 128 bytes of a physical sector, and the odd numbered logical sectors occupy the last 128 bytes of a physical sector. Table 1 details the logical sector relations. We know that the CP/M RWTS subroutine does the I/O to the physical sectors. We also know that the RWTS 110 buffer contains 256 bytes and that the logical sectors contain 128 bytes. It is extremely inefficient to require a physical read or write each time we wish to read or write to a logical sector. Since the RWTS 110 buffer after a read contains two consecutive logical sectors, the procedure used by the BIOS READ subroutine is to perform a physical read only if the previous read was not from the same physical sector. If the read was from the same sector, the appropriate RWTS 110 buffer half is transferred as a logical sector to the DMA buffer. In any case a call to READ will put the appropriate logical sector in the DMA buffer. The problem is even more complicated when we wish to write a logical sector.

CP/M 2.xx requires that a protocol be followed by the BIOS so that the BDOS (Basic Disk Operating System) can use a very efficient disk write procedure. The protocol states that prior to calling the subroutine WRITE the 2-80 C register should contain one of the following values: C = 0 if the write is to a previously allocated sector or to a sector other than the directory area or the first sector of an unallocated block; C = 1 if the write is to a directory sector; C = 2 if the write is to the first sector of an unallocated block. A block (or group) in the case of Apple CP/M is 8 logical sectors. The protocol tells the BIOS what bookkeeping system to use. This avoids unnecessary pre-reads to fill half the 110 buffer and unnecessary writes if the I/O buffer doesn’t have to be cleared. In simpler terms, when the C register contains a 0 or a 2, the write operation may be delayed until another write to a different logical sector is requested, and when the C register contains a 1 (or an odd number in the Apple CP/M), the DMA buffer is written immediately to the disk.

The BIOS entry points are found by first locating the table of “jump vectors.” The jump table is pointed to by the “warm boot” jump at location 0000H. The jump table, see Table 2, starts at location ]MPTBL which for CP/M 2.xx versions is always at a memory page boundary. For the case of the 60K memory configuration of CP/M version 2.23, JMPTBL has the value 0FA00H.
Standard CP/M protocol requires that the track and sector subroutines must be called prior to each READ or WRITE. Some CP/M systems make a call to SETTRK and SET SEC optional after the first call if the sectors to be accessed haven’t changed from the previous I/O command, Apple CP/M is such a system.
Not all CP/M systems require a call to SECTRAN to translate logical to physical sectors. The Apple skew is calculated in the RWTS, READ, and WRITE subroutines; hence, a call to SECTRAN is not required. Rules governing disk access for CP/M systems not requiring SECTRAN are as follows:

Not all CP/M systems require a call to SECTRAN to translate logical to physical sectors. The Apple skew is calculated in the RWTS, READ, and WRITE subroutines; hence, a call to SECTRAN is not required. Rules governing disk access for CP/M systems not requiring SECTRAN are as follows:

  1. The read/write buffer should be selected by placing the buffer address in register BC and calling SETDMA. The buffer space requires 128 bytes.
  2. The disk drive should be selected by loading register C with the drive value, 0 for A:, 1 for B:, etc., and calling SELDSK.
  3. The track is selected by placing the track number in register BC and calling SETTRK. Apple CP/M requires the track to be in register C only, register B is ignored.
  4. The logical sector is selected by placing the sector number in register BC and calling SETSEC. Apple CP/M requires the sector to be in register C only, register B is ignored.
  5. Calling READ reads the selected sector from the selected drive to the read/write buffer. After a read an error code is stored in register A; 0 = no errors; 1 = non-recoverable error occurred.
  6. Calling WRITE writes the contents of the read/write buffer to the selected sector and drive. The error codes stated for READ apply.

I’ll close this article with the program TURNKEY which demonstrates the use of the CP/M disk 110 routines. To use TURNKEY type TURNKEY command; for example, TURNKEY DIR B:. The disk in drive A: is updated to perform the command upon a cold start. The example will have the directory of the disk in drive B: displayed when the system is booted.

. . . about the author

Murray Arnow is a physicist who has a somewhat unconventional work history. His time has been shared nearly equally between university appointments and industrial positions. While at the university his responsibilities included teaching physics and doing experimental research in an area of atomic physics which didn’t seem to hold a great deal of interest for most of his colleagues. Interleaved with his academic appointments, he has worked for companies which make military radar, color picture tubes and CAT Scanners, the latter product being associated with his current position.
The nature of Arnow’s work frequently requires that data be collected and analyzed. This often means that he has to build and interface a variety of electronic equipment to fill this need. He has interfaced the Apple to devices ranging from point plotters to oscilloscopes to aid in the collection and analysis of data.
A large portion of his spare time is spent fiddling with the Apple. In the process he has become quite well acquainted with both DOS and CP/M Arnow also finds himself becoming more dependent on the Apple as a text processor for both correspondence and technical paper writing.

Table 1

Apple Sector Skewing Relations
CP/M Logical DOS 3.3 Physical

CP/M RWTS
Sectors	Sectors 	Sectors 	Sectors

0	00,01 	0 	0
1	0C,0D 	E 	8
2	18,19 	D 	1
3	04,05 	C 	9
4	10,11 	B 	2
5	1C,1D 	A 	A
6	08,09 	9 	3
7	14,15 	8 	B
8	16,17 	7 	4
9	02,03 	6 	C
A	0E,0F 	5 	5
B	1A,1B 	4 	D
C	06,07 	3 	6
D	12,13 	2 	E
E	1E,1F 	1 	7
F	0A,0B 	F 	F

Table 2

Jumptable to BIOS entry points.

JMPTBL    JMP BOOT 	;Cold start.
JMPTBL+3  JMP WBOOT 	;Warm start.  This is the location
			    ;jumped to from 0000H.
JMPTBL+6  JMP CONST 	;Console status check.
JMPTBL.9  JMP CONIN 	;Console input.
JMPTBL+12 JMP CONOUT 	;Console output.
JMPTBL+15 JMP LIST 	;List device output.
JMPTBL+18 JMP PUNCH 	;Punch device output.
JMPTBL+21 JMP READER 	;Reader device input.
JMPTBL+24 JMP HOME 	;Home selected disk drive head.
JMPTBL+27 JMP SELDSK 	;Select disk drive.
JMPTBL+30 JMP SETTRK 	;Set track number.
JMPTBL+33 JMP SETSEC 	;Set sector number.
JMPTBL+36 JMP SETDMA 	;Set disk memory access address.
JMPTBL+39 JMP READ 	;Read a selected sector.
JMPTBL+42 JMP WRITE 	;Write to a selected sector.
JMPTBL.45 JMP LISTST 	;Return the status of the list device.
JMPTBL+48 JMP SECTRAN	;Translate logical to physical sectors.

About the Author