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.