C1581-handler

Here it is: a C1581 disk handler for Amiga 3.5" drives. Well, actually you can use it with 80-track 5¼" drives also.. c1581.lha version 1.4 (24.4.1999).


Introduction

For some time, probably three or four years I have used the so-called A64 or IEC module to connect 1541/81 disk drives to my Amiga and transfer data. I have mainly used 1581, because it is somewhat faster at writing than 1541. Still, transfering large picture files and other material is very slow with this method, because no fastloaders/savers are not used.

A big speedup can be get by using a program called read1581, which uses the Amiga's built-in 3½-inch drive to read 1581 disks. However, this doesn't help when you need to transfer data from the host system (in my case Amiga) to the C64 environment. The documentation in the read1581 (or copy1581) package mentioned that the author may later make a version which writes to 1581 disks. Unfortunately he hasn't made this kind of version, so I was encouraged to write my own.

Actually the whole thing started when I purchased a used 5¼-inch drive for my Amiga. I found a program named 1541 from aminet, which promised to read 1541-created disks with a 5¼-inch drive. However, I didn't quite get it to work, and finally read the documentation, which revealed that the disk spin rate would need to be decreased. I couldn't do this with my drive, because it does not have the appropriate trimmer capacitor (or resistor).

Adjustment was needed because the 1541 GCR disk format has four so-called speed zones, where the bit lengths are different. The length varies from 3µs to 4µs. However, this didn't stop me, because the source code was included. I used the Amiga disk hardware in 2µs mode to read from the drive at double speed and then tried to interpret the data. This was a moderate success, the program could successfully read the speed zones 0, 1 and 3 (sometimes retries were required), but still has troubles reading zone 2. The reason is a bit complicated, but basically it's because there are several possible interpretations for exactly the same input bit patterns and they are all right in different occasions. This was a fact I had to finally admit to myself. It just could not be done properly.

Anyway, fiddling with the 1541's GCR format led me to try to cure my many-year gripes about the read1851, which could only read 1581 disks. The Amiga disk hardware can read multiple disk formats because all it does is read the raw bitstream from the disk, the decoding is done in software. Whole tracks are read and written instead of single sectors. GCR or MFM, it doesn't matter, the encoding is pure software. And now I had an example source code I could experiment with.


C1581 Handler Version 1.3

How to use

Mounting on trackdisk.device unit 0 write:
mount c0

If you copy c1581-handler to L:, change the mount file accordingly. If you have a 1581 disk in the drive, this will log in the disk and it will appear on the workbench. If you didn't have a disk in the drive, you can now insert one.

