; PupAlEtha.asm - Pup Alto Ethernet driver (assembly-language code) ; Contains initial portion of Ethernet interrupt handler, plus ; StartEther procedure. These are hand-coded to minimize ; turnaround time for the half-duplex Alto Ethernet interface. ; Copyright Xerox Corporation 1979, 1980 ; Last modified March 9, 1980 5:15 PM by Taft .ent etherProlog .ent StartEther .ent EtherPupFilter .bextz EtherInterrupt .bext pbiFreeQ .bext lenEtherPacket .srel etherProlog: .etherProlog StartEther: .StartEther EtherPupFilter: .EtherPupFilter .zrel EtherInterruptEntry: .EtherInterruptEntry .nrel ; *** constants duplicated in PupAlEth.decl *** packetLength = 6 ; offset of PBI.packetLength etherPacket = 7 ; PBI offset of first word of packet etherType = 8. ; offset of EtherPBI.type pupLength = 9. ; offset of PBI.pup.length typePup = 1000 ; ethernet type meaning "I'm a Pup" ; *** NDB offsets - see EtherNDB structure in PupAlEth.decl *** lastEPLoc = 14. ; EPLoc for most recent interrupt lastEELoc = 15. ; EELoc for most recent interrupt lastEIB = 16. ; -> input PBI for most recent interrupt eIB = 17. ; -> input PBI or 0 if none eOB = 18. ; -> output PBI or 0 if none eState = 19. ; -1: xmting, 0: off, 1: rcving ePLoc = 20. ; -> Post location eBLoc = 21. ; -> Interrupt bit mask location eELoc = 22. ; -> EOT count location, Posted eLLoc = 23. ; -> Load location (mask) eICLoc = 24. ; -> Input count location eIPLoc = 25. ; -> Input pointer location eOCLoc = 26. ; -> Output count location eOPLoc = 27. ; -> Output pointer location eHLoc = 28. ; -> Host Address location outCmd = 29. ; SIO bits to start transmitter inCmd = 30. ; SIO bits to start receiver resetCmd = 31. ; SIO bits to reset hardware & firmware ; prolog in 32.-38. save3 = 38. ; ac3 temp savedID = 39. ; entry to normal bcpl interrupt handler stack = 40. ; ac2 temp ; The state of the Ethernet interface is represented as follows: ; if eState is zero, the interface is idle; if nonzero, active. ; 1 means the receiver has been started; -1 means the transmitter. ; If eIB is nonzero, an input buffer is set up. ; If eOB is nonzero, an output buffer is set up. ; If an input or output buffer is not set up, then eICLoc or eOCLoc ; (respectively) must be zero. ; When the interface is started, ePLoc is set to zero. Hence ; eState nonzero and ePLoc zero means that the interface is ; presently turned on and has not yet posted. ; When the receiver is started, the first word of the input packet ; buffer is zeroed. The idea is that if a packet starts to arrive, ; it will set this word nonzero, thereby indicating that receipt ; of a packet is in progress. ; Ethernet interrupt prolog -- copied into each NDB ; Must match prolog field in EtherNDB .etherProlog: ; EtherNDB.asmProlog: sta 3 .+6 ; save3 lda 3 .+4 ; ndb jmp @EtherInterruptEntry ; EtherNDB.bcplProlog: lda 0 .+2 ; ndb jmp @EtherInterrupt ; The NDB for the interrupting interface is in AC3. ; The former contents of ac3 is in the ndb. ; After saving all necessary state for the current Ethernet post ; condition, we attempt to get the receiver turned on as ; quickly as possible, then branch to the normal (BCPL) interrupt ; handler for more leisurely cleanup. ; ** Must be careful not to clobber the carry bit ** .EtherInterruptEntry: ; AC3/ ndb sta 0 save0 lda 0 @ePLoc 3 ; Ethernet post word sta 0 lastEPLoc 3 ; Save for later snz 0 0 ; Has Ethernet posted? jmp ethin6 ; No, manually-initiated interrupt sta 2 save2 lda 2 c177400 ; Post code = 0 (input done)? and 2 0 szr jmp ethin1 ; No, can re-use input buffer lda 2 eIB 3 ; Yes, get eIB just completed sta 2 lastEIB 3 ; Save it away lda 2 @eELoc 3 ; Save ending word count also sta 2 lastEELoc 3 jmp ethin2 ethin1: lda 2 eIB 3 ; Get current input pbi sz 2 2 ; Skip if none set up jmp ethin3 ; Have one, go use buffer ethin2: lda 2 @.pbiFreeQ ; None yet, get pbiFreeQ header lda 0 0 2 ; Get pointer to head pbi snz 0 0 ; Skip if have one jmp ethin4 ; No pbi's available sta 0 eIB 3 ; Got one, store pointer lda 0 @eIB 3 ; Get successor sta 0 0 2 ; Put at head of pbiFreeQ lda 2 eIB 3 lda 0 .etherPacket ; Offset of packet in pbi add 2 0 ; Make pointer to packet sta 0 @eIPLoc 3 ; Store for interface lda 0 @.lenEtherPacket ; Maximum packet length sta 0 @eICLoc 3 ; AC2/ pbi to use for input. ethin3: subc 0 0 ; won't clobber carry bit sta 0 etherPacket 2 ; Zero first word of packet sta 0 @ePLoc 3 ; Zero post location lda 0 inCmd 3 sio ; Start up input lda 0 c1 ; Set state to "Receiver on" ; Now continue on to the normal BCPL interrupt entry sequence. ; Note that this piece of code is invisible to EtherInterrupt, ; which returns directly to the interrupt handler. ethin5: sta 0 eState 3 ; Update software state lda 2 save2 ethin6: lda 0 savedID 3 ; Interrupt handler entry address sta 0 ethin7 ; ----+ lda 0 save0 ; | lda 3 save3 3 ; | jmp @.+1 ; | ethin7: 0 ; <---+ ; Here if no input pbi and pbiFreeQ empty. AC0/ 0 ethin4: sta 0 @ePLoc 3 ; Zero post location sta 0 eIB 3 ; Note no buffer set up now sta 0 @eICLoc 3 ; Prevent input-under-output jmp ethin5 save0: 0 save2: 0 ; StartEther(ndb) ; Called from interrupt level after processing of a packet is ; complete and new input and/or output packets have been set up. ; This procedure ensures that the transmitter is turned ; on if there is a pbi to be sent and there is not already a packet ; pouring into the receiver. If there is no output, it turns on ; the receiver if necessary. It is assumed that the interface is ; already on if eState is nonzero. .StartEther: sta 3 1 2 ; Return address mov 0 3 ; ndb sta 2 stack 3 ; Need two index registers lda 1 eState 3 ; Get current software state movl# 1 1 szc ; Already transmitting? jmp return ; Yes, nothing to do lda 0 resetCmd 3 ; Constant used below lda 2 eOB 3 ; Is there an output pbi set up? snz 2 2 jmp start4 ; No, go check input mov 1 2 snr ; Yes, is receiver already on? jmp start2 ; No, go start transmitter ; We want to transmit but the receiver is now on. ; Check whether a packet is now being received, and do not ; disturb the interface if so. lda 1 @eBLoc 3 ; Save mask for later restoration lda 2 @eIPLoc 3 ; Check first word of packet lda 2 0 2 sz 2 2 ; Packet now being received? jmp return ; Yes, defer output ; Reset interface and start transmitter. ; We must be careful not to cause an interrupt. ; ac0/ etherResetCommand, ac1/ etherMask, ac2/ 0 sta 2 @eBLoc 3 ; Zero interrupt bit mask location sio ; Reset the interface sta 1 @eBLoc 3 ; Put back regular mask start2: sta 2 @ePLoc 3 ; Zero post location sta 2 @eLLoc 3 ; Initialize load to zero lda 0 outCmd 3 sio ; Start transmitter mkminusone 0 0 ; Note that we are now transmitting jmp start5 ; Here if there is no output to be sent. ; See whether we need to turn on the receiver. ; ac1/ software state start4: lda 2 eIB 3 ; Get current input buffer snz 1 1 ; Is interface already on? snz 2 2 ; No, is there an input buffer? jmp return ; Already on or no buffer, done ; The interface is now off and an input buffer is set up. ; Start the receiver. ac1/ 0, ac2/ pbi sta 1 etherPacket 2 ; Zero first word of packet sta 1 @ePLoc 3 ; Zero post location lda 0 inCmd 3 sio ; Start up input mkone 0 0 ; Note that we are now receiving start5: sta 0 eState 3 return: lda 2 stack 3 lda 3 1 2 jmp 1 3 .pbiFreeQ: pbiFreeQ .etherPacket: etherPacket .lenEtherPacket: lenEtherPacket c1: 1 c177400: 177400 ; EtherPupFilter(pbi) ; hand-coded to avoid procedure call overhead at interrupt level ; = (pbi>>PBI.pup.length+5) rshift 1 eq pbi>>PBI.packetLength & ; pbi>>EtherPBI.type eq typePup .EtherPupFilter: sta 3 1 2 mov 0 3 lda 0 pupLength 3 lda 1 c5 addzr 1 0 lda 1 packetLength 3 se 0 1 jmp rfalse lda 0 etherType 3 lda 1 .typePup se 0 1 rfalse: mkzero 0 0 skp mkminusone 0 0 lda 3 1 2 jmp 1 3 c5: 5 .typePup: typePup .end