; MaxcImp.mu -- Microcode for driving Maxc2 Alto Imp interface ; Last modified April 13, 1978 7:45 PM ; Derived from Alto-1822 microcode by Larry Stewart. ; The software interface is the same, but the implementation is more ; time-efficient due to the use of a number of additional S-registers. ; This microcode runs only on Alto-I. ; Data structures: ; Imp Command Block (ICB) ; word 0 control word -- command ; 1 unused ; 2 input pointer -- next word to be used ; 3 input buffer end -- first word not in buffer ; 4 output pointer -- next word to be used ; 5 output buffer end -- first word not in buffer ; 6 control post location ; 7 control interrupt channels ; 10 input post location ; 11 input interrupt channels ; 12 output post location ; 13 output interrupt channels ; Microcode status codes: $ISDone $777; Normal completion. $ISOvf $1377; Buffer overflow (input only) $ISIBLZ $1777; Block length zero (input only) ; Imp Task-specific functions: $IREAD $L 000000, 070017, 000100; F1=17 ← Input data, branch if end $IWRITE $L 020016, 000000, 120000; F1=16 Output data ← $IOCLR $L 016015, 000000, 000000; F1=15 Clear output wakeup $IPOSTF $L 016014, 000000, 000100; F1=14 ← Status $ISWAKC $L 024014, 000000, 000000; F2=14 Clear SIO-generated wakeup $IBRNCH $L 024013, 000000, 000000; F2=13 4-way branch on wakeup $IIENBL $L 024012, 000000, 000000; F2=12 Start read (turn on RFNIB) $ISETCS $L 026011, 000000, 120000; F2=11 Set control ← $IPTMOD $L 024010, 000000, 000000; F2=10 2-way branch on throwaway mode ; S-registers dedicated to this task $ICBPtr $R70; Imp Command Block pointer $IPtr $R71; Input Pointer -- last word stored $IEPtr $R72; Input end pointer -- first word beyond end of buffer $OPtr $R73; Output Pointer -- last word fetched $OEPtr $R74; Output end pointer -- first word beyond end of buffer %14, 14, 0, IIStart, IControl, IOData, IIData; 00xx, 01xx, 10xx, 11xx !1, 2, IPost, IReset; !1, 2, IIEnab, IIZero; %4, 4, 0, IIAcpt, IIDisc; x0xx, x1xx %5, 5, 0, IIMore, IIFull, IILast, IIFLst; x0x0, x0x1, x1x0, x1x1 %4, 5, 1, IIDMor, IIDLst; x0x1, x1x1 !1, 2, IOMain, IOInit; !1, 2, IOMore, IOEnd; !1, 2, IONLst, IOLast; $12 $12; ; *** Imp Task starting address and main loop *** ImpTask: L← 0, TASK, :IOFin; Indicate no output in progress ; Task always blocks here and wakes up when there is something to do. ImpLoop: T← ICBPtr, IBRNCH; Branch on wakeup condition :IIStart; [IIStart, IControl, IOData, IIData] ; *** Common Post routine *** ; Expects offset of post location in T and post code in M. IPost: MAR← ICBPtr+T; Reference post location T← NWW; MD← M, IPOSTF; Bus AND hardware status with post code L← MD OR T, TASK; OR interrupt bits into NWW NWW← L, :ImpLoop; ; *** Imp Control/Status command *** ; Establishes command block pointer, issues command given in command word, ; and posts control status. IControl: MAR← L← AC1; Fetch control word ICBPtr← L, ISWAKC; Save control block ptr, clear wakeup ISETCS← MD, L← MD-1; Issue control function; Master Reset? T← 6, SH=0; T← control post offset IPDone: L← ISDone, :IPost; [IPost, IReset] L← done status ; Here if Master Reset was issued. IReset: L← 0; Note no output in progress IODone: OPtr← L, :IPDone; ; *** Input initialization *** ; Reads pointers from control block and enables Imp input. IIStart: MAR← 2+T; Reference ICB word 2 ISWAKC; Clear SIO-generated wakeup T← MD-1; T← input pointer -1 L← MD; T← end pointer IEPtr← L; IEPtr← end pointer L← IEPtr-T-1; Test for zero-length buffer L← T, T← 10, SH=0; T← input post offset IPtr← L, :IIEnab; [IIEnab, IIZero] IPtr← input ptr -1 IIEnab: TASK, :IIDMor; ; Here if input buffer length is zero. IIZero: L← ISIBLZ, :IPost; Post buffer length zero error ; *** Input main loop *** ; Note that the instruction at IIAcpt has two branches: a NEXT[9] branch ; for SH=0 (buffer full) and a NEXT[7] branch for IREAD (last word). IIData: MAR← T← IPtr+1; Start data store L← IEPtr-T, IPTMOD; Test buffer full, test discard mode L← T, T← IREAD, SH=0, :IIAcpt; [IIAcpt, IIDisc] Read data word IIAcpt: IPtr← L, L← T, :IIMore; [IIMore, IIFull, IILast, IIFLst] IIMore: MD← M, TASK; Store data word IIDMor: IIENBL, :ImpLoop; Enable input of next word ; Here after reading last word. IILast: MD← M; Store data word T← ICBPtr; MAR← 2+T; Reference ICB word 2 L← IPtr+1, TASK; Store pointer to last word used +1 MD← M; ; Note that our wakeup is still asserted because the IREAD that reads the ; last word doesn't clear it. It is necessary to issue another IREAD ; in this case. IILst1: SINK← IREAD; Clear the wakeup (can't branch) T← 10, :IPDone; T← input post offset, post done ; Here when we are in discard mode. ; There is a NEXT[7] branch for IREAD (last word), which we take, and ; a NEXT[9] branch for SH=0 (buffer full), which we squash. IIDisc: TASK, :IIDMor; [IIDMor, IIDLst] IIDLst: :IILst1; ; Here when input buffer overflows. IIFLst: SINK← IREAD; Also last word, clear wakeup IIFull: T← 10; T← input post offset L← ISOvf, :IPost; Post input overflow status ; *** Output main loop *** ; OPtr contains zero when no output is in progress. ; That is how we decide whether we are initializing or in the main loop. IOData: MAR← T← OPtr+1, BUS=0; Start data fetch, see if first L← OEPtr-T, :IOMain; [IOMain, IOInit] IOMain: L← M-1, IOCLR, SH=0; Last word already sent? L← 0, SH=0, :IOMore; [IOMore, IOEnd] Is this last word? IOMore: IWRITE← MD, L← T, :IONLst; [IONLst, IOLast] Send the data word IOLast: ISETCS← 2; Set last word flop if appropriate IONLst: TASK; IOFin: OPtr← L, :ImpLoop; Update output pointer ; Here when woken up after last word sent. IOEnd: T← 12, :IODone; T← Output post offset; ; Zero OPtr and post normal done ; Here when woken up with no output in progress. IOInit: T← ICBPtr; Reference ICB word 4 MAR← 4+T; NOP; L← MD-1; L← output pointer -1 T← MD; T← end pointer OPtr← L, L← T, TASK; OEPtr← L, :IOData; Now send first word