; FILE ' TriconBody.mu ' -- by Roger Bates ; Body of Trident microcode -- requires definitions in TriConMc.mu ; or equivalent. ; Copyright Xerox Corporation 1979, 1981 ; Last modified June 26, 1981 3:29 PM by Taft ; ; MICROCODE FOR TRIDENT DISK CONTROLLER ; THIS MICROCODE IS WRITTEN TO OPERATE ON BOTH ALTO I AND ALTO II's. ; ;*modified to abort command chain if write-data-late occurs so as ; to avoid chaining into garbage commands. ; Generally tightened things up and made the microcode more robust. ; ;*broken into two parts (global predefs and everything else) ; to facilitate combining Trident microcode with other stuff. ; ;*modified to speed up read task main loop for Alto-IIs. ; ;*modified so un-needed words are not written into MESA's parity locations. ; The new discard locations are 177034 and 177035 (output to alto keyboard). ; ;*modified to allow safe switching of drive select between chained ; disk commands. This mod is achieved by first checking to see if a new ; drive is specified; if so: ; Isue a TAG instruction to select the new drive. ; set the "Last Cylinder" value to -1 so a seek will allways occur ; wait for the next sector pulse to occur (on the newly selected drive) ; so we know that the Tag instruction was the last one issued, and that ; it has infact been executed ; ;*modified for running on both AltoI and AltoII's. ; ;*modified for testing of abnormal conditions in sector loop with abort and ; status in 644. this version doesn't update 640 untill a command is complete ; so that the read task can get its dcb pointer from 640 when needed ; ; ;*Location #03 starts the microcode for sending control and data ; out to the disk controller; ;*Location #17 starts the microcode for receiving data back from ; the disk controller; ;*Location #21 starts a Pollynomial divide routine used for ; disk error recoveries; ; TRIDENT DISK MICROCODE - TASK 03 - SERVICE OF OUTPUT FIFO ; TRIDENT DISK MICROCODE - TASK 17 - SERVICE OF INPUT FIFO ; ; PROCESSES A KBLK (IN PAGE 0) WHICH LOOKS LIKE: ; 640 POINTER TO DCB ; 641 LAST DRIVE SELECTED ; 642 LAST TRACK SELECTED ; 643 STATUS AT SECTOR MARK ; 644 STATUS AT COMMAND ABORT ; ; PROCESSES A DCB WHICH LOOKS LIKE: ; DCB TRACK ADDRESS ; DCB+1 HEAD(LEFT BYTE), SECTOR(RIGHT BYTE) ; DCB+2 DRIVE SELECT ; DCB+3 POINTER TO NEXT DCB ; DCB+4 ID (#122645) ; [ ; DCB+5 FIRST COMMAND ; DCB+6 WORD COUNT ; DCB+7 MEMORY ADDRESS FOR DATA TRANSFER ; DCB+8 ECC0 - TO BE FILLED IN BY READ TASK ; DCB+9 ECC1 - TO BE FILLED IN BY READ TASK ; DCB+10 STATUS AT END OF TRANSFER ; ] repeat ; DCB+? Command word of zero to terminate ; DCB+?+1 Mask of channel(s) to interrupt on ; ; ; **SPECIAL FUNCTION 2 DEFINITIONS** ; ; "FOO← STATUS" READS THE STATUS F2 = 10 ; "MD← KDTA" READS A DATA WORD F2 = 06,BUS = 2 ; "KTAG← FOO" WRITES A TAG INSTRUCTION F2 = 12 ; "KDTA← FOO" WRITES A DATA WORD F2 = 13 ; "WAIT" IS IDENTICAL TO 'BLOCK' F2 = 14 OR 15 ; "RESET" RESET INPUT FIFO & CLR ERRORS F2 = 16 ; "EMPTY" WAITS FOR OUTPUT FIFO TO EMPTY F2 = 17 ; $STATUS $L66010, 66010, 000100; DF2=10 (RHS) $nSTATUS$L24010, 00000, 000100; NF2=10 = STATUS but doesn't set BS -- ; this is for Bus ANDing $KDTA $L26013, 14002, 124100; DF2=13 (LHS); BS=2 (RHS) $KTAG $L26012, 00000, 124000; DF2=12 (LHS) REQUIRES BUS DEF $WAIT $L24014, 00000, 000000; NF2=14 $RESET $L24016, 00000, 000000; NF2=16 $EMPTY $L24017, 00000, 000000; NF2=17 ; Wakeup control: ; WAIT and EMPTY must be issued at least 2 instructions before a TASK ; in order for them to "take". ; KDTA← and ←KDTA must be issued at least 2 instructions before a TASK ; in order to affect the priority computation done by that TASK, ; but wakeups are not generated unless the FIFO has at least 4 words of ; data (read task) or at least 4 words of free space (write task). ; This permits the read/write loops to be tight, but requires some care ; at the end of a transfer. ; **HANDY CONSTANTS** $400 $400; $4000 $4000; $10000 $10000; $177700 $177700; $177777 $177777; $612 $612; $BITBUCKET $177034; This is the address for READING the alto keyboard ; **R REGISTERS** $SHREG $R14; MUST BE A REAL 'R' REGISTER $COMM $R14; SET SAME AS SHREG; USED IN BUS=0 $MTEMP $R25; $WCBPTR $R71; $LAST1 $R72; $FROM $R73; $RCBPTR $R74; $FROM/R $R75; $LAST1/R $R76; $AC0 $R3; $AC1 $R2; $NWW $R4; $NUM $R2; Note sharing with AC1; must be a real 'R' register $REF $R76; Note sharing with LAST1/R ; Note: on an Alto-I it is unsafe to test BUS=0 when reading an S-register ; in the first instruction after a task switch. Testing BUS=0 in other than ; the first instruction is OK. ; **PREDEFINITIONS FOR TEST CONDITIONS** !1,2,DCB+4,DONE; !1,2,REPST,DCB+2A; !1,2,ID-ERR,DCB+2; !1,2,NEWDR,DCBA; !1,2,DCB,LOC3A; !1,2,CHKWDL,NOWDL; !1,2,NOWDL2,WABORT; !1,2,SENDTRK,DCB+1L; !1,2,CKSEC,NOWAIT; !1,2,SCOM,SECTOR; !1,2,WAITSEC,SECOK; !1,2,PASTSEC,SCOMM; !1,2,NOEMPTY,SEMPTY; !1,2,DECODE,SNDSTAT; !1,2,NOINT,SNDINT; !1,2,R/WCOM?,DCB+3; !1,2,R/WCOM, NO-R/W; !1,2,WRITE,READ; !1,1,NORW+1; !1,1,WRITE+1; !1,2,W-EVEN,W-ODD; !1,2,CONT/W,DONE/W; !1,2,CHECK,DCB+5; !1,2,END/CK,DCB+5+1; !1,2,SEND/CK,WDS/DONE; !1,1,DCB+5+2; !1,2,DCB+3A,WRT640; !1,2,CK-CNT,ABORT; *&* !1,2,SNDERR,SEC-ERR; *&* ; **WRITE TASK** ; **THIS CODE IS EXECUTED EACH SECTOR TIME** LOC3: T← 2, :LOC3B; SEE IF THERE IS A COMMAND LOC3A: T← 2; LOC3B: MAR← 642-T; NOP; L← MD; T← MD, SH=0; TEST FOR NO COMMAND ADDRESS WCBPTR← L, L← T, :DCB+4; [DCB+4, DONE] ; No DCB present. Normally, just update status in KBLK.Status. ; But if KBLK.drive has its sign bit set, also select that drive. DONE: SH<0; Test for new drive sel L← 7777 AND T, WAIT, :REPST; [REPST, DCB+2A] REPST: MAR← 642+1; UPDATE THE STATUS AT 643B L← STATUS, TASK, :W640X; ; **PROCESS A COMMAND** ; First, test for valid ID (seal), and check for new drive selection. DCB+4: T← 4; MAR← WCBPTR+T; GET CHECK WORD MTEMP← L; Save current drive select T← 122645; L← MD-T; T← 2, SH=0; TEST FOR VALID ID MAR← WCBPTR+T, :ID-ERR; [ID-ERR, DCB+2] GET DRIVE SELECT DCB+2: LAST1← L; LAST1=0 signals first block of sector FROM← L; FROM=0 signal previous write (=> store status) T← MTEMP; TEST FOR DRIVE SEL EQ CURRENT DRIVE L← MD - T, T← MD; L← T, SH=0, TASK; DCB+2A: COMM← L, :NEWDR; [NEWDR, DCBA] COMM← new drive number ; Select new drive. ; Can get here even if there is no DCB (see above). NEWDR: MAR← 642-1; UPDATE KBLK+1 T← 77400, WAIT; Send drive select TAG and wait for next sector MD← COMM, L← COMM OR T; This will make sure that everything is stable MAR← 642; CLOBBER CYLINDER TO FORCE A SEEK KTAG← M; SINK← WCBPTR, BUS=0, TASK; TEST FOR NO COMMAND IN PROGRESS MD← 177777, :DCB; [DCB, LOC3A] ; WRITE TASK (cont'd) ; Fetch cylinder select and check current hardware status DCB: MAR← WCBPTR, :DCBB; GET CYLINDER SELECT DCBA: MAR← WCBPTR; DCBB: L← 400, nSTATUS; Bus AND -- check status for write data-late T← 5, SH=0; L← MD, :CHKWDL; [CHKWDL, NOWDL] ; Write data-late present in status. ; Inspect command and abort unless it is a diskReset. ; This is because a previous command may have dumped garbage into ; this command's disk address (by the label chaining trick). CHKWDL: MAR← WCBPTR+T; Fetch header command COMM← L; T← 10; Test reset command bit L← MD AND T; L← COMM, SH=0; ; Seek to new cylinder if different from current NOWDL: MAR← 642, :NOWDL2; [NOWDL2, WABORT] NOWDL2: COMM← L; T← COMM; L← MD-T; T← 7777.T, SH=0; TEST FOR SAME CYLINDER ADDRESS T← 10000 OR T, :SENDTRK; [SNDTRK, DCB+1L] SENDTRK: L← 40000 OR T, WAIT; WAIT UNTIL POSITIONING DONE MAR← 642; UPDATE KBLK+2 KTAG← M, TASK; MD← COMM; ; Now at correct cylinder. ; Select head DCB+1L: MAR← WCBPTR+1; GET HEAD SELECT T← 177400, EMPTY; LEFT BYTE ONLY L← MD AND T, T← MD; L← Left byte only, T← whole word MAR← WCBPTR+1; Store back word with OffTrack bit stripped off SHREG← L LCY 8; L← 37777 AND T, TASK; MD← M; ; Wait here until write FIFO is empty before sending head select tag. ; (I think this is so that the new head select won't interfere ; with an ongoing read). T← 30000; L← SHREG OR T, TASK; KTAG← M, :DCB+5; Send Head Select Tag instruction ; WRITE TASK (cont'd) ; Come here at the beginning of each "block" in the disk command: DCB+5: T← 5; DCB+5+1: MAR← L← WCBPTR+T, :DCB+5+2; [DCB+5+2, DCB+5+2] Fetch command word DCB+5+2: WCBPTR← L; T← 300; L← MD AND T, T← MD; L← 7777 AND T, SH=0; TEST FOR READ/WRITE COMMAND COMM← L, :CKSEC; [CKSEC, NOWAIT]; COMM← command ; Command is read or write, may have to wait for correct sector CKSEC: L← LAST1; L← 100, SH=0; TEST FOR FIRST BLOCK NOWAIT: LAST1← L, :SCOM; [SCOM, SECTOR] ; First block of a read/write command. Search for correct sector SECTOR: T← 4; MAR← WCBPTR-T; Fetch sector number from DCB L← LAST1-1, BUS=0; TEST FOR SECTOR LOOP HUNG LAST1← L, :CK-CNT; [CK-CNT, ABORT] CK-CNT: T← 17; L← MD AND T; T← 2000 OR T; Test sector = next sector and NotReady = 0 T← STATUS.T; L← M-T, WAIT; TURN OFF WAKE UP TILL SECTOR T← 10000, SH=0; TEST FOR NEXT SECTOR OK PASTSEC: T← 10 OR T, :WAITSEC; [WAITSEC, SECOK] WAITSEC: TASK; WAIT FOR NEXT SECTOR :SECTOR; ; Next instruction forces the controller to wait until the next sector ; pulse before executing any more commands from the FIFO. SECOK: KTAG← 100000; A NON-KTAG INS IS REQUIRED NEXT! L← 4+T, T← 4; L← 10014; T← 4; KTAG← M; ENABLE HEAD AND RESET DISK ERR'S MAR← WCBPTR-T; KTAG← M; 3 TIMES SO HEAD IS ENABLED KTAG← M; FOR 5 US. ; Here we test the sector number all over again, though we just checked it ; about 10 microinstructions ago, with no intervening TASKs. ; It really makes a difference, though! If you don't do this you get ; occasional header errors, sector overflows, etc. Puzzlement! T ← 17; L← MD AND T; T← 2000 OR T; Test sector = next sector and NotReady = 0 T← STATUS.T; L← M-T; SH=0; TEST FOR NEXT SECTOR OK L← T← COMM, :PASTSEC; [PASTSEC, SCOMM] ; This code is to handle errors in the sector loop, and ; other errors that cause the command chain to be aborted. ID-ERR: T← 20, :SNDERR; ***#20 = INVALID ID WABORT: T← 400, :SNDERR; ***#400 = write data-late ABORT: L← T← 2000, nSTATUS; ***#2000 = SEEK INCOMPLETE SH=0; TEST FOR SEEK OK :SNDERR; [SNDERR, SEC-ERR] SEC-ERR: T← M+1; ***#1 = INVALID SECTOR SNDERR: L← 2+T, T← 2; ***#2 = ONE OF THE ABOVE MAR← 642+T; Store abort code in 644 LAST1← L, WAIT; L← KTAG← 0; Reset all tags MD← LAST1; WRT640: MAR← 642-T; TASK; W640X: MD← M, :LOC3; ; WRITE TASK (cont'd) ; Now ready to issue the new command. SCOM: L← T← COMM; SCOMM: L← 10000 OR T, SH=0; Test for end of commands (COMM = 0) MAR← T← WCBPTR+1, :NOEMPTY; [NOEMPTY, SEMPTY] Fetch count or int mask ; End of commands in this DCB SEMPTY: KTAG← 0, L← T; EMPTY; Wait til write FIFO empty before advancing WCBPTR← L; L← MD, TASK, :LINK; Get interrupt bits ; Issue new command NOEMPTY: KTAG← M, L← T; WCBPTR← L; L← MD + 1; GET WORD COUNT+1 LINK: LAST1← L; Save word count +1 or interrupt bits ; Note the following code gets executed even if we have reached the end ; of commands in this DCB, because we want to store status and generate an ; interrupt if the previous command was a write. The extra KDTA← is harmless. MAR← WCBPTR+1; Fetch data address KDTA← LAST1; SEND WORD COUNT+1 L← FROM; L← MD-1, SH=0, TASK; TEST FOR PREVIOUS WRITE FROM← L, :DECODE; [DECODE, SNDSTAT] FROM ← ADDRESS-1 ; Either the previous command was a write or the current command is ; the first one in the DCB. Write the status into the word before ; the start of the current block, which is the status word of the previous ; block if there was one and the DCB seal word otherwise. SNDSTAT: L← 177740, nSTATUS; GET JUST THE STATUS BITS T← 2; MAR← WCBPTR-T; UPDATE STATUS SUCH THAT GOOD STATUS = 1 L← M+1, nSTATUS; Bus AND status again to filter out glitches SINK← COMM, BUS=0; MD← M, :NOINT; [NOINT, SNDINT] ; The previous command was a write and was the last one in the DCB. ; Initiate interrupts as specified in the word after the zero command word. SNDINT: T← LAST1; Interrupt bits fetched above L← NWW OR T, TASK; NWW← L, :DCB+3; NOINT: T← FROM-1, :DECOD1; DECODE: T← FROM-1; DECOD1: L← LAST1+T, TASK; LAST1← L; LAST1← address of last word of data +1 ; Now look at the command and decide what to do. T← COMM, BUS=0; TEST FOR ALL ZERO COMMAND WORD L← 300 AND T, :R/WCOM?; [R/WCOM?, DCB+3] Test write+read bits ; Nonzero command, what is it? R/WCOM?: L← 200 AND T, SH=0; Test write bit L← 4000 AND T, SH=0, :R/WCOM; [R/WCOM, NO-R/W] Test check bit ; Non read-write command, assume it is a diskReset or diskRestore. ; For a disk rezero operation to work, the control tag line must be removed ; while leaving the rezero bit on the tag bus ie KTAG← 2. ; The RESET command will terminate the read task, possibly while it is ; in the middle of a command; it won't wake up again until a new read ; command comes along. Therefore, we zero RCBPTR, FROM/R, and LAST1/R, ; which the read task must check upon resumption from every TASK. NO-R/W: RCBPTR← L; [NORW+1, NORW+1] Resynchronize read task NORW+1: KTAG← 2; FROM/R← L; LAST1/R← L, RESET, :DONE/W; RESET defines the input FIFO as empty ; WRITE TASK (cont'd) ; Read or write command, which? R/WCOM: T← FROM, SH=0, :WRITE; [WRITE, READ] ; *** Write command *** ; *** odd start address test WRITE: KDTA← ONE, L← ONE AND T; [WRITE+1, WRITE+1] SEND START BIT TO DISK WRITE+1: SH=0; TEST FOR EVEN WORD ADDRESS MAR← L← T← FROM+1, :W-EVEN; [W-EVEN, W-ODD] W-ODD: FROM← L, TASK, :W-ODD2; ; ***end of test LOOP/W: MAR← T← FROM+1; W-EVEN: L← LAST1-T; L← ONE+T, SH<0; TEST FOR LAST WORDS FROM← L, :CONT/W; [CONT/W, DONE/W] CONT/W: KDTA← MD, TASK; W-ODD2: KDTA← MD, :LOOP/W; ; *** odd end address test IS NOT NEEDED - ; CONTROLLER WILL IGNORE UNEXPECTED DATA WORDS. ; SO ONE EXTRA WORD MAY BE SENT, BUT IS OK. DONE/W: L← 0; Signal that previous command was a write FROM← L, :DCB+5; ; *** Read command *** ; Test on check bit pending. If not a check command, we are done with it. READ: :CHECK; [CHECK, DCB+5] ; Check command. Pour the words to be checked into the write FIFO. ; Note that the first two words of a block are always checked, ; followed by additional words until a zero word or the end of the block. CHECK: MAR← L← FROM+1; FROM← L; KDTA← MD; MAR← L← FROM+1; FROM← L, TASK; KDTA← MD; LOOP/CK: MAR← L← T← FROM+1; FROM← L; L← LAST1-T; T← 5, SH<0; TEST FOR LAST WORD L← MD, BUS=0, :END/CK; [END/CK, DCB+5+1] TEST FOR ALL ZEROS DATA WORD END/CK: TASK, :SEND/CK; [SEND/CK, WDS/DONE] SEND/CK: KDTA← M, :LOOP/CK; WDS/DONE: :DCB+5; INSTRUCTION AFTER A TASK ; Chain to next DCB, but be careful not to get confused if the software ; has yanked the current DCB out from under us. DCB+3: T←2; MAR←642-T; FIND THE START OF THIS DCB AGAIN NOP; L← MD; MAR← M+T+1, SH=0; GET THE POINTER TO THE NEXT COMMAND :DCB+3A; [DCB+3A, WRT640] DCB+3A: L←MD, :WRT640; NOW PUT THIS POINTER IN 640 ; **READ TASK** ; Note that the read task is highest-priority, so there is no point ; in TASKing except in those places where our wakeup has been removed ; and we desire to wait until the next wakeup. ; The write task can cause us to abort and resynchronize upon resumption ; after any TASK. This may be deduced from the fact that any one of ; RCBPTR, FROM/R, or LAST1/R is zero. (Note that FROM/R = LAST1/R = 0 ; is sufficient to break out of the main loops.) ; **PREDEFINITIONS FOR TEST CONDITIONS** !1,2,STRTBLK,DISCARD; !1,2,CHUCK,INITRD; !1,2,STRTRD,SKIP; !1,2,ALTO?,R-ODDS; !1,2,ALTOII,ALTOI; !1,2,CONT/I,DONE/I; !1,2,LOOP/II,DONE/II; !1,2,CONT/II,BRK/II; !1,2,R-DONE1,R-BRK; !1,2,R-ODDE,R-EXIT; !1,2,ECCERR,NOERR; !1,2,R-EXIT2,LOC17A; !1,2,NORINT,RINT; LOC17: T← 2, :LOC17B; LOC17A: T← 2; LOC17B: MAR← 642-T; T← 5; L← MD+T, BUS=0; TEST FOR NO DCB POINTER BLK?: RCBPTR← L, :STRTBLK; [STRTBLK, DISCARD] STRTBLK: MAR← L← RCBPTR+1; RCBPTR← L; T← MD; FIRST GET COUNT MAR← L← RCBPTR+1; NOW GET THE MEMORY POINTER NOP; L← MD-1; FROM/R← L; SAVE ADDRESS-1 MAR← RCBPTR-1; L← FROM/R+T; LAST1/R← L; LAST1/R = LAST ADDRESS T← 4000; L← MD AND T; L← RCBPTR+1, SH=0; TEST FOR NO COMPAIR COMMAND RCBPTR← L, :CHUCK; [CHUCK, INITRD] CHUCK: T← 2; L← FROM/R+T; IF COMPAIR THEN DISCARD THE MAR← BITBUCKET; FIRST 2 WORDS IN THE FIFO FROM/R← L; Also increment FROM/R by 2 MD← KDTA, TASK; MD← KDTA; INITRD: T← FROM/R; L← LAST1/R-T-1; SH<0; TEST FOR LAST WORDS L← ONE AND T, :STRTRD; [STRTRD, SKIP] SKIP: T← 177700, nSTATUS, :R-EXIT; ; READ TASK (cont'd) ; *** odd start address test STRTRD: MAR← T← FROM/R+1, SH=0; TEST FOR EVEN WORD ADDRESS-1 L← FROM/R, :ALTO?; [ALTO?, R-ODDS] R-ODDS: MD← KDTA, L← T; ; *** Test for ALTO I or ALTO II ALTO?: MAR← 612+1; FROM/R← L; SINK← MD, BUS=0, TASK; Test for an ALTO I :ALTOII; [ALTOII, ALTOI] ; ***Now the Double-Word transfer loop for ALTO I ALTOI: MAR← T← FROM/R+1; L← LAST1/R-T-1; L← ONE+T, SH<0; TEST FOR LAST WORDS FROM/R← L, :CONT/I; [CONT/I, DONE/I] CONT/I: MD← KDTA, TASK; MD← KDTA, :ALTOI; ; ***Now the Double-Word transfer loop for ALTO II ALTOII: T← FROM/R+1; Must do end test once before first iteration L← LAST1/R-T-1, T← LAST1/R; L← -2+T, SH<0; LAST1/R← last word -2 for end test LAST1/R← L, :LOOP/II; [LOOP/II, DONE/II] LOOP/II: MAR← T← FROM/R+1, BUS=0; Note this works ok on Alto-II! L← LAST1/R-T-1, :CONT/II; [CONT/II, BRK/II] CONT/II: MD← KDTA; MD← KDTA; MTEMP← L, L← 0+T+1, SH<0, TASK; Load MTEMP to zero the bus FROM/R← L, :LOOP/II; [LOOP/II, DONE/II] ; *** odd end address test DONE/II: T← LAST1/R+1, BUS=0, :R-DONE; T← Last address-1 DONE/I: T← LAST1/R-1, BUS=0; T← Last address-1 R-DONE: L← ONE AND T, :R-DONE1; [R-DONE1, R-BRK] R-DONE1: MTEMP← L, MAR← 0+T+1, SH=0; TEST FOR EVEN WORD ADDRESS T← 177700, nSTATUS, :R-ODDE; [R-ODDE, R-EXIT] R-ODDE: MD← KDTA; ; ***end of test ; Read the status twice to filter out possible glitch in status bits ; (particularly NotReady) caused by leading edge of Sector pulse. R-EXIT: L← STATUS AND T, TASK; BRK/II: FROM/R← L; ; If we get out of the read loop with RCBPTR=0, it can only happen ; if a RESET command cleared it (see write task, above). In this ; case, the header for the next transfer is sliding into the FIFO, ; so we might as well go read it. R-BRK: L← RCBPTR; Can't test with BUS=0 on Alto-I SH=0; ; READ TASK (cont'd) ;~~SINCE THIS MIGHT BE THE END OF READING, ; THE READ-TASK WAKE-UP MAY NOT BE ACTIVE, ; SO THERE MUST NOT BE A 'TASK' UNTIL ALL PROCESSING IS DONE. ; ;~~THIS IS THE HIGHEST PRIORITY TASK, SO IT WOULDN'T HELP ANY WAY. ; What's left in the FIFO now are: ; (1) 2 words of raw checksum, which we discard; ; (2) 2 words of computed ECC, which are both zero if no error has occurred. MAR← BITBUCKET, :R-EXIT2; [R-EXIT2, LOC17A] R-EXIT2: NOP; MD← KDTA; READ IN 2 EXTRA WORDS MD← KDTA; MAR← L← RCBPTR + 1; NOW ENTER THE FIRST ECC WORD RCBPTR← L; MD← T← KDTA; SAVE ECC0 IN T MAR← L← RCBPTR + 1; NOW ENTER THE SECOND ECC WORD RCBPTR← L; MD← KDTA, L← KDTA OR T; MD← ECC1, L← ECC1 % ECC0 T← FROM/R+1; Recover status word, set low bit (done status) MAR← L← RCBPTR+1, SH=0; TEST FOR ZERO ECC CODE RCBPTR← L, L← T, :ECCERR; [ECCERR, NOERR] ECCERR: L← 10 OR T; Nonzero ECC, set the ECC error bit NOERR: MD← M; Store status in block MAR← L← RCBPTR+1; NOW GET THE NEXT COMM RCBPTR← L; T← 100; L← MD AND T, BUS=0; Test for zero command word MAR← RCBPTR+1, :NORINT; [NORINT, RINT] Fetch interrupt mask (maybe) ; The previous command was the last one. Initiate interrupts as specified ; in the word after the zero command word. RINT: T← NWW; L← MD OR T, SH=0, TASK, :LOC16X; Know L=0 here--force branch to LOC17 NORINT: L← NWW, SH=0, TASK; SEE IF THE NEXT COMM IS NOT A READ LOC16X: NWW← L, :LOC16; [LOC16, LOC17] ; If we get here, it is presumed that another READ command ; awaits. But if a RESET was executed when the read wakeup ; was absent (after TASK two instructions back), RCBPTR will now ; be 0, irrespective of waiting commands. LOC16: L← RCBPTR; Can't test with BUS=0 on Alto-I SH=0, :BLK?; Go check and discard if RCBPTR = 0 ; Here if we are awakened with no DCB. Just discard 4 words ; to make our wakeup go away. DISCARD: MAR← BITBUCKET; L← 0; MD← KDTA; MD← KDTA; MAR← BITBUCKET; RCBPTR← L; MD← KDTA, TASK; MD← KDTA, :LOC17; ;************NOW START THE ECC MICROCODE ROUTINE************ ; JUMPRAM(21) ; AC0!0 = NUMBER ; AC0!1 = REFERANCE ; RETURNS AC0 = NUMBER OF SHIFTS ( > 4000B => error) ; Note that this microcode shares registers with the Trident read task. ; Therefore call this only when the disk is quiet. ; **PREDEFINITIONS FOR TEST CONDITIONS** !1,2, ECCCON, ECCXIT; !1,2, DOXOR, NOXOR; !1,2, BADECC, ECCLP; LOC21: MAR← AC0+1; GET REFERENCE NUMBER T← 3777; 11 bits worth L← MD AND T; MAR← AC0; GET NUMBER REF← L; L← MD; NUM← L, L← 0, BUS=0, TASK, :SETAC0; Set AC0 to zero and start loop ECCLP: T← NUM-1; See if NUM=REF L← REF-T-1; T← NUM+T+1, SH=0; T← NUM LSH 1 L← 4000 AND T, :ECCCON; [ECCCON, ECCXIT] Exit if NUM=REF ECCCON: L← T, T← 4000, SH=0; See if end-around carry T← 5 OR T, :DOXOR; [DOXOR, NOXOR] T← 4005 DOXOR: L← M XOR T; Yes, do XOR-feedback trick NOXOR: NUM← L; L← AC0-T; Error if more than 4005 iterations L← AC0+1, SH<0, TASK; Increment iteration count SETAC0: AC0← L, :BADECC; [BADECC, ECCLP] ECCXIT: :EXITRAM; BADECC: :EXITRAM;