Sometime in the first quarter of 2006 i tried to recover my old
Prof80 CP/M Z80 System. After i had the system up ad running from
floppy disk i thought about adding a harddisk to it. Till Rehmann
designed an universal ide interface
for Z80 systems. It was implemented as an adapter that fits into a
z80 cpu socket but i wanted to have a card for the ecb bus. The
original design used pal's but i dont have a programmer for pals. I
found another implementation where the original schematic was used to
program a xilinx cpld (9572). I thought i could use this one without
modifications but it did not work for me, the access to the 16 bit data
of the ide disk was not working.
Ok, why not implementing the stuff in vhdl ? I never worked with
vhdl before but the language did not look very complicated. After some
evenings i had the first code that worked fine in the emulator and i
tried it on real hardware. It worked, but only sometimes. Up to now, i
dont really understand it. With help from vhdl gurus on mikrokontroller.net i got
tips that spikes may produce this behavior, after inserting some
latches it finally worked with tillmanns ide test program written in
turbo pascal. (VHDL Source for viewing or Download)
The next step was writing a CPM3 bios and a loader. I decided to use some kind of partition table and a master table on the disk. The master boot record contains the boot code, data about the disk geometry and a partition table. The first sector of the disk is read without initializing the disk to a specific geometry, hope that this works with all disks (i tried 2 or 3 drives and it worked).
Structure of the first sector
Offset | Length | Contents | Description |
0000 | 170 | Boot Code | |
0170 | 3 | 'CPM' | Id |
0173 | 6 | Reserved, filled with zeros | |
0179 | 2 | Disk number of tracks | |
017B | 1 | Disk number of heads | |
017C | 1 | Disk number of sectors per track | |
017D | 1 | Boot Partition Number | |
017E | 2 | Start track of Partition 0 (always head 0,Sector 1) | |
0180 | 2 | Partition 0 length in tracks | |
0182 | 2 | Start track of Partition 1 (always head 0,Sector 1) | |
0184 | 2 | Partition length in tracks | |
0186 | 2 | Start track of Partition 2 (always head 0,Sector 1) | |
0188 | 2 | Partition length in tracks | |
018A | 2 | Start track of Partition 3 (always head 0,Sector 1) | |
018C | 2 | Partition length in tracks | |
018E | 2 | Start track of Partition 4 (always head 0,Sector 1) | |
0190 | 2 | Partition length in tracks | |
0192 | 2 | Start track of Partition 5 (always head 0,Sector 1) | |
0194 | 2 | Partition length in tracks | |
0196 | 2 | Start track of Partition 6 (always head 0,Sector 1) | |
0198 | 2 | Partition length in tracks | |
019A | 2 | Start track of Partition 7 (always head 0,Sector 1) | |
019C | 2 | Partition length in tracks | |
019E | 2 | Start track of Partition 8 (always head 0,Sector 1) | |
01A0 | 2 | Partition length in tracks | |
01A2 | 2 | Start track of Partition 9 (always head 0,Sector 1) | |
01A4 | 2 | Partition length in tracks | |
01A6 | 2 | Start track of Partition 10 (always head 0,Sector 1) | |
01A8 | 2 | Partition length in tracks | |
01AA | 2 | Start track of Partition 11 (always head 0,Sector 1) | |
01AC | 2 | Partition length in tracks | |
01AE | 2 | Start track of Partition 12 (always head 0,Sector 1) | |
01B0 | 2 | Partition length in tracks | |
ß1B2 | 2 | Start track of Partition 13 (always head 0,Sector 1) | |
01B4 | 2 | Partition length in tracks | |
01B6 | 2 | Start track of Partition 14 (always head 0,Sector 1) | |
01B8 | 2 | Partition length in tracks | |
01BA | 2 | Start track of Partition 15 (always head 0,Sector 1) | |
01BC | 2 | Partition length in tracks | |
The following 4 entries are only for linux compat | |||
this should allow reading the first 4 partitions under linux | |||
01BE | 10 | ||
1 | Boot | 1 byte Boot Indicator | |
1 | head | 1 byte 0..FF | |
1 | sector | upper bits for track, 6 bits sector (0..63) | |
1 | track | lower 8 bit of track | |
1 | sysid | 52h for CP/M | |
1 | headend | ||
1 | secend | ||
1 | trackend | ||
4 | relstartsec | ||
4 | relsecs | relative length in sectors | |
01CE | 10 | drive | |
head | 0..FF | ||
sector | upper bits for track, 6 bits sector (0..63) | ||
track | lower 8 bit of track | ||
sysid | |||
headend | |||
secend | |||
trackend | |||
relstartsec | |||
relsecs | relative length in sectors | ||
01DE | 10 | drive | |
head | 0..FF | ||
sector | upper bits for track, 6 bits sector (0..63) | ||
track | lower 8 bit of track | ||
sysid | |||
headend | |||
secend | |||
trackend | |||
relstartsec | |||
relsecs | relative length in sectors | ||
01EE | 10 | drive | |
head | 0..FF | ||
sector | upper bits for track, 6 bits sector (0..63) | ||
track | lower 8 bit of track | ||
sysid | |||
headend | |||
secend | |||
trackend | |||
relstartsec | |||
relsecs | relative length in sectors | ||
01FE | 2 | 55h AAh | Linux compat |
The table may contain up to 16 CPM partitions. To allow accessing
the first 4 partitions from linux, there are compat fields but they are
not yet filled. I wrote a simple fdisk in pascal that can create the
partition structure.
The boot process works as follows:
The first sector of a partition contains the disk format and the cpm
loader sits after this table:
Offset | Length | Contents | Description |
000 | 6 | 'CPM3AD' | Signature |
006 | 2 | SPT | total number of 128-byte logical records per track (all heads) |
008 | 1 | BSH | Data allocation block shift factor |
009 | 1 | BLM | Block mask |
00A | 1 | EXM | Extent mask |
00B | 2 | DSM | one less than the total number of blocks on the drive, less than or equal to 7FFFH |
00D | 2 | DRM | number of directory entries minus one |
00F | 1 | AL0 | reserved directory blocks |
010 | 1 | AL1 | |
011 | 2 | CKS | 8000h = fixed media, size of the directory check vector |
013 | 2 | OFF | reserved tracks, should be 1 |
015 | 1 | PSH | physical record shift factor |
016 | 1 | PHM | physical record mask |
017 | 9 | Reserved, Zeros | |
020 | 1 | Description Length | |
021 | 14h | Description, max 20 chars |
As you can see, this is the dpb used by cpm3. The ide bios sets the
dpb from this table when the login function is called from cpm. There
are some maximums that can be set, see ide.z80 for details. Btw, i
converted most of the files to z80 and i'm using the slr assembler.
Currently i'm using the loader from DR, i also tried ZPM3LDR but it did
not work ?