#include "DFC5.h" extern struct MsgPort *TaskPort[6], *MainPort, *TPort; extern struct IMsg IMsg[6]; static char *TaskBuffer[5]; /* local buffers (for doing I/O) */ static char *Buffer[80]; /* pointers to the 80 buffer chunks */ static long CheckSum[80]; /* checksums of the internal RAM buffer */ struct IOStdReq *IOStdReq[4]; /* our I/O request blocks */ /* * This routine attempts to allocate 80 buffers. Return 0 on failure, otherwise * the denominator of the fraction of a disk allocated (1=entire disk, 2=half * disk, etc. upto 4). Note than when 3 is returned, actually 80/3+1 tracks * were allocated. */ int AllocBuffers(void) { register int i; for (i=0; i<80; i++) if ((Buffer[i]=AllocMem((long)TRACKSIZE, MEMF_PUBLIC))==NULL) { if (i>=40) { FreeBuffers(40); return(2); } else if (i>=27) { FreeBuffers(27); return(3); } else if (i>=20) { FreeBuffers(20); return(4); } else { FreeBuffers(0); return(0); } } return(1); } /* * And this routine lets them go from a defined index. */ void FreeBuffers(int i) { while(i<80) { if (Buffer[i]) { FreeMem(Buffer[i], TRACKSIZE); Buffer[i] = NULL; } i++; } } /* * We attempt to create an I/O request. We allocate memory for it and * for its port. */ struct IOStdReq *CreateIOStdReq(void) { register struct IOStdReq *IOStdReq; if (!(IOStdReq = AllocMem(sizeof(struct IOStdReq), MEMF_CLEAR | MEMF_PUBLIC))) return (NULL); IOStdReq->io_Message.mn_Node.ln_Type = NT_MESSAGE; IOStdReq->io_Message.mn_Length = sizeof(struct IOStdReq)-sizeof(struct Message); if ((IOStdReq->io_Message.mn_ReplyPort = CreatePort(NULL,0))==NULL) { FreeMem(IOStdReq, sizeof(struct IOStdReq)); return(NULL); } return(IOStdReq); } /* * This routine frees an I/O request. */ void DeleteIOStdReq(struct IOStdReq *IOStdReq) { DeletePort(IOStdReq->io_Message.mn_ReplyPort); FreeMem(IOStdReq, sizeof(struct IOStdReq)); } /* * This routine recalculates the checksum for a block, and updates it in * word 5. The sum of all the words in a block must be 0. */ void ReCheck(long *w) { register int i; register long CheckSum; CheckSum = 0; for (i=0; i<128; i++) CheckSum += w[i]; w[5] -= CheckSum; } /* * This routine checksums a track of the internal RAM buffer. */ long Check(long *Track) { register int i; register long CheckSum = 0; for (i=0; i<(TRACKSIZE/sizeof(long)); i++) CheckSum += Track[i]; return(CheckSum); } /* * We simply DateStamp the creation date, modification date, and * rechecksum the block. A random factor is added because of multiple * async accesses. */ void UpdateRootBlock(char *b) { static unsigned char Rand; Rand += 7; DateStamp((void *)(b + 420)); DateStamp((void *)(b + 484)); ((struct DateStamp *)(b + 420))->ds_Tick = (((struct DateStamp *)(b + 420))->ds_Tick+Rand)%3000; ((struct DateStamp *)(b + 484))->ds_Tick = (((struct DateStamp *)(b + 484))->ds_Tick+Rand)%3000; ReCheck((void *)b); } /* * This routine creates data for the format option. Note the clever * way the data is built up; this routine should build a disk exactly * the same way the standard AmigaDOS format does. If we are on track * 40, some additional work must be done to create a root block and * bitmap, but it's not too bad. I don't know if this is the right * way to do under FFS, but since it works . . . */ void MakeFormatData(int Track, char *b, int UseFFS) { register long *p; register long cs; register long i; register unsigned char *q; p = (long *)b; cs = (((ULONG)('D') << 24) | ((ULONG)('O') << 16) | ((ULONG)('S') << 8) | (UseFFS ? '\1' : '\0')) + (((long)Track & 48) << 16); for (i=0; i=0) { fcpy(p, Buffer[Track]); CheckSum[Track] = Check((long *)Buffer[Track]); } } /* * Now we try to allocate a disk. First, we attempt to allocate an * I/O request, and then we actually open the device. If either of * these fail, we return 0. This can occur if a drive is opened that * doesn't exist. */ int OpenDisk(int Unit) { register struct IOStdReq **p = &IOStdReq[Unit]; if (*p) return(1); if ((*p = CreateIOStdReq()) && OpenDevice(TD_NAME, Unit, (void *)*p, 0) == 0) return(1); else { if (*p) { DeleteIOStdReq(*p); *p = NULL; } return(0); } } /* * Here we release a disk. We check that we have it first! Then, we * close the device, kill the I/O request, and exit. */ void CloseDisk(int Unit) { register struct IOStdReq **p = &IOStdReq[Unit]; if (*p) { CloseDevice((void *)*p); DeleteIOStdReq(*p); *p = NULL; } } /* * Here we do our main track I/O with IOStdReq. The routine returns a parsed error field. * Notice that the I/O is sync (much simpler), but the overall design of the program * makes it automagically async-like. */ int DoTDIO(int Unit, int Track, void *Buffer, int Command, int Size) { if (Unit>3) return(0); IOStdReq[Unit]->io_Length = Size; IOStdReq[Unit]->io_Data = Buffer; IOStdReq[Unit]->io_Command = Command; IOStdReq[Unit]->io_Offset = Track * (long)TRACKSIZE; DoIO((void *)IOStdReq[Unit]); if (IOStdReq[Unit]->io_Error == TDERR_DiskChanged) return(NO_DISK); else if (IOStdReq[Unit]->io_Error == TDERR_WriteProt) return(WRITE_PROTECTED); else if (IOStdReq[Unit]->io_Error) return(GENERIC_ERROR); else return(0); } /* * This code is executed by (max) *FIVE* tasks at the same time. It manages a disk * (which is deduced from the UserData of the task structure) using a message passing * interface. If the unit number is 4, it manages the internal 80 tracks buffer. * This code is executed at priority 1. */ __saveds void DiskTask(void) { void *VerifyBuffer; int AllocFlag, Passes; register struct IMsg *Message; register int Unit = (int)FindTask(NULL)->tc_UserData; register int n; AllocFlag = ((TaskPort[Unit] = CreatePort(NULL, 0)) != NULL); if (Unit<4) { AllocFlag &= OpenDisk(Unit); AllocFlag &= ((VerifyBuffer = AllocMem(TRACKSIZE, MEMF_CHIP))!=NULL); AllocFlag &= ((TaskBuffer[Unit] = AllocMem(TRACKSIZE, MEMF_CHIP))!=NULL); } else AllocFlag &= ((Passes = IMsg[Unit].im_n = AllocBuffers()) != 0); IMsg[Unit].im_RC = AllocFlag; if (!AllocFlag) goto GameOver; PutMsg(TPort, (struct Message *)&IMsg[Unit]); SetTaskPri(FindTask(NULL), 1); FOREVER { WaitPort(TaskPort[Unit]); while(Message = (void *)GetMsg(TaskPort[Unit])) { Message->im_RC = 0; n = Message->im_n; if (Unit == 4) n = n % (80/Passes+(Passes==3)); switch(Message->im_Action) { case INIT: if (n<0 && Unit<4) { if (!DoTDIO(Unit, 0, VerifyBuffer, CMD_READ, 512) && (*((unsigned long *)VerifyBuffer) != (((ULONG)('D') << 24) | ((ULONG)('O') << 16) | ((ULONG)('S') << 8) | '\0') && *((unsigned long *)VerifyBuffer) != (((ULONG)('D') << 24) | ((ULONG)('O') << 16) | ((ULONG)('S') << 8) | '\1'))) Message->im_RC = NOT_DOS; n = 79; } DoTDIO(Unit, n, 0, TD_SEEK, TRACKSIZE); break; case STOP_MOTOR: DoTDIO(Unit, 0, 0, TD_MOTOR, 0); break; case READ_TRACK : if (Unit<4) { if ((Message->im_RC = DoTDIO(Unit, n, TaskBuffer[Unit], CMD_READ, TRACKSIZE)) == GENERIC_ERROR) Message->im_RC = READ_ERROR; Message->im_p = TaskBuffer[Unit]; } else { if (CheckSum[n] != Check((long *)Buffer[n])) Message->im_RC = READ_ERROR; Message->im_p = Buffer[n]; } break; case WRITE_TRACK : case WRITE_AND_VERIFY_TRACK: if (Unit<4) { if ((Message->im_RC = DoTDIO(Unit, n, TaskBuffer[Unit], TD_FORMAT, TRACKSIZE)) == GENERIC_ERROR) Message->im_RC = WRITE_ERROR; if (Message->im_Action == WRITE_TRACK || Message->im_RC) break; DoTDIO(Unit, n, VerifyBuffer, CMD_READ, TRACKSIZE); Message->im_RC = (fcmp(TaskBuffer[Unit], VerifyBuffer) ? VERIFY_ERROR : 0); } break; case EXIT: goto GameOver; } ReplyMsg((void *)Message); } } GameOver: SetTaskPri(FindTask(NULL), 127); if (TaskPort[Unit]) { DeletePort(TaskPort[Unit]); TaskPort[Unit] = NULL; } if (Unit<4) { if (VerifyBuffer) FreeMem(VerifyBuffer, TRACKSIZE); if (TaskBuffer[Unit]) FreeMem(TaskBuffer[Unit], TRACKSIZE); TaskBuffer[Unit] = NULL; CloseDisk(Unit); } else FreeBuffers(0); PutMsg(TPort, (struct Message *)&IMsg[Unit]); return; }