WACCI 138 Index - Home Page www.wacci.org.uk


01 - Thanx & Stuff 02 - Fair Comment 03 - A to Z of the CPC 04 - From the Archives
05 - Twenty Questions 06 - Keyboard Scanning 07 - Meet the Relatives 08 - Programmers' Patch
09 - Genesis 10 - Famous Last Words

img_138_08_01.gif - 2,315 bytes

Programmers' Patch

In a change to the advertised programme, Matthew Phillips examines exactly how the CPC stores information on a disc

I promised that we would turn our attention to disassemblers this month, but I am afraid you are going to be disappointed (or pleased, depending on how you feel about disassemblers) as I am going to leave that for another time.
  Looking through my old WACCIs, I don't remember many articles looking at discs from the technical point of view. Issues 128 and 129 (approximately March and May 1999) have a couple of articles by James Hoskisson on the chip which controls the disc drive, the FDC, but what I want to look at is how the data is actually arranged on the disc, and how we program the operating system or the FDC to get the data on and off the disc.

History in the reworking

The Amstrad CPC and PCW series use a disc format compatible with CP/M. In the case of the CPC series, Amsdos is compatible with CP/M 2.2, whereas the PCW uses CP/M Plus (also known as CP/M 3.1). CP/M Plus has facilities for (very insecure) password protection of files, as well as datestamping which is more sophisticated than that on Microsoft Windows! You can use these facilities from CP/M Plus on a CPC as well, but you may then have problems using the discs from Amsdos.
  CP/M was the first standard operating system for microcomputers, but unfortunately its disc formats were far from standardised. There are hundreds of different incompatible disc formats around. If you want to see the range, get hold of a copy of 22DISK for the PC (WACCI PD disc 42). The demo version supports about 150 formats, whereas the full version supports over 400 (and there are several more that I know of, even then). Unless you are extremely lucky, none of these formats will work in your Amstrad CPC computer, even if you were given examples on a 3-inch disc.
  The reason for this incompatibility is that while CP/M specifies the logical arrangement of the data on the disc, it does not require computer manufacturers to use any set physical arrangement. Let me explain the difference.

The word of Amstrad

An Amstrad CPC data format disc can hold 180K. In a logical sense, we can just regard the disc as being a sequence of 184320 bytes. CP/M stores information in the directory of the disc so that it knows that a file occupies bytes 2048 to 4095 inclusive, for example. Physically, however, the data is chopped up into sectors, and stored on different tracks on the disc. The standard part of the CP/M operating system, written by Digital Research, does not know what sector numbers and tracks to look for. These are specified in the part of the BIOS which is provided by the computer manufacturer.
  As a result, most manufacturers had a go at providing their own incompatible formats. Even Amstrad, fairly late in the CP/M scene, managed to provide their PCW computers with a disc format which cannot be recognised by the CPC! To be fair, there was a good reason for this, which I may go into later.

Disc structure

The recording surface on a disc is organised into tracks. There is a read/write head in the disc drive (a bit like the head in a tape recorder) which can be moved in and out, nearer to the centre of the disc or further towards the edge. It is moved by a stepper motor a set distance each time.
  As the disc spins, the tracks form concentric circles covering the magnetic surface. In 3-inch drives used with CPCs, the head can be stepped into forty positions (actually, forty-two generally) and so there are forty tracks on a disc, numbered in true programming practice from 0 to 39.
  Later disc drives, like the second drive of the PCW 8256, or any drive on a PCW 9512 or a modern PC, have a more accurate recording head which is stepped at half the distance, to give eighty tracks. They also have two heads, to allow the computer access to both sides of the disc without turning it over.
  When a disc is formatted, the computer divides each track into sectors. Each sector includes a four-byte identifier, whose values are conventionally referred to as C, H, R, and N:

C - is the number of the track (also known as the cylinder number)
H - is the number of the side of the disc (the head number)
R - is the number of the sector
N - is the size of the sector

The C and H values are not quite as obvious as they look. They will usually be set up as one would expect, so that the ID of each sector on track 6 of side 0 has a C value of 6 and H equal to 0. You can be cunning though, and format sectors so that the C and H values in the ID do not correspond to the physical location of the sector on the disc. The values are stored in the ID mainly to help the disc drive controller chip, because with older drives the stepper motors may not position the head accurately every time. By checking the values in the ID, the disc drive controller could see whether the motor had done its job properly, and if not, instruct it to step the head again.

