[comp.sources.amiga] v90i218: MSH 1.30 - Messydos File System Handler, Part06/06

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (07/16/90)

Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 218
Archive-name: devices/msh-1.30/part06

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 6 (of 6)."
# Contents:  src/devio.c
# Wrapped by tadguy@xanth on Sun Jul 15 19:59:13 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/devio.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/devio.c'\"
else
echo shar: Extracting \"'src/devio.c'\" \(48145 characters\)
sed "s/^X//" >'src/devio.c' <<'END_OF_FILE'
X/*-
X * $Id: devio.c,v 1.30 90/06/04 23:18:52 Rhialto Rel $
X * $Log:	devio.c,v $
X * Revision 1.30  90/06/04  23:18:52  Rhialto
X * Release 1 Patch 3
X * 
X * DEVIO.C
X *
X * The messydisk.device code that does the real work.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dev.h"
X#include "device.h"
X
X/*#undef DEBUG			/**/
X#ifdef DEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xstruct DiskResource *DRResource;/* Argh! A global variable! */
Xvoid *CiaBResource;		/* And yet another! */
X
Xvoid		Internal_Update();
Xword		DataCRC();
Xword		CalculateGapLength();
X
X/*-
X *  The high clock bit in this table is still 0, but it could become
X *  a 1 if the two adjecent data bits are both 0.
X *  In fact, that is the principle of MFM clock bits: make sure that no
X *  two 1 bits are adjecent, but not too many (more than 3) 0 bits either.
X *  So, 0 c 0 -> 0 1 0	    (where c is a clock bit to be determined)
X *	0 c 1 -> 0 0 1
X *	1 c 0 -> 1 0 0
X *	1 c 1 -> 1 0 1
X *  The sync pattern, $4489, is %0100 0100 1000 1001
X *				  ~ ~  ~ ~  ~ ~  ~ ~ -> %1010 0001 -> $A1
X *  also follows the rules, but won't be formed by encoding $A1...
X *  Since the bytes are written high bit first, the unknown clock bit
X *  (for encoded nybbles 0-7, high bit 0) will become a 1 if the preceding
X *  byte was even (with low bit 0).
X *  So, the clock bit is the NOR of the two data bits.
X-*/
X
Xbyte		MfmEncode[16] = {
X    0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
X    0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
X};
X
X#define SYNC	0x4489
X#define TLEN	12500	    /* In BYTES */
X#define RLEN	(TLEN+1324) /* 1 sector extra */
X#define WLEN	(TLEN+20)   /* 20 bytes more than the theoretical track size */
X
X#define INDEXGAP	40  /* All these values are in WORDS */
X#define INDXGAP 	12
X#define INDXSYNC	 3
X#define INDXMARK	 1
X#define INDEXGAP2	40
X#define INDEXLEN	(INDEXGAP+INDXGAP+INDXSYNC+INDXMARK+INDEXGAP2)
X
X#define IDGAP2		12  /* Sector header: 22 words */
X#define IDSYNC		 3
X#define IDMARK		 1
X#define IDDATA		 4
X#define IDCRC		 2
X#define IDLEN		(IDGAP2+IDSYNC+IDMARK+IDDATA+IDCRC)
X
X#define DATAGAP1	22  /* Sector itself: 552 words */
X#define DATAGAP2	12
X#define DATASYNC	 3
X#define DATAMARK	 1
X#define DATACRC 	 2
X#define DATAGAP3_9	78  /* for 9 or less sectors/track */
X#define DATAGAP3_10	40  /* for 10 sectors/track */
X#define DATALEN 	(DATAGAP1+DATAGAP2+DATASYNC+DATAMARK+MS_BPS+DATACRC)
X
X#define BLOCKLEN	(IDLEN+DATALEN)     /* Total: 574 words */
X
X/* INDENT OFF */
X#asm
X
X; Some hardware data:
X
XINDEXSYNC	equ $5224	; special sync for index
XSYNC		equ $4489	; normal sector sync
XTLEN		equ 12500	; 2 miscrosecs/bit, 200 ms/track -> 100000 bits
XWLEN		equ TLEN+20
X
X;;;;
X;
X;   The following lengths are all in unencoded bytes (or encoded words)
X
XINDEXGAP	equ 40
XINDXGAP 	equ 12
XINDXSYNC	equ  3
XINDXMARK	equ  1
XINDEXGAP2	equ 40
X
XIDGAP2		equ 12
XIDSYNC		equ  3
XIDMARK		equ  1
XIDDATA		equ  4
XIDCRC		equ  2
X
XDATAGAP1	equ 22
XDATAGAP2	equ 12
XDATASYNC	equ  3
XDATAMARK	equ  1
XDATACRC 	equ  2
X
Xcustom		equ $DFF000
X
XDsklen		equ $24
XIntena		equ $9a 	; Interrupt enable  register (write)
XIntreq		equ $9c 	; Interrupt request register (write)
X
X; Flags in DSKLEN:
X
Xdskdmaoff	equ $4000
X
X; Flags in INTENA/INTREQ:
X
Xintf_setclr	equ 1<<15
Xintf_dskblk	equ 1<<1
X
X; CIA interrupt control register bits/flags:
X
Xciaicrf_flg	equ    1<<4	 ; flg interrupt (disk index)
X
X; some cia.resource library functions
X
X		public	_LVOSignal
X		public	_LVOAbleICR
X		public	_LVOSetICR
X
X_SafeEnableICR: move.l	_CiaBResource,a6
X		move.b	4+1(sp),d0
X		jsr	_LVOSetICR(a6)      ; clear pending interrupt
X		move.b	4+1(sp),d0
X		or.b	#$80,d0 	    ; then enable it
X		jsr	_LVOAbleICR(a6)
X		rts
X;;;;
X;
X;   Disk index interrupt code.
X;   is_Data (A1) is the value to stuff into the DSKLEN register.
X;   A0 points to the custom chips already.
X;   It then enables the disk block interrupt and disables the
X;   index interrupt.
X
X_IndexIntCode:
X;	 movem.l A2-A4/D2-D7,-(sp)
X	move.w	#dskdmaoff,Dsklen(a0)
X	move.w	a1,Dsklen(a0)
X	move.w	a1,Dsklen(a0)       ; this enables the DMA
X	move.w	#intf_setclr|intf_dskblk,Intena(a0)
X	move.l	_CiaBResource,a6
X	move.b	#ciaicrf_flg,d0
X	jsr	_LVOAbleICR(a6)     ; disable index interrupt
X;	 movem.l (sp)+,A2-A4/D2-D7
X	rts
X;;;;
X;
X;   Disk DMA finished interrupt code.
X;   (a1) is the task to Signal, 4(a1) is the signal mask to use.
X;   Disables the disk block finished interrupt.
X
X_DskBlkIntCode:
X	move.w	#dskdmaoff,Dsklen(a0)   ; disable disk DMA
X	move.w	#intf_dskblk,Intena(a0) ; disable the interrupt
X	move.w	#intf_dskblk,Intreq(a0) ; clear 'diskblock finished' flag
X	move.l	4(a1),d0            ; signal mask
X	move.l	(a1),a1             ; task to signal
X	jsr	_LVOSignal(a6)
X	rts
X
X#endasm
X
X#define DSKDMAEN	(1<<15)
X#define DSKWRITE	(1<<14)
X
Xvoid IndexIntCode(), DskBlkIntCode();
X
X/* INDENT ON */
X
Xint
XHardwareIO(dev, unit, dskwrite)
XDEV	       *dev;
Xregister UNIT  *unit;
Xint		dskwrite;
X{
X    struct {
X	struct Task *task;
X	ulong signal;
X    } tasksig;
X
X    debug(("Disk buffer is at %lx\n", dev->md_Rawbuffer));
X
X    tasksig.task = FindTask(NULL);
X    tasksig.signal = 1L << unit->mu_DmaSignal;
X
X    unit->mu_DRUnit.dru_Index.is_Data = (APTR) ((WLEN >> 1)|DSKDMAEN| dskwrite);
X    unit->mu_DRUnit.dru_DiscBlock.is_Data = (APTR) &tasksig;
X
X    /* Clear signal bit */
X    SetSignal(0L, tasksig.signal);
X
X    /* Allocate drive and install index and block interrupts */
X    GetDrive(&unit->mu_DRUnit);
X
X    /* Select correct drive and side */
X    ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR;    /* See hardware manual p229 */
X    ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR
X		       & ~(CIAF_DSKSEL0 << unit->mu_UnitNr)
X		       & ~(unit->mu_CurrentSide << CIAB_DSKSIDE);
X
X    /* Set up disk parameters */
X
X/*
X * This is the adkcon setup: MFM mode, wordsync, no MSBsync, fast mode.
X * The precomp is 0 nanoseconds for the outer half of the disk, 120 for
X * the rest.
X */
X    {
X	register word adk;
X
X	custom.adkcon = ADKF_PRECOMP1|ADKF_PRECOMP0|ADKF_MSBSYNC;
X
X	adk = ADKF_SETCLR|ADKF_MFMPREC|ADKF_FAST|ADKF_WORDSYNC;
X
X	/* Are we on the inner half ? */
X	if (unit->mu_CurrentTrack > unit->mu_NumCyls >> 1) {
X	    adk |= ADKF_PRECOMP0;
X	}
X	custom.adkcon = adk;
X    }
X
X    /* Set up disk buffer address */
X    custom.dskpt = (APTR) dev->md_Rawbuffer;
X
X    /* Enable disk DMA */
X    custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_DISK;
X
X    if (dskwrite) {
X	/* Enable disk index interrupt to start the whole thing. */
X	SafeEnableICR((int) CIAICRF_FLG);
X    } else {
X	/* Set the sync word */
X	custom.dsksync = SYNC;
X
X	/* Do the same as the disk index interrupt would */
X	custom.dsklen = DSKDMAOFF;
X	custom.dsklen = (RLEN >> 1) | DSKDMAEN;
X	custom.dsklen = (RLEN >> 1) | DSKDMAEN;
X
X	custom.intena = INTF_SETCLR | INTF_DSKBLK;
X    }
X
X    Wait(tasksig.signal);
X
X    FreeDrive();
X}
X
X#if 0
X#define ID_ADDRESS_MARK     0xFE
X#define MFM_ID		    0x5554
X#define DATA_ADDRESS_MARK   0xFB
X#define MFM_DATA	    0x5545
X
X/* INDENT OFF
Xbyte
XDecodeByte(mfmdecode, mfm)
Xbyte *mfmdecode;
Xword mfm;
X{
X    return mfmdecode[(byte)mfm & 0x7F] |
X	   mfmdecode[(byte)(mfm >> 8) & 0x7F] << 4;
X} */
X
X#asm
Xmfmdecode   set     4
Xmfm	    set     8
X
X_DecodeByte:
X	move.l	mfmdecode(sp),a0
X	move.b	mfm(sp),d1      ; high nybble
X	and.w	#$7f,d1 	; strip clock bit (and garbage)
X	move.b	(a0,d1.w),d0    ; decode 4 data bits
X	lsl.b	#4,d0		; make room for the rest
X
X	move.b	mfm+1(sp),d1    ; low nybble
X	and.b	#$7f,d1 	; strip clock bit again
X	or.b	(a0,d1.w),d0    ; insert 4 decoded bits
X
X	rts
X
X#endasm
X
X/* INDENT ON */
Xbyte DecodeByte();
X
Xint
XDecodeTrack(dev, unit)
XDEV	       *dev;
XUNIT	       *unit;
X{
X    register word  *rawbuf = (word *)dev->md_Rawbuffer; /*  a2 */
X    byte	   *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
X    byte	   *trackbuf = unit->mu_TrackBuffer;
X    register byte  *decode = dev->md_MfmDecode; 	/*  a3 */
X    word	   *oldcrc = unit->mu_CrcBuffer;
X    register byte  *secptr;				/*  a4 */
X    long	    sector;
X    word	    numsecs;
X    register long   numbytes;				/*  d3 */
X    word	    maxsec;
X
X#define Len	((byte *)rawbuf - dev->md_Rawbuffer)
X    maxsec = 0;
X
X    for (numsecs = 0; numsecs < MS_SPT_MAX; numsecs++) {
X	/*
X	 *  First try to find a sector id.
X	 */
Xfind_id:
X	while (*rawbuf != SYNC) {
X	    if (++rawbuf >= rawend) {
X		debug(("id start, EOT %4x\n", Len));
X		goto end;
X	    }
X	}
X	while (*rawbuf == SYNC) {
X	    rawbuf++;
X	}
X	if (*rawbuf++ != MFM_ID) {
X	    debug(("No ID (%4x), %4x\n", rawbuf[-1], Len));
X	    goto find_id;
X	}
X
X	sector = DecodeByte(decode, *rawbuf++);
X	if (sector != unit->mu_CurrentTrack) {
X	    debug(("Track error?? %d\n", (int)sector));
X	    goto find_id;
X	}
X	sector = DecodeByte(decode, *rawbuf++);
X	if (sector != unit->mu_CurrentSide) {
X	    debug(("Side error?? %d\n", (int)sector));
X	    goto find_id;
X	}
X	if (rawbuf >= rawend) {
X	    debug(("id end, EOT %4x\n", Len));
X	    goto end;
X	}
X	sector = DecodeByte(decode, *rawbuf++);
X	debug(("#%2d %4x, ", (int)sector, Len-0xC));
X	if (sector > MS_SPT_MAX) {
X	    debug(("Bogus sector number) "));
X	    goto find_id;
X	}
X	if (sector > maxsec)
X	    maxsec = sector;
X	sector--;		/* Normalize sector number */
X
X	/*
X	 *  Then find the data block.
X	 */
Xfind_data:
X	while (*rawbuf != SYNC) {
X	    if (++rawbuf >= rawend) {
X		debug(("data start, EOT %4x\n", Len));
X		return 0; /* TDERR_TooFewSecs; */
X	    }
X	}
X	while (*rawbuf == SYNC) {
X	    rawbuf++;
X	}
X	if (*rawbuf++ != MFM_DATA) {
X	    debug(("No Data (%4x), %4x\n", rawbuf[-1], Len));
X	    goto find_id;
X	}
X	debug(("%4x, ", Len-8));
X
X	if (rawbuf >= rawend) {
X	    debug(("short data, EOT %4x\n", Len));
X	    goto end;
X	}
X	secptr = trackbuf + MS_BPS * sector;
X	for (numbytes = 0; numbytes < MS_BPS; numbytes++) {
X	    *secptr++ = DecodeByte(decode, *rawbuf++);
X	}
X	debug(("%4x\n", Len));
X	oldcrc[sector]	= DecodeByte(decode, *rawbuf++) << 8;
X	oldcrc[sector] |= DecodeByte(decode, *rawbuf++);
X	unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
X    }
X
Xend:
X    if (numsecs == 0)
X	return TDERR_TooFewSecs;
X
X#ifndef READONLY
X    /*
X     * If we read the very first track, we adjust our notion about the
X     * number of sectors on each track. This is the only track we can
X     * accurately find if this number is unknown. Let's hope that the first
X     * user of this disk starts reading it here.
X     */
X    if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
X	unit->mu_SectorsPerTrack = maxsec;
X    }
X    unit->mu_CurrentSectors = maxsec;
X    debug(("%d sectors\n", unit->mu_SectorsPerTrack));
X#endif
X
X    return 0;
X
X#undef Len
X}
X#else	/* Use assembly */
X
Xint
XDecodeTrack(dev, unit)
XDEV	       *dev;
XUNIT	       *unit;
X{
X    register word  *rawbuf = (word *)dev->md_Rawbuffer; /*  a2 */
X    byte	   *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
X    byte	   *trackbuf = unit->mu_TrackBuffer;
X    register byte  *decode = dev->md_MfmDecode; 	/*  a3 */
X    word	   *oldcrc = unit->mu_CrcBuffer;
X    register byte  *secptr;				/*  a4 */
X    long	    sector;
X    word	    numsecs;
X    register long   numbytes;				/*  d3 */
X    word	    maxsec;
X
X#asm
X
XMFM_ID	    equ     $5554
XMFM_DATA    equ     $5545
X
Xrawbuf	    equr    a2
Xdecode	    equr    a3
Xsecptr	    equr    a4
Xnumbytes    equr    d3
X
Xrawend	    set     -4
Xtrackbuf    set     -8
Xoldcrc	    set     -12
Xsector	    set     -16
Xnumsecs     set     -18
Xmaxsec	    set     -20
X
X	move.w	#0,numsecs(a5)      ; no sectors found yet
X	move.w	#0,maxsec(a5)       ; and no highest sector number
X
X;;;;	First we will try to find a sector id.
Xfind_id:
X	cmp	#SYNC,(rawbuf)+
X	beq.s	fid_gotsync
X	cmpa.l	rawend(a5),rawbuf
X	blt	find_id
X	bra	return		    ; We ran off the end of the buffer.
X
Xfid_gotsync:			    ; Skip the other syncs.
X	cmp.w	#SYNC,(rawbuf)
X	bne	fid_endsync
X	lea	2(rawbuf),rawbuf
X	bra	fid_gotsync
X
Xfid_endsync:
X	cmp.w	#MFM_ID,(rawbuf)+
X	bne	find_id
X
X	bsr	DecodeByte	    ; track #
X	bsr	DecodeByte	    ; side #
X	moveq.l #0,d0		    ; clear high part
X	bsr	DecodeByte	    ; sector #
X	cmp.w	#MS_SPT_MAX,d0	    ; sector number too large?
X	bgt	find_id
X	cmp.w	maxsec(a5),d0       ; what is the highest sector number?
X	ble	nomax
X	move.w	d0,maxsec(a5)       ; record the highest sector number
Xnomax:
X	subq.w	#1,d0		    ; normalize sector number
X	move.l	d0,sector(a5)
X
Xfind_data:			    ; Then find the data block.
X	cmp	#SYNC,(rawbuf)+
X	beq.s	fda_gotsync
X	cmpa.l	rawend(a5),rawbuf
X	blt	find_data
X	bra	return		    ; we ran off the end of the buffer.
X
Xfda_gotsync:			    ; skip the other syncs.
X	cmp.w	#SYNC,(rawbuf)
X	bne	fda_endsync
X	lea	2(rawbuf),rawbuf
X	bra	fda_gotsync
X
Xfda_endsync:
X	cmp.w	#MFM_DATA,(rawbuf)+ ; do we really have a data block?
X	bne	find_id
X
X	cmpa.l	rawend(a5),rawbuf   ; will we still be inside the mfm data?
X	bge	return
X
X	move.l	sector(a5),d0       ; calculate the location to
X	moveq.l #LOG2_MS_BPS,d1     ;  store this sector.
X	asl.l	d1,d0
X	move.l	trackbuf(a5),secptr
X	add.l	d0,secptr
X
X	move.w	#MS_BPS-1,numbytes
Xdata_copy:
X	bsr	DecodeByte
X	move.b	d0,(secptr)+
X	dbra	numbytes,data_copy
X
X	move.l	sector(a5),d3       ; get pointer to crc location
X	add.l	d3,d3		    ; 2 bytes of crc per sector
X	move.l	oldcrc(a5),a0
X	add.l	d3,a0
X
X	bsr	DecodeByte	    ; get high byte
X	move.b	d0,(a0)+
X	bsr	DecodeByte	    ; and low byte of crc
X	move.b	d0,(a0)+
X
X#endasm
X	unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
X#asm
X	addq.w	#1,numsecs(a5)
X	cmp.w	#MS_SPT_MAX,numsecs(a5)
X	blt	find_id
Xreturn:
X#endasm
X
X    if (numsecs == 0)
X	return TDERR_TooFewSecs;
X
X#ifndef READONLY
X    /*
X     * If we read the very first track, we adjust our notion about the
X     * number of sectors on each track. This is the only track we can
X     * accurately find if this number is unknown. Let's hope that the first
X     * user of this disk starts reading it here.
X     */
X    if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
X	unit->mu_SectorsPerTrack = maxsec;
X    }
X    unit->mu_CurrentSectors = maxsec;
X    debug(("%d sectors\n", unit->mu_SectorsPerTrack));
X#endif
X
X    return 0;
X
X}
X
X#asm
X;;;;
X;
X;   Decode a single MFM word to a byte.
X;   Auto-increments the rawbuffer pointer.
X
XDecodeByte:
X	move.b	(rawbuf)+,d1    ; high nybble
X	and.w	#$7f,d1 	; strip clock bit (and garbage)
X	move.b	(decode,d1.w),d0; decode 4 data bits
X	lsl.b	#4,d0		; make room for the rest
X
X	move.b	(rawbuf)+,d1    ; low nybble
X	and.b	#$7f,d1 	; strip clock bit again
X	or.b	(decode,d1.w),d0; insert 4 decoded bits
X
X	rts
X
X#endasm
X#endif	/* using assembly */
X
X/*
X * Initialize the ibm mfm decoding table
X */
X
Xvoid
XInitDecoding(decode)
Xregister byte  *decode;
X{
X    register int    i;
X
X    i = 0;
X    do {
X	decode[i] = 0xff;
X    } while (++i < 128);
X
X    i = 0;
X    do {
X	decode[MfmEncode[i]] = i;
X    } while (++i < 0x10);
X}
X
X#ifdef notdef
Xlong
XMyDoIO(req)
Xregister struct IORequest *req;
X{
X    req->io_Flags |= IOF_QUICK;
X    BeginIO(req);
X    return WaitIO(req);
X}
X#endif
X
X/*
X * Switch the drive motor on. Return previous state. Don't use this when
X * you have allocated the disk via GetDrive().
X */
X
Xint
XTDMotorOn(tdreq)
Xregister struct IOExtTD *tdreq;
X{
X    debug(("TDMotorOn "));
X    tdreq->iotd_Req.io_Command = TD_MOTOR;
X    tdreq->iotd_Req.io_Length = 1;
X    DoIO(tdreq);
X    debug(("was %ld\n", tdreq->iotd_Req.io_Actual));
X
X    return tdreq->iotd_Req.io_Actual;
X}
X
X/*
X * Get the number of cylinders the drive is capable of using.
X */
X
Xint
XTDGetNumCyls(tdreq)
Xregister struct IOExtTD *tdreq;
X{
X    tdreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
X    DoIO(tdreq);
X
X    return tdreq->iotd_Req.io_Actual / NUMHEADS;
X}
X
X/*
X * Seek the drive to the indicated cylinder. Use the trackdisk.device for
X * ease. Don't use this when you have allocated the disk via GetDrive().
X */
X
Xint
XTDSeek(unit, ioreq, cylinder)
XUNIT	       *unit;
Xstruct IOStdReq *ioreq;
Xint		cylinder;
X{
X    register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
X
X    debug(("TDSeek %d\n", cylinder));
X
X    tdreq->iotd_Req.io_Command = TD_SEEK;
X    tdreq->iotd_Req.io_Offset = cylinder * (TD_SECTOR * NUMSECS * NUMHEADS);
X    if ((ioreq->io_Flags & IOMDF_40TRACKS) && (unit->mu_NumCyls == 80))
X	tdreq->iotd_Req.io_Offset *= 2;
X    DoIO(tdreq);
X
X    return tdreq->iotd_Req.io_Error;
X}
X
Xvoid	       *
XGetDrive(drunit)
Xregister struct DiskResourceUnit *drunit;
X{
X    register void  *LastDriver;
X
X    debug(("GetDrive: "));
X    for (;;) {
X	drunit->dru_Message.mn_Node.ln_Type = NT_MESSAGE;
X	LastDriver = GetUnit(drunit);
X
X	debug(("LastDriver %08lx\n", LastDriver));
X	if (LastDriver) {
X	    return LastDriver;
X	} else {
X	    while (drunit->dru_Message.mn_Node.ln_Type != NT_REPLYMSG)
X		Wait(1L << drunit->dru_Message.mn_ReplyPort->mp_SigBit);
X	    Remove(drunit);
X	    debug(("GetDrive: Retry\n"));
X	}
X    }
X}
X
Xvoid
XFreeDrive()
X{
X    GiveUnit();
X}
X
Xint
XGetTrack(ioreq, side, track)
Xstruct IOStdReq *ioreq;
Xint		side;
Xint		track;
X{
X    register int    i;
X    DEV 	   *dev;
X    register UNIT  *unit;
X
X    debug(("GetTrack %d %d\n", track, side));
X    dev = (DEV *) ioreq->io_Device;
X    unit = (UNIT *) ioreq->io_Unit;
X
X    if (track != unit->mu_CurrentTrack || side != unit->mu_CurrentSide) {
X#ifndef READONLY
X	Internal_Update(ioreq, unit);
X#endif
X	for (i = MS_SPT_MAX-1; i >= 0; i--) {
X	    unit->mu_SectorStatus[i] = TDERR_NoSecHdr;
X	}
X
X	TDMotorOn(unit->mu_DiskIOReq);
X	if (TDSeek(unit, ioreq, track)) {
X	    debug(("Seek error\n"));
X	    return ioreq->io_Error = IOERR_BADLENGTH;
X	}
X	unit->mu_CurrentTrack = track;
X	unit->mu_CurrentSide = side;
X	ObtainSemaphore(&dev->md_HardwareUse);
X	HardwareIO(dev, unit, 0);
X	i = DecodeTrack(dev, unit);
X	ReleaseSemaphore(&dev->md_HardwareUse);
X	debug(("DecodeTrack returns %d\n", i));
X
X	if (i != 0) {
X	    unit->mu_CurrentTrack = -1;
X	    return i;
X	}
X    }
X
X    return 0;
X}
X
X/*
X * Test if it is changed
X */
X
Xint
XCheckChanged(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT  *unit;
X{
X    register struct IOExtTD *tdreq;
X
X    if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
X	ioreq->iotd_Count < unit->mu_ChangeNum) {
Xdiskchanged:
X	ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
Xerror:
X	return 1;
X    }
X    return 0;
X}
X
X/*
X * Test if we can read or write the disk. Is it inserted and writable?
X */
X
Xint
XCheckRequest(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT  *unit;
X{
X    register struct IOExtTD *tdreq;
X
X    if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
X	ioreq->iotd_Count < unit->mu_ChangeNum) {
Xdiskchanged:
X	ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
Xerror:
X	return 1;
X    }
X    /*
X     * if (ioreq->iotd_Req.io_Offset + ioreq->iotd_Req.io_Length >
X     * (unit->mu_NumCyls * MS_NSIDES * MS_SPT * MS_BPS)) {
X     * ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; goto error; }
X     */
X
X    tdreq = unit->mu_DiskIOReq;
X
X    if (unit->mu_DiskState == STATEF_UNKNOWN) {
X	tdreq->iotd_Req.io_Command = TD_PROTSTATUS;
X	DoIO(tdreq);
X	if (tdreq->iotd_Req.io_Error == 0) {
X	    if (tdreq->iotd_Req.io_Actual == 0) {
X		unit->mu_DiskState = STATEF_PRESENT | STATEF_WRITABLE;
X	    } else
X		unit->mu_DiskState = STATEF_PRESENT;
X	} else
X	    unit->mu_DiskState = 0;
X    }
X    if (!(unit->mu_DiskState & STATEF_PRESENT))
X	goto diskchanged;
X
X    /*
X     * Check _WRITE, _UPDATE, _FORMAT
X     */
X    if (STRIP(ioreq->iotd_Req.io_Command) != CMD_READ) {
X	if (!(unit->mu_DiskState & STATEF_WRITABLE)) {
X	    ioreq->iotd_Req.io_Error = TDERR_WriteProt;
X	    goto error;
X	}
X    }
X    return 0;
X}
X
X
X/*
X * Read zero or more sectors from the disk and copy them into the user's
X * buffer.
X */
X
Xvoid
XCMD_Read(ioreq, unit)
Xregister struct IOExtTD *ioreq;
Xregister UNIT  *unit;
X{
X    int 	    side;
X    int 	    cylinder;
X    int 	    sector;
X    byte	   *userbuf;
X    long	    length;
X    long	    offset;
X    byte	   *diskbuf;
X    int 	    retrycount;
X
X    debug(("CMD_Read "));
X    userbuf = (byte *) ioreq->iotd_Req.io_Data;
X    length = ioreq->iotd_Req.io_Length / MS_BPS;	/* Sector count */
X    offset = ioreq->iotd_Req.io_Offset / MS_BPS;	/* Sector number */
X    debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
X
X    cylinder = offset / unit->mu_SectorsPerTrack;
X    side = cylinder % MS_NSIDES;
X    cylinder /= MS_NSIDES;
X    sector = offset % unit->mu_SectorsPerTrack;       /* 0..8 or 9 */
X    debug(("Tr=%d Si=%d Se=%d\n", cylinder, side, sector));
X
X    ioreq->iotd_Req.io_Actual = 0;
X
X    if (length <= 0 || CheckRequest(ioreq, unit))
X	goto end;
X
X    retrycount = 0;
X    diskbuf = unit->mu_TrackBuffer + MS_BPS * sector;
Xgettrack:
X    GetTrack(ioreq, side, cylinder);
X
X    for (;;) {
X	/*
X	 * Have we ever checked this CRC?
X	 */
X	if (unit->mu_SectorStatus[sector] == CRC_UNCHECKED) {
X	    /*
X	     * Do it now. If it mismatches, remember that for later.
X	     */
X	    if (unit->mu_CrcBuffer[sector] != DataCRC(diskbuf)) {
X		debug(("%d: %04x, now %04x\n", sector, unit->mu_CrcBuffer[sector], DataCRC(diskbuf)));
X		unit->mu_SectorStatus[sector] = TDERR_BadSecSum;
X	    } else
X		unit->mu_SectorStatus[sector] = TDERR_NoError;
X	}
X	if (unit->mu_SectorStatus[sector] > TDERR_NoError) {
X	    if (++retrycount < 3) {
X		unit->mu_CurrentTrack = -1;
X		goto gettrack;
X	    }
X	    ioreq->iotd_Req.io_Error = unit->mu_SectorStatus[sector];
X	    goto end;	    /* Don't use this sector anymore... */
X	}
X	retrycount = 0;
X	CopyMem(diskbuf, userbuf, (long) MS_BPS);
X	ioreq->iotd_Req.io_Actual += MS_BPS;
X	if (--length <= 0)
X	    break;
X	userbuf += MS_BPS;
X	diskbuf += MS_BPS;
X	if (++sector >= unit->mu_SectorsPerTrack) {
X	    sector = 0;
X	    diskbuf = unit->mu_TrackBuffer;
X	    if (++side >= MS_NSIDES) {
X		side = 0;
X		if (++cylinder >= unit->mu_NumCyls) {
X		    /* ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; */
X		    goto end;
X		}
X	    }
X	    GetTrack(ioreq, side, cylinder);
X	}
X    }
X
Xend:
X    TermIO(ioreq);
X}
X
X#ifdef READONLY
X
Xvoid
XCMD_Write(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X    TermIO(ioreq);
X}
X
Xvoid
XTD_Format(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X    TermIO(ioreq);
X}
X
X#endif
X
Xvoid
XCMD_Reset(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    unit->mu_CurrentSide = -1;
X    unit->mu_TrackChanged = 0;
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Update(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT  *unit;
X{
X#ifndef READONLY
X    if (unit->mu_TrackChanged && !CheckRequest(ioreq, unit))
X	Internal_Update(ioreq, unit);
X#endif
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Clear(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    if (!CheckChanged(ioreq, unit)) {
X	unit->mu_CurrentSide = -1;
X	unit->mu_TrackChanged = 0;
X    }
X    TermIO(ioreq);
X}
X
Xvoid
XTD_Seek(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    if (!CheckChanged(ioreq, unit)) {
X	word		cylinder;
X
X	cylinder = (ioreq->iotd_Req.io_Offset / unit->mu_SectorsPerTrack) /
X		    (MS_BPS * MS_NSIDES);
X	TDSeek(unit, ioreq, cylinder);
X    }
X    TermIO(ioreq);
X}
X
X/*
X * Ask the trackdisk.device for the answer, but keep a local copy.
X */
X
Xvoid
XTD_Changenum(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOStdReq *req;
X
X    req = &unit->mu_DiskIOReq->iotd_Req;
X    req->io_Command = TD_CHANGENUM;
X    DoIO(req);
X
X    unit->mu_ChangeNum = req->io_Actual;
X    ioreq->iotd_Req.io_Actual = req->io_Actual;
X    TermIO(ioreq);
X}
X
Xint
XDevInit(dev)
Xregister DEV   *dev;
X{
X    if (!(DRResource = OpenResource(DISKNAME)))
X	goto abort;
X
X    if (!(CiaBResource = OpenResource(CIABNAME)))
X	goto abort;
X
X#ifndef READONLY
X    if (!InitWrite(dev))
X	goto abort;
X#endif
X
X    InitDecoding(dev->md_MfmDecode);
X    InitSemaphore(&dev->md_HardwareUse);
X    return 1;			/* Initializing succeeded */
X
Xabort:
X    return DevCloseDown(dev);
X}
X
Xint
XDevCloseDown(dev)
XDEV	       *dev;
X{
X#ifndef READONLY
X    FreeBuffer(dev);
X#endif
X    return 0;			/* Now unitialized */
X}
X
X#ifndef READONLY
X/*
X * Calculate the length between the sectors, given the length of the track
X * and the number of sectors that must fit on it.
X * The proper formula would be
X * (((TLEN/2) - INDEXLEN) / unit->mu_SectorsPerTrack) - BLOCKLEN;
X */
X
Xword
XCalculateGapLength(sectors)
Xint		sectors;
X{
X    return (sectors == 10) ? DATAGAP3_10 : DATAGAP3_9;
X}
X#endif
X
XUNIT	       *
XUnitInit(dev, UnitNr)
XDEV	       *dev;
Xulong		UnitNr;
X{
X    register UNIT  *unit;
X    struct Task    *task;
X    struct IOStdReq *dcr;
X    struct IOExtTD *tdreq;
X
X    unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
X    if (unit == NULL)
X	return NULL;
X
X    if (!(tdreq = CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*tdreq)))) {
X	goto abort;
X    }
X    unit->mu_DiskIOReq = tdreq;
X    if (OpenDevice(TD_NAME, UnitNr, tdreq, TDF_ALLOW_NON_3_5)) {
X	tdreq->iotd_Req.io_Device = NULL;
X	goto abort;
X    }
X    dcr = (void *) CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*dcr));
X    if (dcr) {
X	unit->mu_DiskChangeReq = dcr;
X	unit->mu_DiskChangeInt.is_Node.ln_Pri = 32;
X	unit->mu_DiskChangeInt.is_Data = (APTR) unit;
X	unit->mu_DiskChangeInt.is_Code = DiskChangeHandler;
X	/* Clone IO request part */
X	dcr->io_Device = tdreq->iotd_Req.io_Device;
X	dcr->io_Unit = tdreq->iotd_Req.io_Unit;
X	dcr->io_Command = TD_ADDCHANGEINT;
X	dcr->io_Data = (void *) &unit->mu_DiskChangeInt;
X	SendIO(dcr);
X    }
X    NewList(&unit->mu_ChangeIntList);
X
X    unit->mu_NumCyls = TDGetNumCyls(tdreq);
X    unit->mu_UnitNr = UnitNr;
X    unit->mu_DiskState = STATEF_UNKNOWN;
X    unit->mu_CurrentSide = -1;
X    unit->mu_TrackChanged = 0;
X    unit->mu_InitSectorStatus = CRC_UNCHECKED;
X    unit->mu_SectorsPerTrack = MS_SPT;
X
X    unit->mu_DRUnit.dru_Message.mn_ReplyPort = &unit->mu_DiskReplyPort;
X    unit->mu_DRUnit.dru_Index.is_Node.ln_Pri = 32; /* high pri for index int */
X    unit->mu_DRUnit.dru_Index.is_Code = IndexIntCode;
X    unit->mu_DRUnit.dru_DiscBlock.is_Code = DskBlkIntCode;
X
X    /*
X     * Now create the Unit task. Remember that it won't start running
X     * since we are Forbid()den. But just to be sure, we Forbid() again.
X     */
X    Forbid();
X    task = CreateTask(DevName, TASKPRI, UnitTask, TASKSTACK);
X    task->tc_UserData = (APTR) unit;
X
X    unit->mu_Port.mp_Flags = PA_IGNORE;
X    unit->mu_Port.mp_SigTask = task;
X    NewList(&unit->mu_Port.mp_MsgList);
X
X    unit->mu_DiskReplyPort.mp_Flags = PA_IGNORE;
X    unit->mu_DiskReplyPort.mp_SigTask = task;
X    NewList(&unit->mu_DiskReplyPort.mp_MsgList);
X    Permit();
X
X    return unit;
X
Xabort:
X    UnitCloseDown(NULL, dev, unit);
X    return NULL;
X}
X
Xint
XUnitCloseDown(ioreq, dev, unit)
Xstruct IOExtTD *ioreq;
XDEV	       *dev;
Xregister UNIT  *unit;
X{
X    /*
X     * Get rid of the Unit's task. We know this is safe because the unit
X     * has an open count of zero, so it is 'guaranteed' not in use.
X     */
X
X    if (unit->mu_Port.mp_SigTask) {
X#ifdef DEBUG
X	extern struct SignalSemaphore PortUse;
X
X	/*
X	 * Make sure that the unit task does not get removed when it has
X	 * the semaphore.
X	 */
X	ObtainSemaphore(&PortUse);
X#endif
X	RemTask(unit->mu_Port.mp_SigTask);
X#ifdef DEBUG
X	ReleaseSemaphore(&PortUse);
X#endif
X    }
X    if (unit->mu_DiskChangeReq) {
X#if 0				/* V1.2 and V1.3 have a broken
X				 * TD_REMCHANGEINT */
X	register struct IOExtTD *req = unit->mu_DiskIOReq;
X
X	req->iotd_Req.io_Command = TD_REMCHANGEINT;
X	req->iotd_Req.io_Data = (void *) unit->mu_DiskChangeReq;
X	DoIO(req);
X	WaitIO(unit->mu_DiskChangeReq);
X#else
X	Disable();
X	Remove(unit->mu_DiskChangeReq);
X	Enable();
X#endif
X	DeleteExtIO(unit->mu_DiskChangeReq);
X	unit->mu_DiskChangeReq = NULL;
X    }
X    if (unit->mu_DiskIOReq) {
X	if (unit->mu_DiskIOReq->iotd_Req.io_Device)
X	    CloseDevice(unit->mu_DiskIOReq);
X	DeleteExtIO(unit->mu_DiskIOReq);
X	unit->mu_DiskIOReq = NULL;
X    }
X    FreeMem(unit, (long) sizeof (UNIT));
X
X    return 0;			/* Now unitialized */
X}
X
X/*
X * Create missing bindings
X */
X/* INDENT OFF */
X
X#asm
X
Xlib_vectsize	equ	6
Xlib_base	equ	-lib_vectsize
X
X_RVOAllocUnit	equ	lib_base-(0*lib_vectsize)
X_RVOFreeUnit	equ	lib_base-(1*lib_vectsize)
X_RVOGetUnit	equ	lib_base-(2*lib_vectsize)
X_RVOGiveUnit	equ	lib_base-(3*lib_vectsize)
X_RVOGetUnitID	equ	lib_base-(4*lib_vectsize)
X
X;_AllocUnit:
X;		move.l	_DRResource,a6
X;		move.l	4(sp),d0
X;		jmp	_RVOAllocUnit(a6)
X;_FreeUnit:
X;		move.l	_DRResource,a6
X;		move.l	4(sp),d0
X;		jmp	_RVOFreeUnit(a6)
X_GetUnit:
X		move.l	_DRResource,a6
X		move.l	4(sp),a1
X		jmp	_RVOGetUnit(a6)
X;_GetUnitID:
X;		move.l	_DRResource,a6
X;		move.l	4(sp),d0
X;		jmp	_RVOGetUnitID(a6)
X_GiveUnit:
X		move.l	_DRResource,a6
X		jmp	_RVOGiveUnit(a6)
X
X#endasm
X/* INDENT ON */
X
X/*
X * We handle disk change interrupts internally, since the io request is
X * held by the device. Since SoftInts caused by the trackdisk.device are
X * broadcast to our clients, our own softint must have the highest
X * priority possible.
X *
X * TD_Addchangeint is an IMMEDIATE command, so no exclusive use of the list
X * is acquired (nor released). The list is accessed by (software)
X * interrupt code.
X */
X
Xvoid
XTD_Addchangeint(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    register UNIT  *unit;
X
X    unit = (UNIT *) ioreq->io_Unit;
X    Disable();
X    AddTail(&unit->mu_ChangeIntList, ioreq);
X    Enable();
X    ioreq->io_Flags &= ~IOF_QUICK;	/* So we call ReplyMsg instead of
X					 * TermIO */
X    /* Note no TermIO */
X}
X
Xvoid
XTD_Remchangeint(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    register struct IOStdReq *intreq;
X
X    intreq = (struct IOStdReq *) ioreq->io_Data;
X    Disable();
X    Remove(intreq);
X    Enable();
X    ReplyMsg(&intreq->io_Message);      /* Quick bit always cleared */
X    ioreq->io_Error = 0;
X    TermIO(ioreq);
X}
X
Xvoid
XDiskChangeHandler()
X{
X    auto UNIT	   *unit;
X    register struct IOStdReq *ioreq;
X    register struct IOStdReq *next;
X/* INDENT OFF */
X#asm
X    movem.l d2-d7/a2-a4,-(sp)
X    move.l  a1,-4(a5)        ;unit
X#endasm
X    /* INDENT ON */
X    unit->mu_DiskState = STATEF_UNKNOWN;
X    unit->mu_ChangeNum++;
X    unit->mu_SectorsPerTrack = MS_SPT;
X    for (ioreq = (struct IOStdReq *) unit->mu_ChangeIntList.mlh_Head;
X	 next = (struct IOStdReq *) ioreq->io_Message.mn_Node.ln_Succ;
X	 ioreq = next) {
X	Cause((struct Interrupt *) ioreq->io_Data);
X    }
X/* INDENT OFF */
X#asm
X    movem.l (sp)+,d2-d7/a2-a4
X#endasm
X    /* INDENT ON */
X}
X
X#ifndef READONLY
X
X/*
X * Parts of the following code were written by Werner Guenther.
X * Used with permission.
X */
X
X/* mu_TrackChanged is a flag. When a sector has changed it changes to 1 */
X
X/*
X * InitWrite() has to be called once at startup. It allocates the space
X * for one raw track, and writes the low level stuff between sectors
X * (gaps, syncs etc.)
X */
X
Xint
XInitWrite(dev)
XDEV	       *dev;
X{
X    if ((dev->md_Rawbuffer =
X	    AllocMem((long)RLEN+2, MEMF_CHIP | MEMF_PUBLIC)) == 0)
X	return 0;
X
X    return 1;
X}
X
X/*
X * FreeBuffer has to be called when msh: closes down, it just frees the
X * memory InitWrite has allocated
X */
X
Xvoid
XFreeBuffer(dev)
XDEV	       *dev;
X{
X    if (dev->md_Rawbuffer) {    /* OIS */
X	FreeMem(dev->md_Rawbuffer, (long) RLEN + 2);
X    }
X}
X
X/*
X * This routine doesn't write to the disk, but updates the TrackBuffer to
X * respect the new sector. We have to be sure the TrackBuffer is filled
X * with the current Track. As GetSTS calls Internal_Update if the track
X * changes we don't have to bother about actually writing any data to the
X * disk. GetSTS has to be changed in the following way:
X *
X * if (track != mu_CurrentTrack || side != mu_CurrentSide) { Internal_Update(); for
X * (i = 0; i < MS_SPT; i++) ..... etc.
X */
X
Xvoid
XCMD_Write(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    int 	    side;
X    int 	    cylinder;
X    int 	    sector;
X    byte	   *userbuf;
X    long	    length;
X    long	    offset;
X    word	    spt;
X
X    debug(("CMD_Write "));
X    userbuf = (byte *) ioreq->iotd_Req.io_Data;
X    length = ioreq->iotd_Req.io_Length / MS_BPS;	/* Sector count */
X    offset = ioreq->iotd_Req.io_Offset / MS_BPS;	/* Sector number */
X    debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
X
X    spt = unit->mu_SectorsPerTrack;
X    cylinder = offset / spt;
X    side = cylinder % MS_NSIDES;
X    cylinder /= MS_NSIDES;
X    sector = offset % spt;
X    debug(("T=%d Si=%d Se=%d\n", cylinder, side, sector));
X
X    ioreq->iotd_Req.io_Actual = 0;
X
X    if (length <= 0 || CheckRequest(ioreq, unit))
X	goto end;
X
X    GetTrack(ioreq, side, cylinder);
X    for (;;) {
X	CopyMem(userbuf, unit->mu_TrackBuffer + MS_BPS * sector, (long) MS_BPS);
X	unit->mu_TrackChanged = 1;
X	unit->mu_SectorStatus[sector] = CRC_CHANGED;
X
X	ioreq->iotd_Req.io_Actual += MS_BPS;
X	if (--length <= 0)
X	    break;
X	userbuf += MS_BPS;
X	/*
X	 * Get next sequential sector/side/track
X	 */
X	if (++sector >= spt) {
X	    sector = 0;
X	    if (++side >= MS_NSIDES) {
X		side = 0;
X		if (++cylinder >= unit->mu_NumCyls)
X		    goto end;
X	    }
X	    GetTrack(ioreq, side, cylinder);
X	}
X    }
X
X    if (length)
X	ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X
Xend:
X    TermIO(ioreq);
X}
X
X/*
X * This is called by your GetSTS() routine if the Track has changed. It
X * writes the changes back to the disk (a whole track at a time). It has
X * to be called if your device gets a CLOSE instruction too.
X */
X
Xvoid
XInternal_Update(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT  *unit;
X{
X    debug(("Internal_Update "));
X    /* did we have a changed sector at all	 */
X    if (unit->mu_TrackChanged != 0) {
X	debug(("needs to write "));
X
X	if (unit->mu_SectorsPerTrack > unit->mu_CurrentSectors)
X	    unit->mu_CurrentSectors = unit->mu_SectorsPerTrack;
X
X	/*
X	 * Only recalculate the CRC on changed sectors. This way, a
X	 * sector with a bad CRC won't suddenly be ``repaired''.
X	 */
X	{
X	    register int i;
X
X	    for (i = unit->mu_CurrentSectors - 1; i >= 0; i--) {
X		if (unit->mu_SectorStatus[i] == CRC_CHANGED) {
X		    unit->mu_CrcBuffer[i] = DataCRC(unit->mu_TrackBuffer + i * MS_BPS);
X		    debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
X		}
X	    }
X	}
X	{
X	    DEV 	   *dev;
X	    register struct IOExtTD *tdreq;
X	    word	    SectorGap;
X
X	    dev = (DEV *) ioreq->iotd_Req.io_Device;
X	    tdreq = unit->mu_DiskIOReq;
X	    SectorGap = CalculateGapLength(unit->mu_CurrentSectors);
X
X	    ObtainSemaphore(&dev->md_HardwareUse);
X	    EncodeTrack(unit->mu_TrackBuffer, dev->md_Rawbuffer,
X			unit->mu_CrcBuffer,
X			unit->mu_CurrentTrack, unit->mu_CurrentSide,
X			SectorGap, unit->mu_CurrentSectors);
X
X	    TDMotorOn(tdreq);
X	    if (TDSeek(unit, ioreq, unit->mu_CurrentTrack)) {
X		debug(("Seek error\n"));
X		ioreq->iotd_Req.io_Error = TDERR_SeekError;
X		goto end;
X	    }
X	    HardwareIO(dev, unit, DSKWRITE);
X
X	    ReleaseSemaphore(&dev->md_HardwareUse);
X	    unit->mu_TrackChanged = 0;
X	}
X    }
Xend:
X    debug(("done\n"));
X}
X
X/*
X * TD_Format writes one or more whole tracks without reading them first.
X */
X
Xvoid
XTD_Format(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
X    DEV 	   *dev;
X    short	    side;
X    int 	    cylinder;
X    byte	   *userbuf;
X    int 	    length;
X    word	    spt;
X    word	    gaplen;
X
X    debug(("CMD_Format "));
X
X    if (CheckRequest(ioreq, unit))
X	goto termio;
X
X    userbuf = (byte *) ioreq->iotd_Req.io_Data;
X    length = ioreq->iotd_Req.io_Length / MS_BPS;	    /* Sector count */
X    cylinder = ioreq->iotd_Req.io_Offset / MS_BPS;	    /* Sector number */
X    /*
X     * Now try to guess the number of sectors the user wants per track.
X     * 40 sectors is the first ambiguous length.
X     */
X    if (length <= MS_SPT_MAX)
X	spt = length;
X    else if (length < 40) {
X	if (length > 0) {
X	    for (spt = 8; spt <= MS_SPT_MAX; spt++) {
X		if ((length % spt) == 0)
X		    goto found_spt;
X	    }
X	}
X	/*
X	 * Not 8, 16, 24, 32, 9, 18, 27, 36, 10, 20, or 30? That is an error.
X	 */
X	ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
X	goto termio;
X    } else  /* assume previous number */
X	spt = unit->mu_SectorsPerTrack;
X
Xfound_spt:
X    gaplen = CalculateGapLength(spt);
X
X    /*
X     * Assume the whole disk will have this layout.
X     */
X    unit->mu_SectorsPerTrack = spt;
X
X    length /= spt;
X    cylinder /= spt;
X
X    side = cylinder % MS_NSIDES;
X    cylinder /= MS_NSIDES;
X    debug(("userbuf %08lx cylinder %d len %d\n", userbuf, cylinder, length));
X
X    ioreq->iotd_Req.io_Actual = 0;
X
X    /*
X     * Write out the current track if we are not going to overwrite it.
X     * After the format operation, the buffer is invalidated.
X     */
X    if (cylinder <= unit->mu_CurrentTrack &&
X	unit->mu_CurrentTrack < cylinder + length)
X	Internal_Update(ioreq, unit);
X
X    dev = (DEV *) ioreq->iotd_Req.io_Device;
X
X    while (length > 0) {
X	{
X	    register int i;
X
X	    for (i = spt - 1; i >= 0; i--) {
X		unit->mu_CrcBuffer[i] = DataCRC(userbuf + i * MS_BPS);
X		debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
X	    }
X	}
X	ObtainSemaphore(&dev->md_HardwareUse);
X	EncodeTrack(userbuf, dev->md_Rawbuffer, unit->mu_CrcBuffer,
X		    cylinder, side,
X		    gaplen, spt);
X
X	TDMotorOn(tdreq);
X	if (TDSeek(unit, ioreq, cylinder)) {
X	    debug(("Seek error\n"));
X	    ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
X	    break;
X	}
X	unit->mu_CurrentSide = side;
X	HardwareIO(dev, unit, DSKWRITE);
X
X	ReleaseSemaphore(&dev->md_HardwareUse);
X
X	length--;
X	userbuf += MS_BPS * spt;
X	ioreq->iotd_Req.io_Actual += MS_BPS * spt;
X
X	if (++side >= MS_NSIDES) {
X	    side = 0;
X	    if (++cylinder >= unit->mu_NumCyls)
X		goto end;
X	}
X    }
Xend:
X    unit->mu_CurrentSide = -1;
X    unit->mu_TrackChanged = 0;
Xtermio:
X    TermIO(ioreq);
X}
X/* INDENT OFF */
X
X#asm
X
X; we need a buffer for the Sector-ID field to calculate its checksum
X;SectorHeader:
X;	 dc.b	 0		   ; track
X;	 dc.b	 0		   ; side
X;	 dc.b	 0		   ; sector
X;	 dc.b	 2		   ; length (2=512 bytes)
X;	 dc.w	 0		   ; CRC
X
X	public _EncodeTrack
X
X;   EncodeTrack(TrackBuffer, Rawbuffer, Crcs, Track, Side, GapLen, NumSecs)
X;		4	     4		4     2      2	   2	   2
X
X_EncodeTrack:
X	movem.l d2-d7/a2-a6,-(sp)  ; save registers
X
Xfp	set	(4*(6+5))+4        ; 4 for return address
Xtrackbf set	0
Xrawbf	set	4
Xcrcs	set	8
Xtrack	set	12
Xside	set	14
Xgaplen	set	16
Xnumsecs set	18
X
X; a0	ptr in encoded data (also putmfmbyte)
X; a2	ptr to mfm encoding table (putmfmbyte)
X; a3	ptr to data to be crc'd (HeaderCRC)
X; a4	ptr to table with calculated CRC's
X; a5	ptr to unencoded data
X
X; d0	byte to be encoded (putmfmbyte)
X; d1	trashed by putmfmbyte
X; d3	used by putmfmbyte
X; d5	sector number
X; d6	general counter of byte spans
X; d7	sector countdown
X
X	sub.w	#2,fp+gaplen(sp)   ; gap length between sectors
X	move.l	fp+rawbf(sp),a0    ; pointer to mfmencoded buffer
X	move.l	fp+crcs(sp),a4     ; pointer to precalculated CRCs
X	move.l	fp+trackbf(sp),a5  ; pointer to unencoded data
X	lea	_MfmEncode,a2	   ; pointer to MFM lookup table
X
X	move.w	#$9254,d0	   ; a track starts with a gap ($4e)
X	moveq	#INDEXGAP-1,d6
Xingl1	move.w	d0,(a0)+           ; mfmencoded = $9254
X	dbf	d6,ingl1
X
X	move.w	#$aaaa,d0	   ; a track index starts with a gap containing
X	moveq	#INDXGAP-1,d6	   ; 12 * 0 (mfm = $aaaa)
Xingl2	move.w	d0,(a0)+
X	dbf	d6,ingl2
X
X	move.w	#INDEXSYNC,d0	   ; The INDEX field begins here, starting
X	move.w	d0,(a0)+           ; with 3 syncs (3 * $c2) with a missing
X	move.w	d0,(a0)+           ; clock bit
X	move.w	d0,(a0)+
X
X	move.w	#$5552,(a0)+       ; INDEX mark ($fc)
X
X	move.w	#$9254,d0	   ; more gap
X	moveq	#INDEXGAP2-1,d6
Xingl3	move.w	d0,(a0)+           ; mfmencoded = $9254
X	dbf	d6,ingl3
X
X	lea	-6(sp),sp          ; Reserve room for SectorHeader
Xfp	set	fp+6
X	move.w	fp+numsecs(sp),d7  ; number of sectors to encode
X	subq.w	#1,d7		   ; minus 1 for dbra
X	moveq	#0,d5		   ; start with first sector
X
Xsecloop:
X	move.w	#$aaaa,d0	   ; a sector starts with a gap containing
X	moveq	#IDGAP2-1,d6	   ; 12 * 0 (mfm = $aaaa)
Xid2gl	move.w	d0,(a0)+
X	dbf	d6,id2gl
X
X	move.w	#SYNC,d0	   ; The ID field begins here, starting
X	move.w	d0,(a0)+           ; with 3 syncs (3 * $a1) with a missing
X	move.w	d0,(a0)+           ; clock bit
X	move.w	d0,(a0)+
X
X	move.w	#$5554,(a0)+       ; ID-Address mark ($fe)
X
X	move.l	sp,a3		   ; pointer to Sector-ID buffer
X
X	moveq	#$5554&1,d3	   ; preload d3 for the putmfmbyte routine
X	move.b	fp+track+1(sp),0(a3)  ; insert current track number
X	move.b	fp+side+1(sp),1(a3)   ; side number
X	addq.w	#1,d5		   ; sectors start with 1 instead of 0
X	move.b	d5,2(a3)           ; sector number
X	move.b	#MS_BPScode,3(a3)  ; sector length 512 bytes
X	bsr	HeaderCRC	   ; calculate checksum
X	move.w	d0,IDDATA(a3)      ; put it past the data
X
X	moveq	#IDDATA+IDCRC-1,d6 ; 6 bytes Sector-ID
Xsidl	move.b	(a3)+,d0           ; get one byte
X	bsr	putmfmbyte	   ; encode it
X	dbf	d6,sidl 	   ; end of buffer ?
X
X	moveq	#$4e,d0 	   ; recalculate the MFM value of the
X	bsr	putmfmbyte	   ; first gap byte
X
X	moveq	#DATAGAP1-1-1,d6   ; GAP consisting of
X	move.w	#$9254,d0	   ; 22 * $4e
Xdg1l	move.w	d0,(a0)+
X	dbf	d6,dg1l
X
X	moveq	#DATAGAP2-1,d6	   ; GAP consisting of
X	move.w	#$aaaa,d0	   ; 12 * 0 (mfm = $aaaa)
Xdg2l	move.w	d0,(a0)+
X	dbf	d6,dg2l
X
X	move.w	#SYNC,d0	   ; Sector data
X	move.w	d0,(a0)+           ; starts with 3 syncs
X	move.w	d0,(a0)+
X	move.w	d0,(a0)+
X
X	move.w	#$5545,(a0)+       ; Data Address Mark ($fb)
X
X	moveq	#$5545&1,d3	   ; preload d3
X	move	#MS_BPS-1,d6	   ; a sector has 512 bytes
Xdblockl move.b	(a5)+,d0           ; get one byte from the buffer
X	bsr	putmfmbyte	   ; encode it
X	dbf	d6,dblockl	   ; end of sector ?
X
X	move.b	(a4)+,d0           ; get first byte of CRC
X	bsr	putmfmbyte	   ; encode it
X	move.b	(a4)+,d0           ; get second byte
X	bsr	putmfmbyte	   ; encode it
X
X	moveq	#$4e,d0 	   ; recalculate the MFM value of the
X	bsr	putmfmbyte	   ; first gap byte -> -1 in following loop
X
X;	moveq	#DATAGAP3-1-1,d6   ; sector ends with a gap
X	move.w	fp+gaplen(sp),d6   ; sector ends with a gap, -1 for dbf
X	move.w	#$9254,d0	   ; 80 * $4e
Xdg3l	move.w	d0,(a0)+
X	dbf	d6,dg3l
X
X	dbf	d7,secloop	   ; next sector. d5 has been incremented
X
X	lea	6(sp),sp           ; Free room for SectorHeader
Xfp	set	fp-6
X
X	move.l	fp+rawbf(sp),d6    ; pointer to mfmencoded buffer
X	add.l	#WLEN-2,d6	   ; end of encoded buffer (-2 for dbf)
X	move.l	a0,d0		   ; (I really want to   sub.l a0,d6 )
X	sub.l	d0,d6		   ; length of the remains
X	lsr.l	#1,d6		   ; turn into words
X
X	move.w	#$9254,d0	   ; Fill the end of the track with $4e
Xendgl	move.w	d0,(a0)+           ; $9254 mfm encoded
X	dbf	d6,endgl
X
X	movem.l (sp)+,d2-d7/a2-a6
X	rts
X
X; putmfmbyte encodes one byte (in D0) into MSDOS MFM format to the location
X; pointed by A0. D3 has to be preserved between calls !
X; A2 must contain the pointer to the encoding table.
X; Destroys D0, D1. Updates A0 and D3. Requires A0, D0, D3.
X
Xputmfmbyte
X	moveq	#16-4,d1
X	lsl.l	d1,d0		; split the byte into two nibbles
X	lsr.w	d1,d0		; low nibble is in bits 0..15
X				; high nibble in bits 16..31
X	swap	d0		; process high nibble first
X	and.w	#$0f,d0 	; mask out unwanted bits
X	move.b	0(a2,d0.w),d1   ; get mfmencoded nibble from table
X	btst	#6,d1		; we now have to work out if
X	bne.s	1$		; the high bit of the unencoded data
X	btst	#0,d3		; byte and the low bit of the last
X	bne.s	1$		; encoded data are both 0. if this is the
X	bset	#7,d1		; case the first clock bit has to be '1'
X1$	move.b	d1,(a0)+        ; write high (encoded) nibble
X	swap	d0		; process low nibble
X	move.b	0(a2,d0.w),d3   ; ....same as above
X	btst	#6,d3
X	bne.s	2$
X	btst	#0,d1
X	bne.s	2$
X	bset	#7,d3
X2$	move.b	d3,(a0)+
X	rts
X
X#endasm
X#endif				/* READONLY */
X#asm
X
X; The CRC is computed not only over the actual data, but including
X; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
X; As we don't read or encode these fields into our buffers, we have to
X; preload the registers containing the CRC with the values they would have
X; after stepping over these fields.
X;
X; How CRCs "really" work:
X;
X; First, you should regard a bitstring as a series of coefficients of
X; polymomials. We calculate with these polynomials in modulo-2
X; arithmetic, in which both add and subtract are done the same as
X; exclusive-or. Now, we modify our data (a very long polynomial) in
X; such a way that it becomes divisible by the CCITT-standard 16-bit
X;		 16   12   5
X; polynomial:	x  + x	+ x + 1, represented by $11021. The easiest
X; way to do this would be to multiply (using proper arithmetic) our
X; datablock with $11021. So we have:
X;   data * $11021		 =
X;   data * ($10000 + $1021)      =
X;   data * $10000 + data * $1021
X; The left part of this is simple: Just add two 0 bytes. But then
X; the right part (data $1021) remains difficult and even could have
X; a carry into the left part. The solution is to use a modified
X; multiplication, which has a result that is not correct, but with
X; a difference of any multiple of $11021. We then only need to keep
X; the 16 least significant bits of the result.
X;
X; The following algorithm does this for us:
X;
X;   unsigned char *data, c, crclo, crchi;
X;   while (not done) {
X;	c = *data++ + crchi;
X;	crchi = (@ c) >> 8 + crclo;
X;	crclo = @ c;
X;   }
X;
X; Remember, + is done with EOR, the @ operator is in two tables (high
X; and low byte separately), which is calculated as
X;
X;      $1021 * (c & $F0)
X;  xor $1021 * (c & $0F)
X;  xor $1021 * (c >> 4)         (* is regular multiplication)
X;
X;
X; Anyway, the end result is the same as the remainder of the division of
X; the data by $11021. I am afraid I need to study theory a bit more...
X
X
X; This is the entry to calculate the checksum for the sector-id field
X; requires:  a3 = pointer to the unencoded data
X; returns:   d0 = CRC
X
XHeaderCRC:
X	movem.l  d1-d3/a3-a5,-(sp) ; save registers
X	move.w	 #$b2,d0	   ; preload registers
X	moveq	 #$30,d1	   ; (CRC for $a1,$a1,$a1,$fb)
X	moveq	 #3,d3		   ; calculate checksum for 4 bytes
X	bra.s	 getCRC 	   ; (=track,side,sector,sectorlength)
X
X; This is the entry to calculate the checksum for the data field
X; requires:  a3 = pointer to the unencoded data
X; returns:   d0 = CRC
X
X; C entry point for DataCRC(byte *data)
X
X_DataCRC:
X	movem.l d1-d3/a3-a5,-(sp)  ; save registers
Xfp	set	(4*(3+3))+4
Xdata	set	0
X	move.l	fp+data(sp),a3     ; get parameter
X	move.w	 #$e2,d0	   ; preload the CRC registers
X	move.w	 #$95,d1	   ; (CRC for $a1,$a1,$a1,$fe)
X	move.w	 #MS_BPS-1,d3	   ; a sector 512 bytes
X
XgetCRC	lea	 CRCTable1,a4
X	lea	 CRCTable2,a5
X	moveq	 #0,d2
X
X1$	move.b	 (a3)+,d2
X	eor.b	 d0,d2
X	move.b	 0(a4,d2.w),d0
X	eor.b	 d1,d0
X	move.b	 0(a5,d2.w),d1
X	dbf	 d3,1$
X
X	lsl.w	 #8,d0
X	move.b	 d1,d0
X	movem.l  (sp)+,d1-d3/a3-a5
X	rts
X
X
XCRCTable1:
X	dc.b $00,$10,$20,$30,$40,$50,$60,$70,$81,$91,$a1,$b1,$c1,$d1,$e1,$f1
X	dc.b $12,$02,$32,$22,$52,$42,$72,$62,$93,$83,$b3,$a3,$d3,$c3,$f3,$e3
X	dc.b $24,$34,$04,$14,$64,$74,$44,$54,$a5,$b5,$85,$95,$e5,$f5,$c5,$d5
X	dc.b $36,$26,$16,$06,$76,$66,$56,$46,$b7,$a7,$97,$87,$f7,$e7,$d7,$c7
X	dc.b $48,$58,$68,$78,$08,$18,$28,$38,$c9,$d9,$e9,$f9,$89,$99,$a9,$b9
X	dc.b $5a,$4a,$7a,$6a,$1a,$0a,$3a,$2a,$db,$cb,$fb,$eb,$9b,$8b,$bb,$ab
X	dc.b $6c,$7c,$4c,$5c,$2c,$3c,$0c,$1c,$ed,$fd,$cd,$dd,$ad,$bd,$8d,$9d
X	dc.b $7e,$6e,$5e,$4e,$3e,$2e,$1e,$0e,$ff,$ef,$df,$cf,$bf,$af,$9f,$8f
X	dc.b $91,$81,$b1,$a1,$d1,$c1,$f1,$e1,$10,$00,$30,$20,$50,$40,$70,$60
X	dc.b $83,$93,$a3,$b3,$c3,$d3,$e3,$f3,$02,$12,$22,$32,$42,$52,$62,$72
X	dc.b $b5,$a5,$95,$85,$f5,$e5,$d5,$c5,$34,$24,$14,$04,$74,$64,$54,$44
X	dc.b $a7,$b7,$87,$97,$e7,$f7,$c7,$d7,$26,$36,$06,$16,$66,$76,$46,$56
X	dc.b $d9,$c9,$f9,$e9,$99,$89,$b9,$a9,$58,$48,$78,$68,$18,$08,$38,$28
X	dc.b $cb,$db,$eb,$fb,$8b,$9b,$ab,$bb,$4a,$5a,$6a,$7a,$0a,$1a,$2a,$3a
X	dc.b $fd,$ed,$dd,$cd,$bd,$ad,$9d,$8d,$7c,$6c,$5c,$4c,$3c,$2c,$1c,$0c
X	dc.b $ef,$ff,$cf,$df,$af,$bf,$8f,$9f,$6e,$7e,$4e,$5e,$2e,$3e,$0e,$1e
X
XCRCTable2:
X	dc.b $00,$21,$42,$63,$84,$a5,$c6,$e7,$08,$29,$4a,$6b,$8c,$ad,$ce,$ef
X	dc.b $31,$10,$73,$52,$b5,$94,$f7,$d6,$39,$18,$7b,$5a,$bd,$9c,$ff,$de
X	dc.b $62,$43,$20,$01,$e6,$c7,$a4,$85,$6a,$4b,$28,$09,$ee,$cf,$ac,$8d
X	dc.b $53,$72,$11,$30,$d7,$f6,$95,$b4,$5b,$7a,$19,$38,$df,$fe,$9d,$bc
X	dc.b $c4,$e5,$86,$a7,$40,$61,$02,$23,$cc,$ed,$8e,$af,$48,$69,$0a,$2b
X	dc.b $f5,$d4,$b7,$96,$71,$50,$33,$12,$fd,$dc,$bf,$9e,$79,$58,$3b,$1a
X	dc.b $a6,$87,$e4,$c5,$22,$03,$60,$41,$ae,$8f,$ec,$cd,$2a,$0b,$68,$49
X	dc.b $97,$b6,$d5,$f4,$13,$32,$51,$70,$9f,$be,$dd,$fc,$1b,$3a,$59,$78
X	dc.b $88,$a9,$ca,$eb,$0c,$2d,$4e,$6f,$80,$a1,$c2,$e3,$04,$25,$46,$67
X	dc.b $b9,$98,$fb,$da,$3d,$1c,$7f,$5e,$b1,$90,$f3,$d2,$35,$14,$77,$56
X	dc.b $ea,$cb,$a8,$89,$6e,$4f,$2c,$0d,$e2,$c3,$a0,$81,$66,$47,$24,$05
X	dc.b $db,$fa,$99,$b8,$5f,$7e,$1d,$3c,$d3,$f2,$91,$b0,$57,$76,$15,$34
X	dc.b $4c,$6d,$0e,$2f,$c8,$e9,$8a,$ab,$44,$65,$06,$27,$c0,$e1,$82,$a3
X	dc.b $7d,$5c,$3f,$1e,$f9,$d8,$bb,$9a,$75,$54,$37,$16,$f1,$d0,$b3,$92
X	dc.b $2e,$0f,$6c,$4d,$aa,$8b,$e8,$c9,$26,$07,$64,$45,$a2,$83,$e0,$c1
X	dc.b $1f,$3e,$5d,$7c,$9b,$ba,$d9,$f8,$17,$36,$55,$74,$93,$b2,$d1,$f0
X
X#endasm
X
X/* INDENT ON */
END_OF_FILE
if test 48145 -ne `wc -c <'src/devio.c'`; then
    echo shar: \"'src/devio.c'\" unpacked with wrong size!
fi
# end of 'src/devio.c'
fi
echo shar: End of archive 6 \(of 6\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.