; FILE ' TriconBody.mu ' -- by Roger Bates ; Body of Trident microcode -- requires definitions in TriConMc.mu ; or equivalent. ; Copyright Xerox Corporation 1979 ; Last modified November 13, 1979 2:35 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) $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 (write task) or at least 4 words of free space (read 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: T_ 400; Check current status for write data-late L_ STATUS AND T; 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; 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: T_ 2000; ***#2000 = SEEK INCOMPLETE L_ T_ STATUS . T; 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: T_ 177740; L_ STATUS AND T; GET JUST THE STATUS BITS T_ 2; MAR_ WCBPTR-T; UPDATE STATUS L_ M + 1; SUCH THAT GOOD STATUS = 1 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, :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, :R-ODDE; [R-ODDE, R-EXIT] R-ODDE: MD_ KDTA; ; ***end of test 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;