This "mount c0" must be done in the directory containing the handler (if you didn't move it to L:) so that it can be located by DOS.

The c1581 handler handles the MFM decoding and encoding itself, thus mfm.device is not used. Mountfiles C0 (unit 0), C1 (unit 1) and C2 (unit 2) are provided. These are for WB2.1+, but it should be easy to add "C0:" and "#" when adding to mountlist.

Notes

Subdirectories work, i.e. you can use and create them. The format is:
mkdir «name»,«starting track»,«number of blocks»
The usual restrictions apply. It must be at least 120 blocks, the number of blocks must be a multiple of 40, the blocks must be free, and they must be inside the parent directory.

Currently you can't control the usage of disk buffers. "Addbuffers" shows the amount of memory allocated in 512-byte increments. The handler caches whole tracks, so you should expect values 1,20,40,etc. Aging is performed on the tracks, so if a track is not referenced in a while, it is flushed, I'm just not sure if the aging is fast (or slow) enough. Written data is flushed in each Close() and after 1.5-2s non-activity time.

LHA and some other compression programs have problems packing directly from the handler, because the actual file size and the size in the file info block do not match (a performance issue). Copy the files first, pack them later. The real drag about this is that LhA seems to succeed creating the packet, but actually generates a broken archive. Very nice, right?

Previously you could only format disks that were NOT 1581 disks. Now you can format any disk you want, provided you use "format quick". This is because otherwise the format command tries to use the trackdisk.device to do a low-level format first, which will fail.

Because you are beta-testing this handler (and even if you weren't), please comment on

  1. what should I tell in this file
  2. what doesn't work, but should
  3. what would be nice to have
  4. what does work..
  5. what do you use the handler for etc..

Just fearlessly mail albert@cs.tut.fi, and reveal your thoughts. Please, let me know!

The Implemented functions

I have implemented most of the packets/functions, but some are still missing. The list of implemented dospackets/functions is as follows:

ACTION_IS_FILESYSTEM, ACTION_CURRENT_VOLUME, ACTION_DIE
ok

ACTION_DISK_INFO - DiskInfo(), ACTION_INFO - Info()
Returns the last unknown action number in id_NumSoftErrors. So that you can check if something does not work because the handler does not understand a particular packet.

ACTION_FINDUPDATE, ACTION_FINDINPUT, ACTION_FINDOUTPUT - Open()
MODE_READWRITE (i.e. ACTION_FINDUPDATE) is not currently implemented to prevent accidental erasing of important files. MODE_NEWFILE (ACTION_FINDOUTPUT) also does not delete an existing file.

ACTION_READ - Read(), ACTION_WRITE - Write(), ACTION_END - Close()
You can try to read a file opened for writing, but it always fails. Empty (0-byte) files created by the handler show up as 0-block files, although they really take up one block on the disk. One may debate whether this is a bug or the correct behaviour, though (the 1581 DOS does it this way also).

ACTION_LOCATE_OBJECT - Lock(), ACTION_FREE_LOCK - UnLock()
ACTION_SAME_LOCK - SameLock(), ACTION_COPY_DIR - DupLock()
ACTION_PARENT - Parent()
ok

ACTION_EXAMINE_OBJECT - Examine(), ACTION_EXAMINE_NEXT - ExNext()
Note: in ExNext() the file size in blocks is correct, but the size in bytes is rounded up to the next 254 bytes. Because of this some programs that rely upon the size in the fileinfoblock can behave strangely. One example is LhA. In this case, copy the files first, and use these programs after that.

ACTION_DELETE_OBJECT - Delete()
Can't delete directories. Won't delete locked files. Tries to deallocate all blocks, but in case of disk errors, some blocks may be left unfreed. Only deletes properly closed files.

ACTION_SET_PROTECT - SetProtection()
Removing either delete or write permission will lock the file. Turning on both delete and write permission simultaneously will unlock the file. Currently you can't lock or unlock directories.

ACTION_FORMAT - Format()
You have to use "format quick", because otherwise the format command tries to use the trackdisk.device to do a low-level format first, which will fail.

ACTION_RENAME_DISK - Relabel()
ok

ACTION_RENAME_OBJECT - Rename()
Works, but you can't rename across directories. Ho-ho-ho, like you ever needed it really.. :-)

ACTION_SET_COMMENT - SetComment()
Not implemented. The comment field is used to show the file type.

ACTION_SEEK - Seek()
Works only when the file has been opened for reading. Is very slow if you seek from the end of the file (OFFSET_END), because the whole file needs to be read to find out the file length.

ACTION_INHIBIT - Inhibit()
Somewhat works, so that you can format disks.

ACTION_CREATE_DIR - CreateDir()
Works, with the restrictions the 1581 DOS imposes.

Changes for Version 1.4

Subdirectory creation is now possible. The format is:
mkdir «name»,«starting track»,«number of blocks»
The usual restrictions apply. It must be at least 120 blocks, the number of blocks must be a multiple of 40, the blocks must be free, and they must be inside the parent directory.

Error handling is improved. Disk removal while writing now causes a "Must replace disk" requestor.

Doslist is now properly locked for volumenode insertions and deletions. I hope this fixes the lockup problems on some OS3.1 machines. Also, Inhibit() now handles "more Uninhibits than Inhibits" scenarios better, i.e. you can mount the handler from devs:dosdrivers now. (Remember to correct the handler location in the mount file.)

I also added a separate program 1581 to read and write disk images, as adding disk image support to the handler seemed more problematic than it was worth.
1581 0 file - reads from drive 0 to file
1581 1 file write - writes to drive 1 from file

Changes for Version 1.3

The block allocation routine fixed so that it now works correctly for subdirectories also.

Changes for Version 1.2

Can read disk images by "cat c0: >ram:disk-image". The command that is used to read the data should not care about the directory entry type. ("copy" can't be used because of this, "type" can't be used because it eats NUL's.)

Performs verify on write and handles disk removal while writing or reading.

Changes for Version 1.1

File locking is now performed (Open/Close, Lock/UnLock) and checked (Open/Close, Lock/UnLock, Delete).

Real file sizes are reported when directly examining a file. i.e. "dir c0:" shows rouded-up sizes for files, while "dir c0:file" shows the actual size of "file". Unfortunately, this doesn't cure the problem with LhA and other compression programs, unless you list the files separately (lha a pack c0:file1 c1:file2) instead of using wildcard patterns.

Now returns secondary error 0 in ParentDir() call for disk root directories. Now you can change current directory to the handler in AmigaDOS shell also (i.e. you can "cd" to the handler.) and not only get strange error messages.

You can now format 1581 disks also.

TODO

Validate/repair + virtual files ($, @0: for commands etc.?)

'Direct-access', i.e. disk image copying "with copy c0:«zzz» c1:«zzz»" («zzz» to be decided) ("cat c0: >file" already works)

Maybe a separate disk editor/repair program?.

Relative file read/write (actually, read works just as PRG/SEQ/USR).


1541 GCR and 1581 MFM Disk Format

1541 GCR disk format

In group code recording (GCR) each nybble (4 bits) is replaced by 5 bits in the disk. The purpose of this coding is to limit the number of consequtive equal bits so that small changes in the disk spin rate and processing speed does not cause the decoder to lose synchronization and be confused. GCR limits the number of consequtive 1-bits to 8, and the number of consequtive 0-bits to 2.

In addition to valid GCR data we need some kind of a marker, sync, to tell us where the sector headers and sector data blocks start. In GCR a long run of 1-bits is used. At least 16 consequtive 1-bits are used as a sync mark.

GCR Coding Table

	Nybble		GCR
	0  0	0000	01010	 a 10
	1  1	0001	01011	 b 11
	2  2	0010	10010	12 18
	3  3	0011	10011	13 19
	4  4	0100	01110	 e 14
	5  5	0101	01111	 f 15
	6  6	0110	10110	16 22
	7  7	0111	10111	17 23
	8  8	1000	01001	 9  9
	9  9	1001	11001	19 25
	a 10	1010	11010	1a 26
	b 11	1011	11011	1b 27
	c 12	1100	01101	 d 13
	d 13	1101	11101	1d 29
	e 14	1110	11110	1e 30
	f 15	1111	10101	15 21
1541 sectors:
	SYNC 0x08   csum sector track id1 id2   GAP
	SYNC 0x07   (256 x data) csum           GAP
Each sector has two parts: header and data. If there is a 0x08 after a sync mark, it is a sector header, if there is 0x07, it's the sector data itself. Both parts have a checksum, which is calculated from the contents by xor:ing them together. For example if (csum^sector^track^id1^id2) is not zero, there is an error in the data. Because the bit length changes depending on the speed zone, there are more sectors on some tracks than there are on others.

1581 MFM disk format

In MFM a clock bit is added before each data bit. The clock bit is logical one between two consequtive zero data bits, zero otherwise. This encoding ensures that there are no consequtive 1-bits, and there is only upto 3 consequtive 0-bits. A special bit pattern is used as a sync mark. This bit pattern 0100010010001001 ($4489) can not appear in normal MFM data.

MFM Coding Table

	 0 0 0    0 0 1    0 1 0    0 1 1    1 0 1    1 1 0    1 1 1
	x01010x  x010010  x00100x  x001010  0100010  010100x  0101010
sync:	 0100 0100 1000 1001  ($4489)
	  1 0  1 0  0^0  0 1
	             |
	             `-- 'invalid' MFM, should be 1
1581 track:
	10 sectors:
	10x (3x $4489 + header + 3x $4489 + sector ) + pad
	10x (3x 16 b  + 656 b  + 3x 16 b  + 8992 b ) + 2785 b = 97440 + 2785
	10x (SYNC     + 7+34B  + SYNC     + 515+47B) + pad

header:
	ident	0xfe
	track
	side	(1=down, 0=up)
	sector
	sector size (2=512 bytes)
	crc1
	crc2

sector:
	ident	0xfb
	512 bytes of data
	crc1
	crc2
Even though the c1581 file system handles the disk as 3200 256-byte sectors, the actual physical format is 80 tracks, 2 sides, 10 512-byte sectors each. Three sync marks ($4489) are used before header and sector data. An identification byte tells whether it is a sector header or sector data.

CRC is calculated from the whole contents, including the ident byte, and the result should be zero. The same CRC polynomial as in Y-modem, Z-modem etc. is used. The curiosity is that the initial value (shift register state) of 0xe59a must be used.


To the homepage of albert@cs.tut.fi