-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLispDisk.mc
More file actions
629 lines (554 loc) · 25.2 KB
/
Copy pathLispDisk.mc
File metadata and controls
629 lines (554 loc) · 25.2 KB
1
*-----------------------------------------------------------Title[LispDisk.mc,July 16, 1985 2:32 PM ,Masinter];* February 10, 1985 10:40 PM, Masinter* allow for more partitions at expense of less error checking* Jan 1984, Taft* Emulates an Alto Diablo (double model-44) disk on one surface* (or "partition") of the Dorado's Trident disk 0.*-----------------------------------------------------------*-----------------------------------------------------------% Data structures*-----------------------------------------------------------KBLK format: VM 521: pointer to disk command block (KCB) VM 522: status at beginning of current sector VM 523: disk address of most recently started disk command VM 524: sector interrupt bit maskKCB format: KCB+0: pointer to next KCB KCB+1: ending status KCB+2: command KCB+3: pointer to header block KCB+4: pointer to label block KCB+5: pointer to data block KCB+6: no-error completion interrupt bit mask KCB+7: error completion interrupt bit mask KCB+10: unused KCB+11: disk addressDisk address format: 0-3: sector number 4-12: cylinder number 13: head number 14: drive number (XORed with command bit 15) 15: restore requestDisk command format: 0-7: partition number + seal (lsh 11B 4)( was 0-4: seal (= 11B) 5-7: partition number (0 = default, 1-7 = specific partition; note that it's impossible to select partitions 8-19 by this means)) 8-9: header operation (0 = read, 1 = check, 2 or 3 = write) 10-11: label operation 12-13: data operation 14: seek only 15: complement drive number of disk addressDisk status format: 0-3: sector number 4-7: 17B 8: seek failed 9: seek in progress 10: unit not ready 11: data late 12: no data transferred 13: data checksum error 14-15: completion code: 0 = normal, 1 = hardware error, 2 = check error, 3 = illegal sectorAlto emulation is performed only on drive 0, which may be either a T-80 oran AMS-315. A Trident disk is formatted as 815 cylinders, 5 heads, 29 sectors.An AMS-315 disk is formatted as 815 cylinders, 19 heads, 29 sectors.Diablo disk addresses are mapped onto the Dorado disk as follows: Dorado cylinder = 406*(Diablo drive) + Diablo cylinder +3 Dorado head = (partition number)+1 Dorado sector = nSectorsDiablo*(Diablo head) + Diablo sector%* Disk format parameters:MC[nSectorsDiablo, 16]; * Emulated # of sectors (14B or 16B)MC[offsetCylinderDiablo, 3]; * Cylinders reserved at beginning of diskSet[interleaveSectors, 0]; * 1 to interleave sectors 3:1, 0 notSet[staggerSectors, 1]; * Stagger sectors on adjacent cylindersSet[XTask, IP[DSK]];TopLevel;*-----------------------------------------------------------* Disk task initialization*-----------------------------------------------------------Subroutine;DSKInitPC: T_ DSK, CoReturn;TopLevel; T_ A0, RBase_ RBase[DiskRegs]; K400_ 400C, Call[ClearDisk]; * Disable controller, clear wakeups TridentFlag_ A0, MemBase_ MDS, Call[InitRamDiablo]; * Init format Ram for Diablo* Attempt to select a head that is legal on an AMS-315, illegal on a T-80.* By looking at the error status, we can see what type of drive we have. T_ KSelect, Call[SendDriveTag]; * Select the drive T_ 5C; * Head 5 T_ T OR (tagHead), Call[SendTag]; KTemp0_ muffHeadOvfl, Call[Read1Muff]; T_ tagDiskReset, KTemp0, Branch[.+2, R odd]; * Did we get a HeadOverflow error? MaxPartition_ 23C, Branch[.+2]; * No, this is an AMS-315 (19 heads) MaxPartition_ 5C; * Yes, this is a T-80 (5 heads) T_ T OR (tagControl), Call[SendTag]; * Reset error status KStatus_ A0, Branch[AForgetCmmd];*-----------------------------------------------------------* SIO instruction* Diablo disk does not use SIO, but Trident disk does.* If TriDisk.mc is loaded, this label gets redefined.*-----------------------------------------------------------DiskSIO: Branch[ESIO];*-----------------------------------------------------------* Idle loop for Diablo emulation: awakened once per sector.* Stores status and checks for newly-issued commands.* If KStatus # 0, it is the ending status for a command that has just been* finished; if KStatus = 0, the disk was idle during the last sector.*-----------------------------------------------------------AltoLoop: T_ clearSeekTagTW, Call[DoMuffOutput]; * clear any spurious SeekTagTW Call[UpdateSector]; T_ (K400)+(124C); * VM 524 = sector interrupt mask KTemp3_ (Fetch_ T)-(3C); RBase_ RBase[NWW]; NWW_ (NWW) OR MD, RBase_ RBase[DiskRegs];* The NWW>0 test is not logically necessary, but is desirable because most* existing disk software does NOT use interrupts. T_ KStatus, Branch[.+2, ALU<=0]; * Disk active last sector? T_ KStatus, Reschedule; * NWW>0 now KTemp3_ (Fetch_ KTemp3)+1, * VM 521 = KCB pointer Branch[AWasntIdle, ALU#0]; * Branch if already have status* Controller was idle last sector. Select drive, read status anew, and deselect* drive (so as to leave drive deselected most of the time). Trident spec is* that selecting a drive takes effect within 200 ns, so it's unnecessary for* the microcode to do any special timing or synchronization. T_ KSelect, Call[SendDriveTag]; * Turn on select for current drive Call[AMapHdwStatus]; * Read status and map to Diablo format PD_ MD, Sector, * MD#0 if about to start a new command Branch[SectorUnsync, R<0]; * Don't deselect if sector unsynchronized T_ (KSelect) AND (Not[tagSelectDrive!]C), Branch[DontDeselect, ALU#0]; * Deselect drive if no command pending Call[SendDriveTag]; * Turn off select Nop; * Placement* The sector number we insert is the raw hardware sector number, not the* emulated Diablo sector number. I doubt anyone cares.DontDeselect: T_ LSH[Sector, 14];SectorUnsync: T_ KStatus_ (KStatus) OR T; * Insert sector number into software statusAWasntIdle: Store_ KTemp3, DBuf_ T, T_ MD; * VM 522 _ current status KPtr_ PD_ T; * Test for nonzero KCB pointer T_ (KPtr)+(2C), DblBranch[DoACmmd, EndAltoLoop, ALU#0];* EndAltoLoop gets defined once in DiskSubrs.mc, and redefined in TriDisk.mc* if the latter is loaded.* EndAltoLoop:* KStatus_ A0, Block, Branch[AltoLoop];*-----------------------------------------------------------* Have a new Diablo command to execute. KPtr = KCB pointer, T = KPtr+2.*-----------------------------------------------------------DoACmmd: T_ (Fetch_ T)+(7C); * Fetch disk command (KCB+2) KCmmd_ MD, Fetch_ T; * Fetch disk address (KCB+11) KCmmd_ (KCmmd) - (ACmmdSeal); * just subtract the seal! branch[ACmmdBadSeal, ALU<0], KAddr_ MD; * used to check if seal equal PD_ (KAddr)+(LShift[Sub[20, nSectorsDiablo!], 14]C); Branch[ABadSector, Carry], * branch if invalid sector T_ (K400)+(123C); * VM 523 KStatus_ A0, Fetch_ T; * Fetch diskAddr previous command Store_ T, DBuf_ KAddr; * Store diskAddr this command T_ DPF[KCmmd, 1, 1]; * Extract command bit 15 into bit 14 KAddr_ (KAddr) XOR T, * A[14] XOR C[15] defines disk number Branch[ARestore, R odd]; T_ (KAddr) XOR MD; * Compare this diskAddr with previous T_ T AND (177772C); * Just cylinder and disk numbers PD_ T AND (7777C); * (= ((KAddr) XOR MD) AND (7772C)) PD_ (KCmmd) AND (3400C), Branch[NoASeek, ALU=0];*-----------------------------------------------------------* Must do a seek. KCmmd and KAddr are set up.* Dorado cylinder = 406*(Diablo drive) + (Diablo cylinder)*-----------------------------------------------------------ASeek: T_ (K400)+(226C); * 626B = 406 = number of cylinders KTemp0_ T; T_ LDF[KAddr, 11, 3]; * Cylinder for command PD_ (KTemp0)-T; * Test for legal cylinder PD_ (KAddr) AND (2C), Branch[ABadCylinder, Carry']; * Test drive bit T_ T+(offsetCylinderDiablo), Branch[.+2, ALU=0]; T_ (KTemp0)+T; * Drive=1, add 406 to cylinder Call[SeekAndWaitForReady];*-----------------------------------------------------------* Now compute the head number.* Dorado head = (partition number)-1* Partition number is in KCmmd[0:7] if nonzero, else DefaultPartition.*----------------------------------------------------------- PD_ (KCmmd) AND (177400C);NoASeek: T_ DefaultPartition, Branch[.+2, ALU=0]; * Skip if command partition = 0 T_ LDF[KCmmd, 10, 10]; * Get partition from command T_ T OR (tagHead); T_ T-1, Call[SendTag]; * Send the head tag command:If[staggerSectors]; ********** Stagger sectors on adjacent cylinders* If cylinder is odd then flip the Diablo head bit. This makes a given sector* on adjacent cylinders be on opposite sides of the disk. This means that* during sequential reads, a seek to the next cylinder will lose* only half a revolution rather than a whole revolution. T_ (KAddr) AND (10C); * Low bit of cylinder T_ RSH[T, 1]; * Shift to head position KAddr_ (KAddr) XOR T;:EndIf; *************************************************-----------------------------------------------------------* Now compute the sector number.* Let s = nSectorsDiablo*(Diablo head) + (Diablo sector)*----------------------------------------------------------- PD_ (KCmmd) AND (2C); * Seek-only command? PD_ (KAddr) AND (4C), Branch[ACmmdSeekOnly, ALU#0]; KAddr_ LDF[KAddr, 4, 14], Branch[.+2, ALU=0]; * Extract sector KAddr_ (KAddr)+(nSectorsDiablo); * Add sectors for head 1:If[interleaveSectors]; ********* Interleave sectors 3:1 ***********-----------------------------------------------------------* The sector number s is mapped into a hardware sector number [0..29]* according to the following table:** s = -- 19 0 10 20 1 11 21 2 12 22 3 13 23 4* h = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14** s = 14 24 5 15 25 6 16 26 7 17 27 8 18 -- 9* h = 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29** Thus h _ if s = 19 then 1 else 3*(s mod 10) + s/10 + 2* Note that h = 28 is unusable, and h = 29 is the sector after Index.*----------------------------------------------------------- T_ 1C; * Compute s/10 and s mod 10 KAddr_ (KAddr)-(12C); * Go around this loop at most 3 times T_ T+1, Branch[.-1, ALU>=0]; KAddr_ (KAddr)+(12C); * Now KAddr = s mod 10, T = s/10 + 2 T_ ((KAddr)+T) RCY 1; * KAddr _ 3*KAddr + T KAddr_ ((KAddr)+T) LCY 1; PD_ (KAddr)-(36C); * If result is 30 then change to 1 KCmmd_ (KCmmd) AND (374C), Branch[.+2, ALU<0]; KAddr_ 1C;:Else; ********** Not interleaving sectors ********** KCmmd_ (KCmmd) AND (374C);:EndIf; ***********************************************-----------------------------------------------------------* Convert the Alto disk command into a Dorado disk command.* Alto format: seal[0:7], headerCmmd[8:9], labelCmmd[10:11], dataCmmd[12:13],* seekOnly[14], exchangeDisks[15];* Commands: 0 = read, 1 = check, 2 or 3 = write* Dorado format: headerCmmd[8:9], labelCmmd[10:11], dataCmmd[12:13];* Commands: 0 = none, 1 = write, 2 = check, 3 = read*----------------------------------------------------------- T_ (KCmmd) AND (250C); * Turn any 3's into 2's T_ (NOT T) RSH 1; KCmmd_ (KCmmd) AND T; KCmmd_ (KCmmd) XOR (374C); * Complement to make Dorado commandsAWaitSector: SCall[WaitForSector]; * Returns with TIOA[DiskControl] Branch[ABadSector]; * +1 return: failed to find the sector*-----------------------------------------------------------* Issue the command to the controller.* Then check to see whether we issued it in time, and if not revoke it* and wait for this sector to come around again.*----------------------------------------------------------- Output_ KCmmd, Call[UpdateSector]; * Returns with T = Sector PD_ (KAddr) XOR T; * Are we still at the same sector? T_ A0, TIOA[DiskControl], Branch[ACmmdInTime, ALU=0]; Output_ T; * Not in time. This clears Active Output_ T, Branch[AWaitSector]; * This reloads command register*-----------------------------------------------------------* Now do the data transfers.* Each call to DoAltoCmmd executes the command in KCmmd[14:15]* and left-cycles KCmmd 2 bits.*-----------------------------------------------------------ACmmdInTime: Block, Call[UpdateSector]; * Block til start of sector T_ (KPtr)+(3C); KCmmd_ RCY[KCmmd, KCmmd, 6]; * Header command to [14:15] DskMAddr_ 1C; * Header length -1 KTemp3_ (Fetch_ T)+1, Call[DoAltoCmmd]; * Header pointer (KCB+3) DskMAddr_ 7C; * Label length -1 KTemp3_ (Fetch_ KTemp3)+1, Call[DoAltoCmmd]; * Label pointer (KCB+4) DskMAddr_ 377C; * Data length -1 Fetch_ KTemp3, Call[DoAltoCmmd]; * Data pointer (KCB+5) Nop;*-----------------------------------------------------------* Command has completed. Store ending status and initiate interrupts* as appropriate. KPtr still points to KCB.* KStatus = 0 if all has gone well so far; # 0 if a check error or ECC error* has been detected by the microcode. Have not yet looked at hardware status.* KTemp0 = muffReadError or muffWriteError, depending on the operation* performed on the last block.*-----------------------------------------------------------ACmmdEnd: KPtr_ (Fetch_ KPtr)+1, Call[Read1Muff]; * Fetch successor KCB KTemp3_ MD, PD_ (KStatus) OR T; * Software or hardware errors? T_ (KPtr)+(5C), Branch[ACmmdEnd2, ALU=0]; * KCB+6: no-error interrupts* Error occurred.* Reset command register in case we have gotten out of sync with controller.* Then disable further KCB processing and read complete status. KTemp3_ T_ A0, TIOA[DiskControl]; KTemp0_ T-T-1, Output_ T, Call[OutputGetsT]; T_ (K400)+(123C); * Forget disk address (set to -1) Store_ T, DBuf_ KTemp0, Call[AMapHdwStatus]; T_ clearErrors, Call[DoMuffOutput]; T_ (KPtr)+(6C); * KCB+7: error interruptsACmmdEnd2: Fetch_ T, T_ clearSeekTagTW, * Fetch interrupt word Call[DoMuffOutput]; * Clear TW caused by TagDone T_ LSH[Sector, 14]; * Merge sector number into status KStatus_ (KStatus) OR T, RBase_ RBase[NWW]; T_ (R400)+(121C); NWW_ (NWW) OR MD, RBase_ RBase[DiskRegs]; Store_ T, DBuf_ KTemp3, * update VM 521 with successor pointer Branch[.+2, ALU<=0]; * Branch if no interrupt pending ReSchedule; T_ KStatus_ (KStatus) OR (7400C); * KStatus[4:7]_17B Store_ KPtr, DBuf_ T, Branch[AltoLoop]; * Store status in KCB+1ACmmdSeekOnly: Branch[ACmmdAbort];*-----------------------------------------------------------* Restore command*-----------------------------------------------------------ARestore: T_ tagControl; T_ T OR (Or[tagDiskReset!, tagReZero!]C), Call[SendTag]; T_ clearAllTWs, Call[DoMuffOutput]; T_ K400, TIOA[DiskControl], Call[OutputGetsT]; * BlockTilIndex Sector_ T-T-1, Block, Branch[ASeek]; * Now do a new seek always*-----------------------------------------------------------DoAltoCmmd: * Do Alto command for one block* Enter: KCmmd[14:15] = command for this block (Dorado format)* MD = pointer to start of block* DskMAddr = (length of block)-1* DiskBR.BRHi = 0* Exit: KCmmd left-cycled 2 bits* MemBase = MDS* KTemp0 = muffler address for reading ending status* Clobbers T, KTemp0, KTemp1, KTemp2, DskMAddr, DiskBR.BRLo* Note: if a check error occurs or improper TW occurs, does not return* but rather aborts command and goes directly to ACmmdDone.*-----------------------------------------------------------Subroutine; KTemp2_ Link;TopLevel; T_ MD, MemBase_ DiskBR; BDispatch_ KCmmd; * Dispatch on KCmmd[14:15]ACmmdTable: DispTable[4, 7, 4], BRLo_ T, Branch[ACmmdTable]; * 0 can't happen T_ 201C, Branch[ACmmdWrite]; * 1 write; T_ sync pattern to write KTemp0_ muffRdFifoTW, Branch[ACmmdCheck]; * 2 check KTemp0_ muffRdFifoTW, Branch[ACmmdRead]; * 3 read*-----------------------------------------------------------* Write command.* Controller gives a WriteFifoTW when there is room for at least 4 words* in the Fifo. Doing an Output that reduces the free space below 4 causes* WriteFifoTW to be dropped at T0 of the 4th cycle after the Output,* so a Block on the 5th cycle will take effect.* I think it was originally intended that a 3-instruction, 2-word loop* be possible: Output; Output; Block;* Unfortunately, this doesn't work if the second Output causes the wakeup* to be dropped, because it is dropped so late that we will go around* the loop twice more, outputting 4 words when there is room for only 3.* Thus the minimal loop is: {Output; Output; Nop}; Block;* where the instructions inside { } may be permuted in any way.* An equivalent loop is: Output; Block;* and it takes less microcode.* Due to control section bugs, we must not Block on a memory reference* if the task wakeup might be dropped at T0 of that instruction.*-----------------------------------------------------------ACmmdWrite: TIOA[DiskData]; DskMAddr_ (Fetch_ DskMAddr)-1, Output_ T; * Output sync pattern DskMAddr_ (Fetch_ DskMAddr)-1, Output_ MD; Block, Branch[.-1, ALU>=0]; Output_ MD; * Output last word* Changing TIOA from DiskData to DiskControl disables WriteFifoTW.* The wakeup is removed at T0 of the third instruction after the one that* changes TIOA, so the earliest we can block is the fourth instruction.* Hardware generates one more WriteFifoTW when it is done with this block. TIOA[DiskControl]; KTemp0_ muffWriteError; * Select appropriate status bit KCmmd_ LCY[KCmmd, KCmmd, 2]; * Shift command for next block Link_ KTemp2;Subroutine; MemBase_ MDS, Block, Return; * Wait until write really finishedTopLevel;* DoAltoCmmd (cont'd)*-----------------------------------------------------------* Read command.* Controller gives a ReadFifoTW when there are at least 3 words in the Fifo* (actually, 2 in the Fifo and 1 in OutReg). Doing an Input that reduces* the count below 3 causes ReadFifoTW to be dropped at T0 of the 4th cycle* after the Input, so a Block on the 5th cycle will take effect.* Thus the minimal loop is: Input; Block;* Due to control section bugs, we must not Block on a memory reference* if the task wakeup might be dropped at T0 of that instruction.*-----------------------------------------------------------ACmmdRead: DskMAddr_ (DskMAddr)+1, Block, Call[Read1Muff]; KTemp0, TIOA[DiskData], Branch[AReadBadTW, R even]; PD_ DskMAddr, T_ Input, Branch[.+2]; * Can't do back-to-back Inputs PD_ Store_ DskMAddr, DBuf_ T, T_ Input; DskMAddr_ (DskMAddr)-1, Block, Branch[.-1, ALU#0];* A read block ends with 2 garbage words and 2 ECC words. When we fall out* of the main loop, we have already read the first garbage word. Call[ReadECC]; * Returns with ECC test hanging FreezeBC, Branch[ReadCheckEnd]; * Remainder same as check case*-----------------------------------------------------------* Check command.* Controller gives a ReadFifoTW when there is at least 1 word in the Fifo* (actually, OutReg full regardless of Fifo). Doing an Input that empties* the Fifo causes ReadFifoTW to be dropped at T0 of the 2nd cycle* after the Input, so a Block on the 3rd cycle will take effect.* Thus the minimal loop is: Input; Nop; Nop; Block;* Due to control section bugs, we must not Block on a memory reference* if the task wakeup might be dropped at T0 of that instruction.*-----------------------------------------------------------ACmmdCheck: DskMAddr_ (Fetch_ DskMAddr)-1, Block, Call[Read1Muff]; KTemp0_ clearCompareErr, Branch[ACheckBadTW, R even]; PD_ (DskMAddr)+1, TIOA[DiskData];* Main check loop -- 4 cycles per word.* At the top of the loop, MD = the word fetched from memory during* the previous cycle, and ALU=0 if that was the last word of the block.ACheckLoop: PD_ MD, T_ MD, KTemp1_ Input, Branch[ACheckLast, ALU=0]; DskMAddr_ (Fetch_ DskMAddr)-1, Branch[ANoCheckWord, ALU=0];* Memory word is nonzero: check it against disk word. PD_ (KTemp1) XOR T; PD_ (DskMAddr)+1, Block, Branch[ACheckLoop, ALU=0]; KTemp0_ A0, Branch[.-1]; * Not equal, cancel clearCompareErr* Memory word is zero: store disk word on top of it.* Note: the combination of memory reference and Block in the second* instruction is OK, because if the wakeup is going to drop,* it will drop by T0 of the first instruction.ANoCheckWord: T_ (DskMAddr)+(2C); * We are behind count by 2 now PD_ (Store_ T)-1, DBuf_ KTemp1, Block, Branch[ACheckLoop];* Fell out of loop.* T = ALU = last word from memory, and KTemp0 = last data word from disk.ACheckLast: PD_ (KTemp1) XOR T, Branch[.+2, ALU#0]; PD_ Store_ T, DBuf_ KTemp1; * T = 0 TIOA[DiskMuff], Branch[.+2, ALU=0]; KTemp0_ A0; * Not equal, cancel clearCompareErr* DoAltoCmmd (cont'd)* If KTemp0 = clearCompareErr, check finished with no errors - it's OK* to write the next block. If KTemp0 = 0, a check error occurred.* Hardware turns on CompareErr flipflop at the beginning of a checked block,* which will inhibit writing of subsequent blocks if the microcode determines* that there is a check error in this block or fails to clear it in time. PD_ Output_ KTemp0; * Clear CompareErr iff no errors T_ A0, TIOA[DiskData], Block, Branch[.+2, ALU=0]; T_ KCmmd; KCmmd_ T, Call[CheckECC]; * returns with ECC test hanging* At this point, ALU#0 if an ECC error occurred, and KCmmd=0 if* a check error occurred. This is the tail of both reading and checking.ReadCheckEnd: KTemp0_ muffReadError, Branch[.+2, ALU=0];AChecksumError: KStatus_ (KStatus) OR (4C); * Alto ChecksumError KCmmd_ LCY[KCmmd, KCmmd, 2]; * Shift command for next block Link_ KTemp2, Branch[.+2, ALU=0];Subroutine; MemBase_ MDS, Return;TopLevel;* KCmmd=0 means a check error occurred. Post CheckError completion code* and abandon commands for remaining blocks. KStatus_ (KStatus) OR (2C); * Alto CheckErrorACmmdAbort: MemBase_ MDS; KTemp0_ muffReadError; Branch[ACmmdEnd];* If a non-Fifo TW occurs at the beginning of reading or checking, most* likely the data was so bad that the controller was unable to lock onto it* before reaching the end of the sector. Report this as a ChecksumError.AReadBadTW: KCmmd_ A0, Branch[AChecksumError];ACheckBadTW: KCmmd_ A0, Branch[AChecksumError];* Command errors post bad status and abort the chain.ABadCylinder: KStatus_ 201C, Branch[.+2]; * Illegal cylinder, report SeekFailABadSector: Kstatus_ 3C; * Illegal sector Branch[ACmmdAbort]; * Duplicated for placement* Incorrect seal abandons the command chain entirely and posts error status* only in VM 522.ACmmdBadSeal: T_ (7S)-(K400); * 177407 -- done + illegal command KStatus_ T;* Zero command chain and set disk address to -2 (any illegal addr will do)AForgetCmmd: T_ (K400)+(123C); T_ (Store_ T)+(DBuf_ -2C); * VM 523 _ -2 Store_ T, DBuf_ 0C, Branch[AltoLoop]; * VM 521 _ 0*-----------------------------------------------------------AMapHdwStatus: * Map hardware status to Diablo format* Enter: KStatus = 0, except perhaps for software-detected errors* (ChecksumError, CheckError)* Exit: KStatus updated to reflect hardware status, including the done bits* but NOT including the sector number.* T = 0* Clobbers T, KTemp0, KTemp1, KTemp2*-----------------------------------------------------------Subroutine; KTemp2_ Link;TopLevel; T_ muffsStatus, Call[Read20Muffs]; PD_ (KTemp0) AND (16000C); * NotSelected, NotOnLine, NotReady? T_ (K400) OR (200C), Branch[.+2, ALU=0]; KStatus_ (KStatus) OR (40C); * Report as NotReady PD_ (KTemp0) AND T; * FifoUnderflow, FifoOverflow? T_ 200C, Branch[.+2, ALU=0]; KStatus_ (KStatus) OR (20C); * Report as DataLate PD_ (KTemp0) AND (5C), Branch[.+2, R>=0]; * SeekIncomplete? KStatus_ (KStatus) OR T, FreezeBC; * Report as SeekFail* Don't know whether to examine WriteError or ReadError.* However, WriteError includes all ReadErrors, and additionally includes* ReadOnly, CylOffset, and FifoParityError. The first two cause DeviceCheck* (included in ReadError) to be set if they occur during writing, and* the third cannot occur during reading. Therefore it suffices to examine* FifoParityError and ReadError to decide whether any hardware error occurred. PD_ (KStatus) AND (3C), Branch[.+2, ALU#0]; T_ A0, Link_ KTemp2, Branch[.+3]; * No error T_ A0, Link_ KTemp2, Branch[.+2, ALU#0]; * CompletionCode already posted? KStatus_ (KStatus) OR (1C); * Post HardwareErrorSubroutine; KStatus_ (KStatus) OR (7400C), Return; * Insert done bits*-----------------------------------------------------------InitRamDiablo: * Init format Ram for Diablo emulation.* Also sets subsector count for drive 0 and issues a BlockTilIndex.* Enter:* Exit: TIOA[DiskMuff]* Clobbers T, KTemp0, KTemp1, KTemp2*-----------------------------------------------------------Subroutine; KTemp2_ Link;TopLevel; MaxSectors_ 36C; * 30 sectors around (actually, 29 + * a fraction, though we use only 28) KTemp0_ 3C; * 4 subsectors per sector KSelect_ (KSelect) OR (4000C); * Sectors do not evenly divide the disk T_ KTemp1_ A0, Call[SetDriveAndSubSector]; * Drive 0 T_ (KTemp1)+1, TIOA[DiskRam], * T_ 1 Call[OutputGetsT]; * [0] header count - 1 = 1 T_ 7C, Call[OutputGetsT]; * [1] label count - 1 = 7 T_ 377C; T_ A0, Output_ T, * [2] data count - 1 = 377 Call[OutputGetsT]; * [3] count for unused block = 0 T_ 104C, Call[OutputGetsT]; * [4] control tag for read T_ 204C, Call[OutputGetsT]; * [5] control tag for write T_ 4C; T_ A0, Output_ T, * [6] control tag for head select Call[OutputGetsT]; * [7] control tag to zero the tag bus T_ 33C, Call[OutputGetsT]; * [10] write delay first block T_ 6C, Call[OutputGetsT]; * [11] write delay succeeding blocks T_ 11C, Call[OutputGetsT]; * [12] read delay first block T_ 2C, Call[OutputGetsT]; * [13] read delay succeeding blocks T_ T-1, Output_ T; * [14] head select delay = 2 T_ A0, Output_ T; * [15] no. of ECC words - 1 = 1 Output_ T, * [16] the constant 0 Call[OutputGetsT]; * [17] unused word T_ clearAll; * Clear all TWs and errors Link_ KTemp2, Branch[DoMuffOutput]; * Do it and return