:TITLE[Ether]; * Ethernet I/O Address Registers Set[EIData, 3]; * Input data used with Input instructions Set[EImData, ADD[LSHIFT[EITask,4],3]]; *Input data used with memory references Set[EIHost, 1]; * Input data Set[EStatus, 2]; * Status/State register (read) Set[EOData, 1]; * Output data used with Output instructions Set[EOmData, ADD[LSHIFT[EOTask,4],1]]; *Output data used with memory references Set[EReadState, 2]; * State register read Set[EWriteState, 0]; * State register write * State Register command words MC[ESetPurgeMode, 260]; * Enables input MC[ESetOutputEOP, 107]; * Enables output, Jam MC[EEnableInput, 220]; MC[EEnableOutput, 103]; * Enables Jam MC[EDisableInput, 200]; MC[EDisableOutput, 100]; * Clears OutputEOP, disables Jam MC[EDisableInputOutput, 300]; * Disables input, output, clears outputEOP, Jam MC[EOReset,OR[LSHIFT[EOTask,4],0]]; * Status bits SET [ESICOLL, 200]; * Receiver-detected collision (Jam) SET [ESODL, 100]; * Output data late (Underrun) SET [ESIDL, 40]; * Input data late (Overrun) SET [ESOCOLL, 20]; * Transmitter-detected collision (Collision) SET [ESCRC, 10]; * Bad CRC SET [ESOFAULT, 4]; * Output DataFault (masked for now) SET [ESOPAR, 2]; * Output Bad Parity (masked for now) SET [ESICMD, 4]; * Input command issued ** Not in hardware: SET [ESOCMD, 2]; * Output command issued ** for Alto emulation only SET [ESIT, 1]; * Incorrectly terminated packet (Bad Alignment) MC[EISMASK, ESIDL, ESCRC, ESIT]; * Status bits reported for input command MC[EOSMASK, ESODL, ESOCOLL]; * Status bits reported for output command MC[ECMDBITS, ESICMD, ESOCMD]; * Command bits SetTask[EOTask]; * R-registers for output task SET[eoRB,LSHIFT[AND[EOTask,3],4]]; *enforces register allocation conventions RV[EOTemp2, ADD[eoRB,0]]; RV[EOTemp1, ADD[eoRB,1]]; RV[EOPtr, ADD[eoRB,2]]; * Buffer base register RV[EOPtrHi, ADD[eoRB,3]]; RV[EOCount, ADD[eoRB,4]]; * Main loop counter RV[EOTemp, ADD[eoRB,5]]; * Temporary registers *MDS register for Ethernet RV[eMDS,76]; *Contains 400b RV[eMDShi,77]; *Contains MDS SetTask[EITask]; * R-registers for input task (EOTask+1) SET[eiRB,LSHIFT[AND[EITask,3],4]]; *enforces register allocation conventions RV[EITemp2, ADD[eiRB,0]]; RV[EITemp1, ADD[eiRB,1]]; RV[EIPtr, ADD[eiRB,2]]; * Buffer base register RV[EIPtrHi, ADD[eiRB,3]]; RV[EICount, ADD[eiRB,4]]; * Main loop counter RV[EITemp, ADD[eiRB,5]]; * Temporary registers RV[EFlag, ADD[eiRB,10]]; * input under output flag (reg 10 of EITask) MC[pERandomReg, 377]; * Pointer to random number (REFR register) * Control block addresses (for Alto emulation, relative to 400) MC[EPLOC, 200]; * Post location MC[EBLOC, 201]; * Interrupt bit mask MC[EELOC, 202]; * Ending word count MC[ELLOC, 203]; * Load mask MC[EICLOC, 204]; * Input count MC[EIPLOC, 205]; * Input pointer MC[EOCLOC, 206]; * Output count MC[EOPLOC, 207]; * Output pointer MC[EHLOC, 210]; * Host address for address recognition * Timer masks (slot number is EOTask) Set [ETimerState, 5]; * Use Timer State 5 for simple timer MC[ETimerMask, LSHIFT [ETimerState, 14]]; *Ether constants required in both init and code *Microcode post codes (small integer in left half, ones in right half for XOR). *Note: value is complemented to get constant less than 8 bits. Use XNOR for formation of post code. MC[ESIDON, NOT [377]]; * Input done MC[ESODON, NOT [777]]; * Output done MC[ESIFUL, NOT [1377]]; * Input buffer overflowed MC[ESLOAD, NOT [1777]]; * Load overflow MC[ESCZER, NOT [2377]]; * Word count zero in input or output command MC[ESABRT, NOT [2777]]; * Command aborted (by SIO) * Miscellaneous MC[ECOLLMASK, 10000]; * Mask for collision detection SET TASK [EITask]; * ETHERNET INITIALIZATION subroutine (executed at EITask). * Will only be called if there is an Ethernet board in the machine. * Read Ethernet ID from board and form constant to be returned by SIO. ON PAGE [EtherInitPage]; EtherInit: T _ GETRSPEC[103] xor (377C), at[EtherInitLoc]; *Stkp (inverted) EITemp _ pEHostRegx; *EHostReg is in the emulator's R space Stkp _ EITemp, EITemp _ T, NoRegILockOK; Input[Stack,EIHost]; Stack _ (Stack) AND (377C); * ID in right half Stack _ (Stack) OR (77400C); Stkp _ EITemp; *restore Stkp eMDS _ 400c; *initialize base register eMDShi _ 0c, return; *this will disappear when the MDS switch is made SET TASK [0]; * EMULATOR TASK -- SIO instruction ON PAGE [EEPage]; *This code executes at task 0 * We get here after the SIO instruction has been issued. *The SIO control bits are in T (bits 16,17). * Get host address constant for Ethernet *RTEMP1 = 1 if called from Mesa, 0 if called from Nova *This code is really part of the emulator, and uses its temporary registers EESIO: AC0 _ T; T _ 177C; *save control bits (useful only if called from Mesa) T _ (ldf[EHostReg,0,10]) xor (T); *these bits will be 177b if the init code was run *(i.e. if there is an Ethernet board in the machine), and will be zero otherwise T _ EHostReg, goto[EESIODisp,ALU=0]; AC0 _ 0C; AC0 _ (AC0) xnor (100000C),goto[EESIO0]; *return 77777b in AC0 if no Ethernet board * Dispatch on low 2 bits of AC0 EESIODisp: Dispatch[AC0,16,2]; AC0 _ T, DISP [EESIO0]; * Host Address * 00 -- Do nothing EESIO0: lu _ RTEMP1, dblgoto[EEMesaRet,EENovaRet,Rodd], AT[EESIOLoc, 0]; EENovaRet: LoadPage[nePage]; CSkipData, GotoP[neTask1st]; EEMesaRet: loadpage[7]; PFetch1[LOCAL,LocalCache0,4], gotop[P7Tail]; *AC0 access above smashed local 0 * 01 -- Start transmitter * Form APC&APCTask word to notify the output microcode EESIO1: RTEMP _ AND[0377, EOStartLoc]C, AT[EESIOLoc, 1]; * Low 8 bits of APC GOTO [EESIONotify], RTEMP _ (RTEMP) OR (OR[lshift[EOTask,14],AND[007400, EOStartLoc]]C); * 10 -- Start receiver * Form APC&APCTask word to notify the input microcode EESIO2: RTEMP _ AND[0377, EIStartLoc]C, AT[EESIOLoc, 2]; * Low 8 bits of APC EESIO2A: GOTO [EESIONotify], RTEMP _ (RTEMP) OR (OR[lshift[EITask,14],AND [007400, EIStartLoc]]C); * 11 -- Reset interface, i.e. abort. Input task is notified to post abort. EESIO3: GOTO[EESIO2A], RTEMP _ AND[0377, EIAbortLoc]C, AT[EESIOLoc, 3]; * Low 8 bits of APC * Notify appropriate code EESIONotify: RCNT _ (EDisableInputOutput); T _ EOReset; OUTPUT[RCNT]; CALL[ETaskRet], APC&APCTASK _ RTEMP; lu _ RTEMP1, dblgoto[EEMesaRet,EENovaRet,Rodd]; *control returns to here when emulator next runs * * * INPUT TASK MICROCODE * SetTask[EITask]; ON PAGE [EIPage]; * Input microcode is notified at EIStart by the emulator (at SIO). * Some initialization is done, and the TPC set up to EIIDLE. * Initialize, enable hardware, and TASK EIStart: CALL [EINIT], EITemp _ EEnableInput, AT [EIStartLoc]; * Wake up here when first word of a new packet arrives. * Address filtering. EIIDLE: Input[EITemp1, EStatus]; *Check status for malformed packet lu _ (EITemp1) and (100400c); *Jam and bad alignment bits EITemp _ ESetPurgeMode, goto[.+2, ALU=0]; goto[EIPurge]; *flush bad packet INPUT [EITemp2, EIData]; * Read in first word T _ EHLOC; PFetch1[eMDS,EITemp1]; *Start fetch of host address T _ RSH [EITemp2, 10]; * Right justify destination host in T SKIP [ALU#0]; * Check for broadcast GOTO [EIBegin], EFlag _ 1C; * Broadcast packet LU _ (EITemp1) XOR (T); *Compare with host address SKIP [ALU#0], LU _ EITemp1; * ALU=0 => destination host = me GOTO [EIBegin], EFlag _ 1C; * Packet for me GOTO [EIPurge, ALU#0]; * ALU=0 => host is promiscuous GOTO [EIBegin], EFlag _ 1C; * Packet for me * Packet not accepted by filter, so tell hardware to ignore the rest of this packet. * Purge packet, and TASK EIPurge: OUTPUT [EITemp, EWriteState], call[ERet]; * Wakeup here at start of next packet GOTO [EIIDLE]; * Packet accepted by filter. * EFLAG is set to 1 to tell the output microcode that a packet came in * (used for input under output). * Disable output task if it is on (probably in retransmission wait). EIBegin: EITemp1 _ EDisableOutput; OUTPUT [EITemp1, EWriteState], call[ERet]; * Set up EIPtr and EICount for single word transfers. CALL[EBSetup], T _ EICLOC; * Set up EIPTR, EICount * Subroutine returns with: EIPtr = IPtr + ICount - 1, EICount = - ICount * Check if buffer count zero. If not first word gets stored in EIAlign loop. GOTO [EICountZero, R>=0], T _ EICount _ (EICount) + 1; * R>=0 => count is zero * Check buffer alignment. * Compute how many singles before first quadword, and form loop counter in EITemp1. * Address: x00 => no singles, loop count = -1 * Address: x01 => 3 singles, loop count = 2 * Address: x10 => 2 singles, loop count = 1 * Address: x11 => 1 singles, loop count = 0 T _ (EIPtr) + (T) + 1; * Form start address in T EITemp1 _ (ZERO) - (T); * Complement, increment EITemp1 _ (LDF[EITemp1, 16, 2]) - 1; CALL [EIAlignE], T _ EICount; * Set return for EIAlign loop EIAlign: GOTO [EIQuad, R<0], EITemp1 _ (EITemp1) - 1; GOTO [EIBufFull1, R>=0], T _ EICount _ (EICount) + 1; INPUT [EITemp2, EIData]; LU _ EITemp2; * Abort EIAlignE: RETURN, PStore1 [EIPtr, EITemp2]; * Now start quadword output. * Adjust EIPtr and EICount for 4-word transfers. EIQuad: EICount _ (EICount) + (3C); CALL [EILoop], EIPtr _ (EIPtr) - (6C); * Set return address for EILoop * Read the Hardware Input Buffer into the Main Memory In Buffer. EILoop: GOTO [EIQuadFull, R>=0], T _ EICount _ (EICount) + (4C); GOTO[EIAttn, IOATTEN]; IOStore4[EIPtr, EImData]; nop; nop; nop; goto[ERet]; *This is so that if the IOStore4 causes an H4PE, it won't cause a LoadPage *error in another task. * Get here when no more room for quadwords in buffer. * Check IOATTEN is high to see if end of packet. EIQuadFull: GOTO [EIAttn1, IOATTEN]; * Not end of packet. Do singles to fill buffer. * 7-EICount = number of singles remaining in buffer. * Set up loop counter as (- No. singles), and read in singles. EICount _ (EICount) - (7C); CALL [EISingles]; EISingles: GOTO [EIBufFull, R>=0], EICount _ (EICount) + 1; GOTO [EIAttnS, IOATTEN], T _ (EICount) + (6C); * Set up T for PStore1 INPUT [EITemp, EIData]; LU _ EITemp; RETURN, PStore1 [EIPtr, EITemp]; * We get here when IOATTEN is detected in EILoop. * Number of word left in buffer = 7 - EICount + 1 (CRC) + Excess count. EIAttn1: NOP; EIAttn: INPUT [EITemp, EStatus]; * Read Status T _ LDF[EITemp, 10,2]; * Isolate Excess Count EICount _ (EICount) XNOR (0C); * Complement EICount _ (EICount) + (11C); * Increment, add 8 EICount _ (EICount) + (T); * Add excess count EIAttn2: EITemp _ (RSH[EITemp, 10]); * Shift down status EITemp _ (EITemp) AND (EISMASK); * Mask out uninteresting status bits EITemp _ (EITemp)XNOR(ESIDON); * Post input done status * Store EECLOC. EIPost: T _ EELOC; CALL[ETaskRet], PStore1 [eMDS, EICount]; * Post status, disable interface (purge packet too), and TASK. * Post status in EITemp, disable value in EICount. EIPostA: CALL [EPost], EICount _ EDisableInput; * End of packet. GOTO [EIIDLE]; * Get here when an IOATTEN is detected during the EISingles loop. * Number of words left in buffer = 1 - EICount + 1 (CRC). EIAttnS: EICount _ (EICount) XNOR (0C); * Complement EICount _ (EICount) + (3C); * Increment, add 2 GOTO [EIAttn2], INPUT [EITemp, EStatus]; * We get here when the input buffer is exactly full. * First check if IOATTEN is high, indicating that the last word was the CRC. EIBufFull1: NOP; EIBufFull: SKIP[NOATTEN], EITemp _ 1C; GOTO [EIAttn2], INPUT [EITemp, EStatus]; *Last word input was CRC * Read one more word to see if the next is the CRC word (which we will discard). EICount _ 0C; * No words left in buffer CALL[ETaskRet], INPUT [EITemp, EIData]; * After wakeup, check IOATTEN. NOP; * Can't check for Attn here GOTO [EIAttn2, IOATTEN], INPUT [EITemp, EStatus]; * IOATTEN => Word was CRC EITemp _ 0C; GOTO [EIPOST], EITemp _ (EITemp) XNOR (ESIFUL); * Input buffer overrun, post status * Get here if input buffer has zero word count. Post. EICOuntZero: EITemp _ 0C; GOTO [EIPost], EITemp _ (EITemp) XNOR (ESCZER); * Input microcode is notified here by emulator SIO when AC0[16:17] = 3. * Manufacture "Abort" status and post. (Input hardware will be disabled again, which doesn't matter.) EIAbort: EITemp _ ECMDBITS, AT [EIAbortLoc]; GOTO [EIPostA], EITemp _ (EITemp) XNOR (ESABRT); * * * Alto-emulation OUTPUT MICROCODE * SetTask[EOTask]; * Output microcode is notified at EOStart by the emulator (at SIO). * Some initialization is done, and the TPC set up to EOIDLE, enable hardware and TASK. EOStart: CALL [EINIT], EOTemp _ EEnableOutput, AT [EOStartLoc]; * Idle state of the Ethernet output task EOIDLE: T _ ELLOC; PFetch1 [eMDS, EOTemp1]; * Fetch current load SKIP [R>=0], T _ (LSH[EOTemp1 , 1]) + 1; * Form new load, check if old overflowed GOTO [EOLDOV], EOTemp _ 0C; * Post Load overflow status EOTemp _ T; * Store updated load in ELLOC T _ ELLOC, TASK; PStore1 [eMDS, EOTemp]; * Store new load * Compute countdown interval * Get random number from "random" register (REFR register used). T _ (GETRSPEC[103]) xor (377c); * Save Stkp and EOTemp2 _ pERandomReg; * point to "random" register Stkp _ EOTemp2, EOTemp2 _ T, NoRegILockOK; T _ LDF [Stack, 4, 10]; * Get bits 4-13 Stkp _ EOTemp2; * and restore EOTemp1 _ (EOTemp1) AND (T); * Mask random number (EOTemp1 has Load mask) GOTO[EOSetup]; GOTO [EOSetup, ALU=0], EOTemp1 _ LSH[EOTemp1,1]; * Scale for 5.44 us ticks T _ (LDF[EOTemp1, 7, 2]) - 1; EOCount _ T, TASK; * Save high part (minus 1) (2 bits) EFlag _ 0C; * Clear Input under Output Flag EOTemp1 _ LDF[EOTemp1, 11, 7]; * EOTemp1 now has low 7 bits of random number * Before starting timer, check if input is set up. T _ EICLOC; PFetch1 [eMDS, EOTemp]; LU _ EOTemp; * Disable output. If the input word count is nonzero, enable the receiver while waiting to transmit. SKIP [ALU=0], EOTemp _ EDisableOutput; * ALU=0 => no input set up EOTemp _ (EOTemp) OR (EEnableInput); OUTPUT [EOTemp, EWriteState]; * Start simple timer with low 7 bits of random number. * Timer slot is EOTask. T _ ETimerMask; * Compute timer word T _ (CTASK) OR (T); EOLoadTimer: EOTemp1 _ (LSH[EOTemp1, 4]) or (T); LoadTimer[EOTemp1], return; *we don't need to have TPC correct here, since control *returns to this task via a Timer notify. * Timer has expired (notified here by task 16). Check if input (under output) came in. EOTimerDone: GOTO [EOMoreTime, R EVEN], LU _ EFlag, AT [EOTimerDoneLoc]; * Check if pkt came in (EFlag = 1) EOTemp _ EDisableOutput; * If so, abort output OUTPUT [EOTemp, EWriteState], call[ERet]; *the transmitter is disabled, so... breakpoint; *we will never get to this instruction. * Check if still more time to elapse before start of transmission (High part of random number >=0). EOMoreTime: EOTemp1 _ 177C; * Set up maximum timer value GOTO [EOLoadTimer, R>=0], EOCount _ (EOCount) - 1; * Enable output and shut off the receiver (in case it was turned on). EOBegin: EOTemp _ EDisableInput; EOTemp _ (EOTemp) OR (EEnableOutput); OUTPUT [EOTemp, EWriteState]; * Set up EOPtr and EOCount for single word transfers. EOSetup: T _ EOCLOC; CALL[EBSetup]; * Subroutine returns with: EOPtr = OPtr + OCount - 1, EOCount = -OCount * Check for zero count. GOTO [EOCountZero, R>=0], LU _ EOCount; * R<0 => count is zero * Compute how many singles before first quadword, and form loop counter in EOTemp1. * Address: x00 => no singles, loop count = -1 * Address: x01 => 3 singles, loop count = 0 * Address: x10 => 2 singles, loop count = 1 * Address: x11 => 1 singles, loop count = 2 T _ EOCount; T _ (EOPtr) + (T) + 1; * Form start address in T EOTemp1 _ (ZERO) - (T); * Complement, increment CALL [EOAlign], EOTemp1 _ (LDF[EOTemp1, 16, 2]) - 1; EOAlign: GOTO [EOQuad, R<0], EOTemp1 _ (EOTemp1) - 1; GOTO [EONoMore1, R>=0], T _ EOCount _ (EOCount) + 1; PFetch1 [EOPtr, EOTemp]; LU _ EOTemp; * Abort GOTO[ERet], OUTPUT [EOTemp, EOData]; * Now start quadword output. * Adjust EOPtr and EOCount for 4-word transfers. EOQuad: EOCount _ (EOCount) + (3C); CALL [EOLoop], EOPtr _ (EOPtr) - (6C); * Set return address for out loop * Output from the Main Memory Output Buffer to the Hardware Output Buffer. EOLoop: GOTO [EOQuadEmpty, R>=0], T _ EOCount _ (EOCount) + (4C); GOTO [EOAbort, IOATTEN]; RETURN, IOFetch4 [EOPtr, EOmData]; * Normal exit from Output Loop is here * 7 - XWCount = number of singles remaining * T is set up for next location. EOQuadEmpty: EOCount _ (EOCount) XOR (7C); CALL[EOSingles], EOCount _ (EOCount) - 1; EOSingles: GOTO [EONoMore, R<0], EOCount _ (EOCount) - 1; PFetch1 [EOPtr, EOTemp]; T _ (ZERO) + (T) + 1, EOTemp; * Abort GOTO[ERet], OUTPUT [EOTemp, EOData]; * We're done outputing words. Set output EOP. EONoMore1: NOP; EONoMore: EOTemp _ ESetOutputEOP; OUTPUT[EOTemp,EWriteState], call[ERet]; * Set OutputEOP * Should be woken up here after hardware's done sending packet or an error EOEND: INPUT [EOTemp, EStatus]; * Read Status EOEND1: LU _ (EOTemp) AND (ECOLLMASK); * Look at collision bit GOTO [EOCOLL, ALU#0], EOTemp_EDisableOutput; * ALU#0 => Collision, try again * If not collision, form status. Could be good packet or underrun (ODL). EOCount _ 0C; EOTemp _ RSH[EOTemp, 10]; * Shift down status EOTemp _ (EOTemp) AND (EOSMASK); * Remove uninteresting bits EOTemp _ (EOTemp) XNOR (ESODON); EOPost: T _ EELOC; CALL[ETaskRet], Pstore1 [eMDS, EOCount]; * Store end count CALL [EPost], EOCount _ EDisableOutput; breakpoint; * Shouldn't get here. * We arrive here after an IOATTEN is detected in the main loop, indicating an error condition. * IOATTEN will be true if a collision or underrun has occurred. EOAbort: GOTO [EOEND1], INPUT [EOTemp, EStatus]; * Now read status * Collision encountered, disable hardware to clear collision, enable and try again. EOCOLL: OUTPUT[EOTemp, EWriteState]; EOTemp _ EEnableOutput; OUTPUT[EOTemp, EWriteState], call[ERet]; GOTO [EOIDLE]; * Load overflow, post status (NOT [ESLOAD], EOTemp is 0) EOLDOV: GOTO [EOPost], EOTemp _ (EOTemp) XNOR (ESLOAD); * Output buffer count is zero. Post (NOT [ESCZR]). EOCountZero: EOTemp _ 0C; GOTO [EOPost], EOTemp _ (EOTemp) XNOR (ESCZER); * Task-independent Subroutines *These subroutines will work properly if *called from EITask or EOTask, due to the identical ordering of the registers in the two tasks. SetTask[AND[EITask,14]]; *Task 0 mod 4 of EI/EOTask block RV[ExTemp2,0 ]; RV[ExPtr,2]; RV[ExPtrHi,3]; RV[ExCount,4 ]; RV[ExTemp,5 ]; *SUBROUTINE [EPost]; * Posts the command completion, and starts an interrupt. * Expects post code and status in ExTemp. ExCount has disable code to be sent to State register. EPost: T _ EBLOC; PFetch1 [eMDS, ExTemp2]; * Fetch wakeup mask; T _ EPLOC; PStore1 [eMDS, ExTemp]; * Store ending status in EPLOC * Now wakeup driver. LoadPage[0]; OUTPUT [ExCount, EWriteState], gotop[DoRIntT]; * SUBROUTINE [EBSetup]. * Set up EPtr and ECount register. * On entry T has pointer to EICLOC or EOCLOC. * Subroutine returns with: * EPtr = Buffer Pointer + Count - 1 * ECount = - Count * The appropriate input or output pointer and count locations are used. * Subroutine knows that EIPLOC = EICLOC + 1, and EOPLOC = EOCLOC + 1. EBSetup: PFetch1 [eMDS, ExCount]; * Fetch count NOP; * So T can be written T _ (ZERO) + (T) + 1; * Point to ExPLOC PFetch1 [eMDS, ExPtr]; * Fetch pointer T _ (ExCount) - 1; ExPtr _ (ExPtr) + (T); * Ptr _ Ptr + count - 1 RETURN, ExCount _ (ZERO) - (T) - 1; * Count _ - Count * Subroutine [EINIT]. * Initialization subroutine. * Called by both input and output task. * ETemp contains the enable code to be used to enable the hardware. EINIT: T _ eMDShi; ExPtrHi _ T; * Set up high part of Buffer pointer GOTO[ERet], OUTPUT [ExTemp, EWriteState]; * These instructions used for Tasking after OUTPUT instructions. ERet: NOP; ETaskRet: EOTemp _ EOTemp, RETURN; *Interlock output operations :END[Ether];(1940)\3910f1 420f0