*----------------------------------------------------------- Title[TriDisk.mc...April 13, 1982 3:31 PM...Taft]; * Emulates an Alto Trident disk on a Dorado's Trident disk(s). * This code is intended to coexist with AltoDiabloDisk.mc -- * on a Dorado with multiple drives, drive 0 is used to emulate * an Alto Diablo disk and the other drives emulate Alto Trident disks. *----------------------------------------------------------- *----------------------------------------------------------- % Data structures *----------------------------------------------------------- KBLK format: VM 640: pointer to disk command block (KCB) VM 641: number of currently-selected drive VM 642: cylinder number of most recently started disk command VM 643: status at last sector pulse KCB format: KCB+0: cylinder number KCB+1: head ,, sector numbers KCB+2: drive number KCB+3: pointer to next KCB KCB+4: command seal (overwritten by initial status) KCB+5: header command KCB+6: header word count KCB+7: pointer to header block KCB+10: header ECC0 KCB+11: header ECC1 KCB+12: header ending status KCB+13 to 20: label command block (similar to KCB+5 to 12) KCB+21 to 27: data command block (similar to KCB+5 to 12) KCB+30: unused KCB+31: completion interrupt bit mask *** not implemented Disk command format: 0-3: unused 4: check data 5: unused 6: strobe late *** no way to access this at present 7: strobe early *** no way to access this at present 8: write 9: read 10: unused 11: reset head address 12: device check reset 13: head select 14: re-zero 15: advance head address Disk status format: 0: seek incomplete 1: illegal head number 2: device check 3: not selected 4: not on-line 5: not ready 6: sector overflow 7: write data late 8: read data late 9: compare error 10: read-only 11: cylinder offset 12: ECC error -- reported only in KCB 12-15: sector number -- reported only in KBLK Note: at present this microcode may be used to emulate either the Alto TFS 9-sector (1024-word data block) format or the Alto Juniper 16-sector (512-word data block) format. HOWEVER, due to deficiencies in the Dorado sector size selection mechanism, a drive to be used for 9-sector emulation must be jumpered for 117 sectors (Dorado standard), whereas a drive to be used for 16-sector emulation must be jumpered for 16 sectors. % *----------------------------------------------------------- * SIO instruction * Diablo disk does not use SIO, but Trident disk does. * DiskSIO is first defined in ADiabloDisk.mc, and is redefined here. * SIO[40] enables Trident emulation; SIO[20] disables it. * Stack = SIO argument. * Current state of Trident emulation is reflected in TridentFlag: * B0: 1 = Trident emulation enabled, 0 = disabled * B1: 0 = force drive selection at next Trident command * B12-15: current subsector count *----------------------------------------------------------- Set[XTask, IP[EMU]]; TopLevel; DontKnowRBase; DiskSIO: PD_ (Stack) AND (60C); PD_ (Stack) AND (40C), Branch[ESIO, ALU=0]; T_ A0, RBase_ RBase[DiskRegs], Branch[.+2, ALU=0]; T_ (TridentFlag) OR (100000C); * Enable Trident emulation * Give the disk task a wakeup, in case the currently-selected drive * is not on-line and hence not giving any sector wakeups. * ASSUMPTION: code doing a disk SIO will NOT be trying to read/write * the disk (Diablo or Trident) at the same time, and hence the microcode * will not be confused by the extra wakeup. TridentFlag_ T, TaskingOff; Wakeup[DSK]; TaskingOn, Branch[ESIO]; *----------------------------------------------------------- * End of idle loop for Diablo emulation. * If Trident emulation is enabled, check for newly-issued Trident commands, * and switch to Trident emulation if any are found. * EndAltoLoop is first defined in DiskSubrs.mc, and is redefined here. *----------------------------------------------------------- Set[XTask, IP[DSK]]; KnowRBase[DiskRegs]; EndAltoLoop: T_ (K400)+(240C); * VM 640 = KCB pointer T_ (Fetch_ T)+1, TridentFlag, Branch[.+2, R<0]; KStatus_ A0, Block, Branch[AltoLoop]; * Trident emulation disabled * Trident emulation is enabled. A Trident command is pending if either * VM 640 (KCB pointer) is nonzero or VM 641 (drive number) is negative. Fetch_ T, KPtr_ PD_ MD; * VM 641 = drive number KTemp0_ MD, Branch[StartTrident, ALU#0]; KTemp0, Branch[StartTrident1, R<0]; KStatus_ A0, Block, Branch[AltoLoop]; * No Trident command * Switch to Trident emulation mode. StartTrident: Nop; StartTrident1: TridentFlag_ (TridentFlag) AND (Not[40000]C); * Force drive select T_ T-T-1, MemBase_ DiskBR, * BRHi_ MDSHi-1 for Trident transfers Call[SetDiskBRHi]; MemBase_ IOBR, Branch[TriIdleLoop]; *----------------------------------------------------------- * Idle loop for Trident emulation: awakened once per sector. * Stores status and checks for newly-issued commands. *----------------------------------------------------------- TriIdleLoop: T_ clearSeekTagTW, Call[DoMuffOutput]; * clear any spurious SeekTagTW Call[UpdateSector]; T_ KSelect, Call[SendDriveTag]; * Turn on select for current drive T_ (K400)+(240C); * VM 640 = KCB pointer T_ (Fetch_ T)+1, TridentFlag, * Switch to Diablo if Trident disabled Branch[SwitchToDiablo, R>=0]; Fetch_ T, KPtr_ PD_ MD; * VM 641 = drive number KAddr_ MD, Branch[TriNewCmmd, ALU#0]; * Branch if have command KAddr_ (KAddr) AND (77777C), * Isolate drive number Branch[TriDriveSelect, R<0]; * Branch if new drive select * No Trident commands. Store Trident status and check for Diablo command. T_ muffsStatus; Call[Read20Muffs]; * Read drive status KTemp1_ T AND (Not[17]C); * Report drive status in [0:11] T_ LDF[Sector, 4, 0]; * Report sector number in [12:15] KTemp1_ (KTemp1) OR T, FreezeBC; * ALU branch cond _ unmasked Sector T_ (KSelect) AND (Not[tagSelectDrive!]C), Branch[.+2, ALU<0]; * Don't deselect if sector unsynchronized Call[SendDriveTag]; * Turn off drive select T_ (K400)+(121C); * VM 521 = Diablo KCB pointer T_ (Fetch_ T)+(Sub[643, 521]C); PD_ MD; Store_ T, DBuf_ KTemp1, Branch[.+2, ALU#0]; * Store status in VM 643 Block, Branch[TriIdleLoop]; *----------------------------------------------------------- * Trident is idle and there is a new Diablo command. Switch to Diablo mode. *----------------------------------------------------------- Nop; SwitchToDiablo: T_ A0, MemBase_ DiskBR; * BRHi_ MDSHi for Diablo transfers Call[SetDiskBRHi]; Call[InitRamDiablo]; * Reset format Ram, select drive 0 KStatus_ A0, MemBase_ IOBR, Block, * This will block until Index Branch[AltoLoop]; * Begin executing Diablo commands *----------------------------------------------------------- * Select new Trident drive, forced by software storing 100000B + drive into * VM 641, without issuing a command. * Also get here when a Trident command selects a new drive. * T = 641, KAddr = new drive number, KPtr = 0 or KCB pointer. *----------------------------------------------------------- TriDriveSelect: Store_ T, DBuf_ KAddr, Call[InitRamTrident]; T_ TridentFlag_ (TridentFlag) OR (40000C); * Reset force flag KTemp0_ T AND (77C); * Extract subsector count T_ KAddr, Call[SetDriveAndSubSector]; * Select drive and subsector count Block, Branch[TriIdleLoop]; * Block til index, then look again *----------------------------------------------------------- * Have the first Trident command of a new chain. * Set up the format Ram according to this command, and force a drive select * if the sector format has changed. *----------------------------------------------------------- TriNewCmmd: Nop; KAddr_ T-T-1, Call[InitRamTrident]; * Pretend current drive is -1 PD_ (TridentFlag)+(TridentFlag); * Test TridentFlag[1] T_ (KPtr)+(4C), Branch[DoTCmmd1, ALU>=0]; * Branch if format changed *----------------------------------------------------------- * Have a Trident command to execute. KPtr = KCB pointer. * Check for correct seal and new drive select. *----------------------------------------------------------- DoTCmmd: T_ (K400)+(241C); * VM 641 = current drive number Fetch_ T, T_ KPtr; KAddr_ MD, T_ T+(4C); * KCB+4 = command seal DoTCmmd1: T_ (Fetch_ T)-(2C); * KCB+2 = drive number Fetch_ T, KCmmd_ 122400C, T_ MD; * Correct seal = 122645B KCmmd_ (KCmmd) OR (245C); KCmmd_ (KCmmd) XOR T; PD_ (KAddr) XOR MD, KAddr_ MD, * Compare new drive with current Branch[TCmmdBadSeal, ALU#0]; * Branch if invalid seal T_ (KPtr)+(5C), Branch[.+2, ALU=0]; T_ (K400)+(241C), Branch[TriDriveSelect]; * New drive, go select it *----------------------------------------------------------- * Run down the command list in the KCB and construct the single-word * command wanted by the controller. T = KPtr+5, KCmmd = 0. * This code depens on the KCB containing at most 3 command blocks. *----------------------------------------------------------- TConstCmmd: T_ (Fetch_ T)+(6C); * Fetch command for this block KCmmd_ LSH[KCmmd, 2]; KTemp0_ PD_ MD; PD_ (KTemp0) AND (12C), Branch[TConstCmmdEnd, ALU=0]; PD_ (KTemp0) AND (200C), Branch[TResetRestore, ALU#0]; PD_ (KTemp0) AND (4000C), Branch[.+2, ALU=0]; KCmmd_ (KCmmd) OR (commandWrite), Branch[TConstCmmd]; Branch[.+2, ALU=0]; KCmmd_ (KCmmd) OR (commandCompare), Branch[TConstCmmd]; KCmmd_ (KCmmd) OR (commandRead), Branch[TConstCmmd]; * End of commands. Left-shift KCmmd so that the first command is non-null. * (Careful: a do-nothing command will leave KCmmd entirely zero.) * Compare requested cylinder with current one. * These two operations are interwoven to save space and time. TConstCmmdEnd: T_ (K400)+(242C); * VM 642 = current cylinder KTemp1_ Fetch_ T; KTemp3_ (Fetch_ KPtr)+1, T_ MD; * KPTR+0 = new cylinder; ALU#0 PD_ (KCmmd) AND (300C), Branch[.+3, ALU=0]; * Branch if do-nothing PD_ T XOR MD, Branch[.+3, ALU#0]; KCmmd_ LSH[KCmmd, 2], Branch[.-2]; * Shift command into position PD_ T XOR MD; Fetch_ KTemp3, T_ MD, Branch[NoTSeek, ALU=0]; * KBLK+1 = head,,sector *----------------------------------------------------------- * Must do a seek. T = new cylinder number, KTemp1 = 642B. *----------------------------------------------------------- Store_ KTemp1, DBuf_ T, * Update current cylinder in VM 642 Call[SeekAndWaitForReady]; *----------------------------------------------------------- * Now select the head and wait for the right sector. * MD = head ,, sector; KTemp3 = KPtr+1. *----------------------------------------------------------- NoTSeek: T_ MD, KStatus_ A0; T_ RSH[T, 10], KAddr_ MD; T_ T OR (tagHead), Call[SendTag]; * Send the head tag command T_ (KAddr) AND (37777C); * Remove offset head positioning PD_ KCmmd; * do-nothing command? KAddr_ (KAddr) AND (377C), Branch[TCmmdSeekOnly, ALU=0]; * Store head,,sector (with offset head positioning stripped off) * back into KPtr+1, so that header checking will work properly * when offset head positioning is being done. Store_ KTemp3, DBuf_ T; TWaitSector: SCall[WaitForSector]; * Returns with TIOA[DiskControl] Branch[TBadSector]; * +1 return: failed to find 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[TCmmdInTime, ALU=0]; Output_ T; * Not in time. This clears Active Output_ T, Branch[TWaitSector]; * This reloads command register *----------------------------------------------------------- * Now do the data transfers. *----------------------------------------------------------- TCmmdInTime: KStatus_ A0, Block, Call[UpdateSector]; * Block til start of sector KCmmd_ RCY[KCmmd, KCmmd, 6]; * Header command to [14:15] TCmmdSeekOnly: T_ (KPtr)+(4C); * KCB+4 = seal = first block -1 TResetFinish: KTemp3_ T; KTemp0_ muffReadError; * Here KTemp3 = pointer to status word for previous block; * KStatus = software status conditions for previous block (just ECC error); * KTemp0 = muffReadError or muffWriteError as appropriate. * Each iteration of TCmmdLoop executes the command in KCmmd[14:15] * and left-cycles KCmmd 2 bits. TCmmdLoop: Call[Read1Muff]; * Check for hardware errors T_ muffsStatus, KTemp0, Branch[TCmmdNoErr, R even]; * There was a hardware error in the previous block. Read entire status word. * Bits 0-11 of the hardware status are identical to the emulated Alto Trident * status. Bits 12 and 13 are IOBParityErr and FifoParityErr, which aren't * defined on the Alto; we map these to ReadDataErr so that the software will * at least notice that there was an error. (These will subsequently cause the * command chain to be aborted.) Bits 14 and 15 are ReadError and WriteError, * which are irrelevant for our purposes here. Call[Read20Muffs]; PD_ (KTemp0) AND (14C); * IOB or Fifo PE? T_ (KTemp0) AND (Not[17]C), Branch[.+2, ALU=0]; T_ T OR (100C); * Yes, also set ReadDataErr bit KStatus_ (KStatus) OR T; * Merge with software status bits TCmmdNoErr: T_ (KStatus)+1; * Set done bit KTemp3_ (Store_ KTemp3)+1, DBuf_ T; * Store status word * Now dispatch on the next command. Note: KTemp0 contains hardware status * that must be preserved for examination at TCmmdEnd. KTemp3_ (KTemp3)+1; * Skip over command word KTemp3_ (Fetch_ KTemp3)+1; * Fetch word count KTemp3_ (Fetch_ KTemp3)+1, T_ MD; * Fetch memory address DskMAddr_ (0S)-T, T_ MD; * DskMAddr_ -count T_ T-(DskMAddr), MemBase_ DiskBR; * T_ memory address + count BDispatch_ KCmmd; * Dispatch on KCmmd[14:15] KStatus_ A0, BRLo_ T; * Set BR for negative indexing TCmmdTable: DispTable[4, 7, 4], Branch[TCmmdEnd]; * 0 no more commands T_ 1C, Branch[TCmmdWrite]; * 1 write; T_ sync pattern to write KTemp0_ muffRdFifoTW, Branch[TCmmdCheck]; * 2 check KTemp0_ muffRdFifoTW, Branch[TCmmdRead]; * 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. *----------------------------------------------------------- TCmmdWrite: 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 KTemp3_ (KTemp3)+1, MemBase_ IOBR; * Skip over ECC words KTemp3_ (KTemp3)+1, Block, Branch[TCmmdLoop]; * Wait til write done *----------------------------------------------------------- * 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. *----------------------------------------------------------- TCmmdRead: DskMAddr_ (DskMAddr)-1, Block, Call[Read1Muff]; KTemp0, TIOA[DiskData], Branch[TReadBadTW, R even]; PD_ DskMAddr, T_ Input, Branch[.+2]; * Can't do back-to-back Inputs PD_ (Store_ DskMAddr)+1, 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 Branch[TReadCheckEnd]; * 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. *----------------------------------------------------------- TCmmdCheck: DskMAddr_ (Fetch_ DskMAddr)+1, Block, Call[Read1Muff]; KTemp0_ A0, TIOA[DiskData], Branch[TCheckBadTW, R even]; * The first two words are treated differently from the rest: * they are ALWAYS checked and are NEVER stored. Handle these in-line. * MD = first word from memory; KTemp0 accumulates the OR of all differences. T_ Input; T_ T XOR MD; KTemp0_ (KTemp0) OR T; DskMAddr_ (Fetch_ DskMAddr)+1, Block; T_ Input; T_ T XOR MD, DskMAddr, Branch[TCheckLast, R>=0]; * Branch if only 2 words in block KTemp0_ (KTemp0) OR T; DskMAddr_ (Fetch_ DskMAddr)+1, Block, Branch[TCheckLoop]; TCheckLast: KTemp0_ (KTemp0) OR T, Branch[TCheckDone]; * Main check loop -- 4 cycles per word. * At the top of the loop, MD = the word fetched from memory during the * previous cycle; KTemp0 accumulates the OR of all differences; * Carry is set iff this is the last iteration. TCheckLoop: PD_ MD, T_ KTemp1_ Input; T_ T XOR MD, Branch[TCheckStore, ALU=0]; * Don't check if MD=0 KTemp0_ (KTemp0) OR T, Branch[.+2, Carry]; DskMAddr_ (Fetch_ DskMAddr)+1, Block, * More to go, fetch next DblBranch[TCheckLoop, TCheckError, ALU=0]; DskMAddr_ (DskMAddr)+1, * Last word, check it and done DblBranch[TCheckDone, TCheckError1, ALU=0]; * If a check error occurs, store the data that was actually read, * and then just read the rest of the block. TCheckError: DskMAddr_ (DskMAddr)-1, Branch[TCheckStore]; TCheckError1: DskMAddr_ (DskMAddr)-1, Branch[TCheckStore]; * As soon as we encounter a zero word from memory, stop checking and * just store the disk data into memory. TNoCheckLoop: KTemp1_ Input; TCheckStore: T_ (DskMAddr)-1, Branch[TCheckStoreLast, R>=0]; DskMAddr_ (Fetch_ DskMAddr)+1; Store_ T, DBuf_ KTemp1, Block, Branch[TNoCheckLoop]; TCheckStoreLast: Store_ T, DBuf_ KTemp1; * Check command (cont'd) * Here when done checking. KTemp0 is nonzero if there were any differences. TCheckDone: PD_ KTemp0, TIOA[DiskMuff]; T_ clearCompareErr, Branch[.+2, ALU#0]; * No differences: clear CompareErr. Note that if a compare error occurred * in an earlier block, ReadDataErr will be set, and this will NOT clear it. Output_ T, Branch[.+2]; * Check error occurred. Set Compare Error status bit here so that the * error is properly reported in the status for this block. * The error will be captured by the hardware (as ReadDataErr) at the * beginning of the next block, and will be reported in the status for * all subsequent blocks until the software issues a Reset. KStatus_ (KStatus) OR (100C); * Now continue on and read the ECC. TIOA[DiskData], Block; Call[CheckECC]; * At this point, the ECC words are in T and KTemp0. * This is the tail of both reading and checking. * KTemp3 points to first ECC word for this block. TReadCheckEnd: PD_ (KTemp0) OR T, MemBase_ IOBR; T_ (Store_ KTemp3)+1, DBuf_ T, Branch[.+2, ALU=0]; * Store ECC0 KStatus_ (KStatus) OR (10C); * ECC error T_ (Store_ T)+1, DBuf_ KTemp0; * Store ECC1 KCmmd_ LCY[KCmmd, KCmmd, 2]; * Shift command for next block KTemp0_ muffReadError; KTemp3_ T, MemBase_ IOBR, Branch[TCmmdLoop]; * 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 SectorOverflow. TReadBadTW: Nop; TCheckBadTW: KCmmd_ T_ A0, TIOA[DiskControl]; * No more commands KTemp0_ A0, Output_ T, Call[OutputGetsT]; * Reset controller KStatus_ 1000C, Branch[TReadCheckEnd]; * Post this status *----------------------------------------------------------- * Command has completed. * KTemp0 has current hardware status (0 if no errors have occurred). *----------------------------------------------------------- TCmmdEnd: PD_ KTemp0, MemBase_ IOBR; * Error occurred? T_ A0, TIOA[DiskControl], Branch[.+2, ALU=0]; Output_ T, Call[OutputGetsT]; * Yes, reset command register T_ clearSeekTagTW, Call[DoMuffOutput]; * Clear TW caused by TagDone T_ (K400)+(214C); * Fifo over/underflow, Fifo/IOB PE? PD_ (KTemp0) AND T; T_ (KPtr)+(3C), Branch[TCmmdCatastrophe, ALU#0]; Fetch_ T, T_ K400; * KCB+3 is pointer to next command T_ T+(240C), KPtr_ MD; Store_ T, PD_ DBuf_ KPtr; * Store it in VM 640 Branch[TriIdleLoop, ALU=0]; * Is there a command? Branch[DoTCmmd]; * Yes, go to it directly *----------------------------------------------------------- * Catastrophic error occurred. * Reset command register in case we have gotten out of sync with controller. * Then disable further KCB processing and post abort status in VM 644. * Format of status word in 644 (as defined by Alto and by Triex): * B5 seek timed out (not implemented; Triex thinks it's B4!) * B6 ECC compute error (not implemented: indistinguishable from * check error!) * B7 data late: microcode may be out of sync with hardware * B9 IOB parity error * B10 Fifo parity error * B11 invalid seal * B13 read task hung (not implemented) * B14 KCB aborted because of one of these errors * B15 failed to find desired sector within 64 sector times *----------------------------------------------------------- TCmmdCatastrophe: T_ (K400)+(200C); PD_ (KTemp0) AND T; * Fifo over/underflow? T_ (KTemp0) AND (14C), Branch[.+2, ALU=0]; * Mask IOB and Fifo PE T_ T OR (40C); * Report over/underflow as data late KStatus_ LSH[T, 3]; * Shift into position TCmmdAbort: KStatus_ (KStatus) OR (2C); * Set abort bit T_ (K400)+(244C); * Store abort status in VM 644 Store_ T, DBuf_ KStatus, KStatus_ A0; T_ (K400)+(240C); * Zero VM 640 to abort command chain Store_ T, DBuf_ KStatus, Branch[TriIdleLoop]; TCmmdBadSeal: KStatus_ 20C, Branch[TCmmdAbort]; TBadSector: KStatus_ 1C, Branch[TCmmdAbort]; *----------------------------------------------------------- * Reset/Restore command. * This is the only place where ClearErrors is done to reset latched errors. * KTemp0 has the command. *----------------------------------------------------------- TResetRestore: Nop; T_ (KTemp0) OR (tagControl); KTemp3_ T, Call[SendTag]; T_ clearErrors, Call[DoMuffOutput]; PD_ LSH[KTemp3, 16]; * Is this a restore? KStatus_ A0, Branch[TResetDone, ALU>=0]; * For Restore, must block til drive is ready and an Index pulse arrives. T_ clearAllTWs, Call[DoMuffOutput]; T_ K400, TIOA[DiskControl], Call[OutputGetsT]; * BlockTilIndex T_ (K400)+(242C); * Forget saved cylinder address Store_ T, Sector_ DBuf_ -1C, Block; * Now finish up by storing header status. Know KCmmd[14:15]=0 here. TResetDone: T_ (KPtr)+(12C); * Address of header status Branch[TResetFinish]; *----------------------------------------------------------- InitRamTrident: * Init format Ram for Trident emulation. * Enter: KPtr = KCB pointer for some Trident command (0 if none) * TridentFlag[12:15] = current subsector count * Assumes Ram words 5-17 are already set up for Diablo emulation. * Exit: Updates TridentFlag[12:15] * Clears TridentFlag[1] if sector size changed, to force drive select. * Clobbers T, KTemp0, KTemp1, KTemp2, KTemp3, KStatus, TIOA * Strategy: InitRam is called when switching between Diablo and Trident mode, * when switching drives, and whenever a new chain of commands is started. * If there is a command, and the command transfers a data block, * then set up the format Ram and select a sector format according * to the command. If there is no command or the command is a non-data * command, then assume whatever sector format is currently selected. * This strategy depends on the assumption that nobody sets up command * chains consisting of a non-data command followed by data commands. *----------------------------------------------------------- Subroutine; PD_ KPtr; KTemp3_ Link, Branch[RamSetup, ALU=0]; TopLevel; T_ (KPtr)+(6C); T_ (Fetch_ T)+(14C); * KCB+6 = header count KTemp0_ MD-1; T_ (Fetch_ T)-(6C); * KCB+22 = data count KTemp2_ MD-1; Fetch_ T, PD_ KTemp2; * KCB+14 = label count KTemp1_ MD-1, Branch[.+2, ALU>=0]; Branch[RamSetup]; * No data count, skip subsector select * Choose subsector count. 9-sector format assumes drive is jumpered for * 117 sectors and chooses a subsector count of 14B. 16-sector format assumes * drive is jumpered for 16 sectors and chooses a subsector count of 0. T_ (KTemp2) AND (1000C); * Data block > 512 words? KStatus_ TridentFlag, Branch[.+3, ALU=0]; T_ 14C; * Yes, choose 14B MaxSectors_ 11C, Branch[.+2]; * 9 sectors around MaxSectors_ 20C; * No, choose 0; 16 sectors around T_ (KStatus) XOR T; * See if different from current T_ T AND (17C); TridentFlag_ (TridentFlag) XOR T, Branch[.+2, ALU=0]; TridentFlag_ (TridentFlag) AND (Not[40000]C); * Force drive select KSelect_ (KSelect) AND (Not[4000]C); * Sectors evenly divide the disk * Load words 0-4 of format Ram; remaining words are same as for Diablo. RamSetup: T_ A0, TIOA[DiskControl], Call[OutputGetsT]; * Clear Ram address T_ KTemp0, TIOA[DiskRam]; T_ KTemp1, Output_ T, * [0] header count -1 Call[OutputGetsT]; * [1] label count -1 T_ A0, Output_ KTemp2, * [2] data count -1 Call[OutputGetsT]; * [3] unused count T_ Or[tagRead!, tagHeadSelect!]C; T_ T OR (tagAltoLeader); * [4] control tag for read Link_ KTemp3, Branch[OutputGetsT]; * Output_ T and Return *----------------------------------------------------------- SetDiskBRHi: * Set BRHi for disk transfers * Enter: T = amount to add to MDSHi to yield correct value * MemBase = DiskBR * Clobbers T *----------------------------------------------------------- Subroutine; RBase_ RBase[EmuBRHiReg]; T_ (EmuBRHiReg)+T, RBase_ RBase[DiskRegs]; BRHi_ T, Return; (1552)