img_138_08_02.gif - 5,690 bytes

Exceptions to the rule

Funny values of C and H are mostly used in copy protected discs, as they stop simple disc copiers from working. You will also come across the situation with a 3.5" disc formatted on each side using a side switch. Although all 3.5" drives have two heads, Amsdos cannot make use of them. The side switch forces the drive to access the other side of the disc, but the computer still thinks it is the first side, so when you format side two, the H values being laid down will be zero, just like those on side one. With some clever code, it is possible to read both sides of one of these discs without switching the side switch, but on most computers you will have to instruct the disc controller chip directly to achieve this.
  The number of the sector is referred to as R (I don't know what that stands for). (Record, I think. - Richard) It can be any value from 0 to 255. The sectors on a track needn't be numbered in any particular order. In fact you can format a track so that more than one sector has exactly the same number, though this makes reading the correct sector extremely difficult.
  Finally, N represents the size of the sector:

0	128 bytes
1	256 bytes
2	512 bytes
3	1024 bytes
4	2048 bytes

and so on...
  Mathematically, N is seven less than the base 2 logarithm of the number of bytes in the sector. Gosh, didn't you just want to know that! On the CPC and PCW, 512 byte sectors are usually used. As you can see, the smallest possible sector is of 128 bytes. This will become more significant when we look into how CP/M organises the data on the disc.

CPC disc formats

Getting down to specifics, what can we say about the formats commonly in use on the Amstrad? Firstly, Amsdos (and Parados) rely on the lowest sector number on the track to identify the physical disc format. From the lowest sector number you can then work out the number of tracks and sides you are expecting, as well as logical CP/M parameters such as the number of directory entries and the size of a block. See the table for all the details. Some of this information is taken from the Parados manual, with the information on Ultraform corrected.
  The "Extdisc formats" shown are those supported by a CP/M+ patch called (wait for it) "Extdisc" which is available on WACCI PD Disc 7. It was written by a New Zealander, and the Nigdos referrred to in two of the formats was apparently a local replacement for Amsdos.
  Sadly, there is some duplication of formats. Ultraform and Parados 41 are identical apart from the sector numbers, as are S-DOS, Romdos D40 and Parados 80. Romdos D10 and Extdisc Large formats are also structurally equivalent. (If I remember rightly, S-DOS was written to use exactly the same disc format as Romdos D80, so that files might be easily interchanged between the two. One day, I'll dig out the source and write an article about how it worked. - Richard)

In practice

In a future article we will look at how you go about working out what format a disc is from the IDs of the sectors, and also how to deal with the clashes you will observe in the table, where two different formats share the same lowest sector number. But to conclude this issue's article, we will do a little programming, and see how to read and write sectors. The assembly listing this month defines two RSX commands to make this easy, and the BASIC listing shows how to use them to read sector &C1 from the first track of a data disc in drive A.
  The commands take parameters as follows:

|RSECT,drive,track,sector,address[,bank]
- reads a sector

|WRSECT,drive,track,sector,address[,bank]
- writes a sector

drive	0 for drive A, 1 for drive B.
track 	0 to 39 for forty-track discs, 0 to 79 for eighty-track discs.
sector	the actual number of the sector, e.g. &C1
address	where to put the data in memory (or where to get it from when writing)
bank 	0 for the main 64K of RAM, 1 for the second 64K, etc.

I would advise experimenting with reading sectors to start with, and make sure you write protect any discs you are not wanting to wreck!
  The code as it stands has several failings. For one thing, none of the parameter values is checked to see that it is in range, so you must get the parameters right or you might do your disc drive damage. Secondly, no error value is returned, so you cannot tell whether the read or write has been successful.

How it works

Let us have a quick look through the code to see how it works. On loading the code, you must call the initialisation routine to register the RSXs with the operating system. The initialisation also looks up two special RSXs, which yield the addresses of the disc ROM routines for reading and writing sectors. These are stored in faraddr1 and faraddr2.
  (Actually, we do not need to be this long-winded: as far as I know the routines are in the same place in the disc ROM whether you are using a 464, 664, 6128, one of the Plus range, or indeed Parados. The BIOS READ SECTOR routine is at &C03C in ROM 7, and BIOS WRITE SECTOR is at &C03F.)
  The RSECT and WRSECT routines are very simple. First the params subroutine is called to load the parameters in from the block pointed to by IX. This subroutine looks to see whether a bank value has been given, and converts the address if necessary. The BIOS routines are called using RST 24, which takes the address of the far address from the two following bytes. See the official firmware guide, pages 19.7 and 19.8 for details of these calls (or page 62 of the alternative firmware guide).
  Next time, we will start to look at how CP/M and Amsdos organise your files on the disc itself. To explore this, you will need to have access to a sector editor. There are lots of these available. I used to use one by Martin Schroeder, published in Amstrad Action, November 1987. A listing for another was published in CWTA in August 1985. Others are available on WACCI PD discs 7 and 55. (For anyone with a secret stash of PD, I would strongly recommend the Dutch program DMon - fast, clean, easy to use, and makes a decent stab at protected discs. Of course, Xexor and later French versions of Discology are more powerful still. - Richard) I have also written one myself, which is almost fit for release.

Save your fingers

This raises an important question. In the past, we had a Programmers' Patch disc, which is WACCI PD Disc 98. This was updated every month to include the example programs from the series. WACCI PD 98 is now full, so it might be time to start a second Programmers' Patch disc.
  What do you all think about that? Is there any demand for a disc, or would you prefer me just to load the files onto my web-site? If I put them on a web-site, would you like them on a DSK image, or in a Zip file? If you have any preferences, please write in to Fair Comment and make Philip's day!
  As always, feedback is welcome on what you would like me to cover in this series. Just write in to Fair Comment or e-mail [email protected].

The BASIC listing

10 MODE 2:IF PEEK(&8500)=1 GOTO 40
20 MEMORY &84FF:LOAD"sect.bin",&8500
30 CALL &8500
40 |RSECT,0,0,&C1,&9000
50 FOR x=0 TO 31
60 FOR y=0 TO 15: PRINT HEX$(PEEK(
   &9000+x*16+y),2);" ";:NEXT
70 PRINT": ";
80 FOR y=0 TO 15:PRINT CHR$(1)+
   CHR$(PEEK(&9000+x*16+y));:NEXT
90 PRINT:NEXT

This assumes that the machine code has previously been assembled to a file called SECT.BIN.

;Disc sector read/write
ORG 8500h

; initialise RSXs
LD BC,jumps
LD HL,worksp
CALL 0BCD1h

; find disc sector reading addresses
LD IX,faraddr1
LD HL,readst
CALL 0BCD4h
LD (IX+0),L
LD (IX+1),H
LD (IX+2),C
LD HL,writest
CALL 0BCD4h
LD (IX+3),L
LD (IX+4),H
LD (IX+5),C

; end of initialisation
RET

; RSX command parameters
; |RSECT,drive,track,sector,addr[,bank]
; |WRSECT,drive,track,sector,addr[,bank]
; sector is the actual sector no., eg &C1
; bank is 0-8.  0 for internal memory,
;	        1 for extra 64K, etc.

.jumps
DEFW names
JP rsect
JP wrsect

.worksp
DEFS 4

.names
DEFB 'RSEC','T'+128
DEFB 'WRSEC','T'+128
DEFB 0

; general routines

.bankswitch
PUSH BC
LD B,7Fh
OUT (C),A
POP BC
RET

.convert
LD A,(IX+0) ;bank number
LD H,(IX+3)
LD L,(IX+2)
INC IX
INC IX
ADD A,A
JR Z,convjp
OR 31h
PUSH HL
SLA H
RLA
SLA H
RLA
POP HL
RES 7,H
SET 6,H
RET
.convjp
LD A,192
RET

; disc access routines

.rsect
CALL params
RST 24
DEFW faraddr1
JR finish

.wrsect
CALL params
RST 24
DEFW faraddr2
finish: LD A,192
JP bankswitch

; for wrsect and rsect
.params
CP 5
JR C,pjp1
CALL convert
CALL bankswitch
JR jumpparam1
.pjp1
LD H,(IX+1)
LD L,(IX+0)
.jumpparam1
LD E,(IX+6)    ; drive number
LD D,(IX+4)    ; track number
LD C,(IX+2)    ; sector number
RET

.faraddr1
DEFS 3
.faraddr2
DEFS 3
.readst
DEFB 84h
.writest
DEFB 85h

01 - Thanx & Stuff 02 - Fair Comment 03 - A to Z of the CPC 04 - From the Archives
05 - Twenty Questions 06 - Keyboard Scanning 07 - Meet the Relatives 08 - Programmers' Patch
09 - Genesis 10 - Famous Last Words

WACCI 138 Index - Home Page www.wacci.org.uk