; ; File: Sonic.a ; ; Contains: Sonic-specific support routines ; ; Written by: Sean Findley ; ; Copyright: © 1990, 1992 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; <1> 10/6/92 GDW New location for ROMLink tool. ; <1> 6/12/92 RLM first checked in ; 02/07/92 jmp (jmp,H2/BG/SJF,Z3) Modifications for passing in SonicPtr instead ; of storing it. ; <1> 2/4/92 mal first checked in ; ——————————————————————————————————————————————————————————————————————————————————————— ; Pre-Horror ROM comments begin here. ; ——————————————————————————————————————————————————————————————————————————————————————— ; <2> 4/21/91 CCH Rolled in Sean Findley's changes. ; <1> 12/14/90 JK Added to builds ; ; To Do: ; ;•••••••••••••••• SONIC.a ; written by Sean J. Findley Jan. 1990 PRINT NOMDIR,NOMCALL MACRO Set10 SMOVE #(TalliesPerSec*10) MOD 65536,Timer0(A2) ; set lower 16 bits of counter SMOVE #(TalliesPerSec*10) DIV 65536,Timer1(A2) ; set upper 16 bits of counter MOVE.L #(1< SONIC registers. Set10Seconds PROC ENTRY WITH SONICRegs Set10 ; set timer for 10 seconds RTS ENDPROC ;•••••••••••••••• ; Restart SONIC. Assumes A2-> SONIC registers. RestartSONIC PROC EXPORT ; take SONIC out of reset WITH SONICRegs MOVEQ #0,D0 SMOVE D0,Command(A2) ; take out of reset @wait SMOVE Command(A2),D0 BTST #SoftReset,D0 BNE.S @wait ; wait for it Set10 ; set timer for 10 seconds MOVEQ #(1< SONIC registers ResetSONIC PROC EXPORT ; WITH SONICRegs MOVEQ #(1<SONIC chip ; Uses D1 SONICSync PROC ENTRY WITH SONICRegs SUB.L D1,D1 ; clear 32 bits BSET D0,D1 SMOVE D1,Command(A2) @waitloop SMOVE Command(A2),D1 BTST D0,D1 BNE.S @waitloop ; wait for command to complete RTS ENDWITH ENDPROC ;•••••••••••••••• ; Link to the end of recv descriptor list. ; A1->new entry, uses A0 LinkToRxList PROC ENTRY MOVE.L A2,-(SP) SUB.L D0,D0 SMOVE D0,Rxpkt.status(A1) ; setup to receive MOVEQ #(1<last descriptor ptr MOVEA.L (A0),A2 ; A2->last descriptor MOVE.L A1,(A0) ; update last pointer MOVE.L A1,D0 IF MMU THEN SUB.L RDAptr+llg,D0 ADD.L RDAptr+lph,D0 ; D0=physical address ENDIF SMOVE D0,Rxpkt.link(A2) ; link us to the end of the list LEA NextRxDesc,A0 TST.L (A0) ; were we out of buffers? BNE.S @done ; no MOVE.L A1,(A0) ; set next recv desc to poll @done MOVEA.L (SP)+,A2 RTS ENDPROC ;•••••••••••••••• SONIC Free Buffer ; void SONICFREEBUFF(desc) SONICFREEBUFF PROC EXPORT parms RECORD 4 RxDescriptor DS.L 1 ; ->to RDA descriptor to free ENDR WITH parms MOVEA.L IntDRtn,A0 ; A0->proc to disable interrupts JSR (A0) ; call it MOVEA.L RxDescriptor(SP),A1 ; A1->descriptor to be freed MOVE.L A2,-(SP) ; save reg MOVE.L D0,-(SP) ; save SR returned by proc ST Rxpkt.isFree(A1) ; mark this desc to be freed LEA FreeQ,A2 ; A2->to be freed queue CMPA.L (A2),A1 ; is this entry at the front? BNE.S @xit ; that's all we can do now ; free as many descriptors as possible starting at the front of the queue @FreeDesc TST.B Rxpkt.isFree(A1) ; can we free this desc? BEQ.S @done ; no, all done MOVE.L Rxpkt.nextRD(A1),(A2) ; set queue head to next desc if any BSR.S doFreeDesc ; give descriptor back to SONIC MOVE.L (A2),D0 ; any more to free? BEQ.S @done ; all done MOVEA.L D0,A1 ; A1->next descriptor to free BRA.S @FreeDesc ; try the next one @done TST.L (A2)+ ; see if queue has been emptied BNE.S @xit ; is not CLR.L (A2) ; clear tail pointer too @xit MOVEA.L IntERtn,A0 ; A0->proc to enable interrupts JSR (A0) ; do it ADDQ.W #4,SP ; strip SR parm MOVEA.L (SP)+,A2 ; get back reg RTS doFreeDesc SMOVE Rxpkt.in_use(A1),D0 ; see if SONIC still using desc TST.W D0 BEQ LinkToRxList ; put desc on receive list ; put desc on hold list ; If the descriptor might still be in use by the SONIC. This can happen if no other descriptors ; have been freed prior to this one OR we have not received any packets since freeing other ; descriptors prior to this one. LEA HoldDescHdr,A0 ; A0->hold list header MOVE.L (A0),Rxpkt.nextRD(A1) ; link prev to our next MOVE.L A1,(A0) ; put desc on head of list RTS ENDWITH ENDPROC ;•••••••••••••••• SONIC Interrupt Handler SONICInterrupt PROC ENTRY OurStack RECORD {A6Link} ; Sz EQU * ; A6Link DS.L 2 ; SONICPtr DS.L 1 ; ENDR ; ; void SONICInterrupt() WITH SONICRegs LINK A6,#OurStack.Sz ; MOVEM.L A2-A3, -(SP) ; save those we use @more MOVEA.L OurStack.SONICptr(A6),A2 ; A2->SONIC regs SMOVE Int_Status(A2),D0 ; get ISR from SONIC MOVE.W D0,D1 ; save it MOVE.W #(1< RTS IntTable ; Each interrupt is called with its respective status bit set in D2. It has the option of ; when to clear it in the ISR. DC.W RFO-IntTable ; receive FIFO overrun DC.W MP-IntTable ; MP tally counter rollover DC.W FAE-IntTable ; FAE tally counter rollover DC.W CRC-IntTable ; CRC tally counter rollover DC.W RBAE-IntTable ; receive buffer overflow DC.W RBE-IntTable ; receive buffers exhausted DC.W RDE-IntTable ; receive descriptors exhausted DC.W TC-IntTable ; timer complete DC.W TXER-IntTable ; transmit error DC.W TXDN-IntTable ; transmit complete DC.W PKTRX-IntTable ; packet received DC.W PINT-IntTable ; programmable interrupt DC.W LCD-IntTable ; load CAM done DC.W HBL-IntTable ; heartbeat lost DC.W Reserved-IntTable ; reserved DC.W Reserved-IntTable ; reserved ;________________ RFO ; receive FIFO overrun MACRxError MOVEA.L NetStatp, A0 ; Get pointer to array ADDQ.L #1, NetStats.MPerr(A0) ; Increment statistic RTS ;________________ MP ; MP tally counter rollover RTS ;________________ FAE ; FAE tally counter rollover RTS ;________________ CRC ; CRC tally counter rollover RTS ;________________ RBAE ; receive buffer overflow BRA.S MACRxError ; report receive error ;________________ RBE ; receive buffers exhausted BRA.S MACRxError ; report receive error ;________________ RDE ; receive descriptors exhausted RTS ;________________ TC ; timer complete ; This interrupt is used to fix a bug in the SONIC. If a packet has not been received in ; the last 10 seconds and there ARE receive descriptors available, AND the current RDA ; pointer is at the End Of List, reset the SONIC. SMOVE Silicon_Rev(A2),D0 ; check SONIC version CMPI.W #3,D0 BHI.S @done ; do nothing if rev 4 or later MOVE.L NextRxDesc,D0 ; see if descriptors available BEQ Set10Seconds ; none available, user is very slow to free buffers SMOVE Current_RDA(A2),D0 BTST #EOL_Bit,D0 BEQ Set10Seconds ; SONIC is not stuck SMOVE Int_Status(A2),D0 BTST #RecdPkt,D0 ; make sure a packet did'nt arrive BNE Set10Seconds ; process it if so ; SONIC is in a loop, reset it and get going again SMOVE Command(A2),-(SP) ; save current command status MOVE.L #1<retry count SMOVE Int_Status(A2),D0 ; check for errors ANDI.W #(1<xmitted TDA MOVE.L TRANParms,-(SP) ; pointer to xmit complete parms MOVEM.L A3,-(SP) ; pass ptr to xmit buffer descriptor MOVE.L TRANProc,A0 ; xmit completion routine address JSR (A0) ; call it ADDQ.W #8,SP ; strip trans parms SMOVE TxPkt.status(A3),D0 ; get back status ANDI.W #$F800, D0 MOVEA.L NetStatp, A0 ; Get pointer to array CMPI.W #$0800, D0 ; were there any? BLO.S @ok ; none BHI.S @many ; more than one ADDQ.L #1, NetStats.sCollFrame(A0) ; Increment statistic BRA.S @coll @many ADDQ.L #1, NetStats.mCollFrame(A0) ; inc multiple collision frame @coll ADDQ.L #1, NetStats.CollFrame(A0) ; inc collision count @ok SMOVE TxPkt.status(A3),D1 ; get back status CMPI.W #(1<this TDA link field SMOVE (A3),D0 ; fetch link field BTST #EOL_Bit,D0 BNE.S @done ; this is the end..my friend SMOVE Upper_TDA(A2),D1 SWAP D1 MOVE.W D0,D1 IF MMU THEN SUB.L TxTDAptr+lph,D1 ADD.L TxTDAptr+llg,D1 ; D1=logical address ENDIF MOVEA.L D1,A3 ; A3->next TDA in xmitted chain MOVEA.L IntERtn,A1 JSR (A1) ; reenable interrupts BRA @doTxComp @done MOVEA.L IntERtn,A1 JSR (A1) ; reenable interrupts ADDQ.W #4,SP ; strip old SR RTS ;________________ PKTRX ; packet received MOVE.L HoldDescHdr,D0 ; any descriptors held? BEQ.S checkDesc ; none to process ; Free available receive descriptors from the hold list. LEA HoldDescHdr,A3 ; A3->prev link MOVEA.L D0,A1 ; A1->held descriptor @chk MOVE.L Rxpkt.nextRD(A1),D1 ; save forward link SMOVE RxPkt.in_use(A1),D0 TST.W D0 ; desc still in use by the SONIC? BMI.S @nxt ; yes BSR LinkToRxList ; put on receive list MOVE.L D1,(A3) ; delete from hold list MOVEA.L A3,A1 ; keep prev link @nxt MOVEA.L A1,A3 ; A3->prev link MOVEA.L D1,A1 ; A1->next held desc maybe TST.L D1 BNE.S @chk ; more to do checkDesc MOVE.L NextRxDesc,D0 BEQ @done MOVEA.L D0,A3 ; A3->next active descriptor SMOVE RxPkt.status(A3),D0 TST.W D0 ; at logical end of list? BEQ @done MOVE.L A3,-(SP) ; pass buffer descriptor MOVE.L RECVparms,-(SP) ; parms ptr for user SMOVE RxPkt.pkt_ptr1(A3),D0 SWAP D0 SMOVE RxPkt.pkt_ptr0(A3),D1 MOVE.W D1,D0 IF MMU THEN SUB.L RBAptr+lph,D0 ADD.L RBAptr+llg,D0 ; D0=logical address ENDIF MOVE.L D0,-(SP) ; pointer to packet data SUB.L D0,D0 SMOVE RxPkt.byte_count(A3),D0 MOVE.L D0,-(SP) ; length of packet received SUB.L D0,D0 SMOVE RxPkt.status(A3),D0 MOVE.L D0,-(SP) ; receive status ; setup pointer to next descriptor SUB.L D1,D1 SMOVE Rxpkt.link(A3),D2 ; get lower part of link BTST #EOL_Bit,D2 ; end of list? BNE.S @FreeQueue ; yes SMOVE Upper_RDA(A2),D1 SWAP D1 ; setup upper part of address MOVE.W D2,D1 ; setup lower part of address IF MMU THEN SUB.L RDAptr+lph,D1 ADD.L RDAptr+llg,D1 ; D1=logical address ENDIF @FreeQueue ; place descriptor on to-be-freed queue LEA FreeQ,A1 ; A1->to be freed queue CLR.L Rxpkt.nextRD(A3) ; clear this desc link field CLR.B Rxpkt.isFree(A3) ; mark as not freed yet TST.L (A1)+ ; check queue head BNE.S @notEmpty ; has entries on it MOVE.L A3,(A1) ; put this desc on the end of queue MOVE.L A3,-(A1) ; and on the front as well BRA.S @doneQueueing ; that's it @notEmpty MOVEA.L (A1),A0 ; A0->tail MOVE.L A3,Rxpkt.nextRD(A0) ; link tail to this desc MOVE.L A3,(A1) ; put this desc on the end of queue @doneQueueing MOVEA.L D1,A3 ; A3->next descriptor maybe LEA NextRxDesc,A0 MOVE.L A3,(A0) ; update for next receive @doRxCall MOVEA.L RECVProc,A0 ; addr of receive routine JSR (A0) ; call it MOVE.L (SP)+,D1 ; get back status LEA 16(SP),SP ; strip other parms MOVEA.L NetStatp, A0 ; Get pointer to array MOVE.W #(1<SONIC registers LEA RECVProc,A0 MOVE.L RECVRtn(A6),(A0) LEA RECVParms,A0 MOVE.L RECVPrms(A6),(A0) LEA TRANProc,A0 MOVE.L TRANRtn(A6),(A0) LEA TRANParms,A0 MOVE.L TRANPrms(A6),(A0) LEA IntDRtn,A0 MOVE.L IntDisable(A6),(A0) LEA IntERtn,A0 MOVE.L IntEnable(A6),(A0) LEA NetStatp,A0 MOVE.L NetStatArray(A6),(A0) SUB.L D0,D0 ; clear 32 bits SMOVE D0,Int_Mask(A2) ; disable SONIC interrupts BSET #SoftReset,D0 ; do a software reset on SONIC SMOVE D0,Command(A2) PEA SONICInterrupt ; addr of our interrupt handler MOVEA.L IntInstall(A6),A0 ; addr of installation proc JSR (A0) ; install interrupt handler ADDQ.W #4,SP ; setup SONIC chip configuration MOVE.L DataConfig(A6),D0 SMOVE D0,Data_Config(A2) ; setup data configuration ; setup reception control SUB.L D0,D0 ; clear 32 bits BSET #RecvErrors,D0 ; receive CRC errors SMOVE Silicon_Rev(A2),D1 CMPI.W #3,D1 BLS.S @setBrd ; no runts for rev 3 or earlier BSET #RecvRunts,D0 ; receive runt packets @setBrd BSET #RecvBroadCast,D0 ; receive broadcasts SMOVE D0,Recv_Control(A2) ; Ensure that descriptors are in the same 64k page MOVE.L NumRxBuffs(A6),D0 ; calculate control memory size MULU #Rxpkt.RxRDAsize,D0 ; memory for recv descriptors ADDI.L #TxTDAsize * Max_Tx_Packets + RRArecSz + CAMDescSz,D0 ; xmit descriptors, RRA and CAM ADD.L MemStart(A6),D0 ; calc ending address SUBQ.L #1,D0 ; make it inclusive CLR.W D0 ; trash lower 16 bits MOVE.L MemStart(A6),D1 CLR.W D1 ; get start address page CMP.L D1,D0 ; see if boundry crossed BEQ.S @AllocateMem ; proceed if not ADDI.L #$10000,D1 ; adjust to 64k boundry SUB.L MemStart(A6),D1 ; calc adjustment amount ADD.L D1,MemStart(A6) ; update pointer SUB.L D1,MemSize(A6) ; update size @AllocateMem ; get memory for TDA MOVE.L #TxTDAsize * Max_Tx_Packets,D0 ; mem for TDA (up to 16 packets) LEA FreeTxTDA,A1 MOVE.L MemStart(A6),(A1) ; save free txtda ptr IF MMU THEN LEA TxTDAPtr,A1 BSR @SetAddresses ; set current phys/log addrs ENDIF MOVEA.L MemStart(A6),A1 ; A1->1st TDA MOVE.L A1,D1 IF MMU THEN SUB.L TxTDAPtr+llg,D1 ADD.L TxTDAPtr+lph,D1 ; D1=physical address of TDA ENDIF SWAP D1 SMOVE D1,Upper_TDA(A2) ; set upper TDA ADD.L D0,MemStart(A6) ; update pointer SUB.L D0,MemSize(A6) ; update size MOVEQ #Max_Tx_Packets-2,D1 ; link together all free TDAs @linkTDA PEA TxTDAsize(A1) ; get link to next TDA MOVE.L (SP),nextTD(A1) ; put in link field of TDA MOVEA.L (SP)+,A1 ; point at next one DBRA D1,@linkTDA ; do them all (last link = nil) ; get memory for RRA MOVEQ #RRArecSz+CAMDescSz,D0 ; mem for 1 RRA and 1 CAM descriptor LEA RRAptr,A1 MOVE.L MemStart(A6),(A1) ; save RRA ptr IF MMU THEN BSR @SetAddresses ; set log/phys addresses ENDIF MOVE.L MemStart(A6),D1 IF MMU THEN SUB.L RRAptr+llg,D1 ADD.L RRAptr+lph,D1 ; D1=physical address of RRA ENDIF SWAP D1 SMOVE D1,Upper_RRA(A2) ; set upper 16 bits of RRA addr ADD.L D0,MemStart(A6) ; update pointer SUB.L D0,MemSize(A6) ; update size ; get memory for RBA MOVE.L MemSize(A6),D0 DIVU #Max_Pkt_Size+RxRDAsize,D0 EXT.L D0 CMP.L NumRxBuffs(A6),D0 ; more than requested? BLS.S @usebuffs ; use number if less MOVE.L NumRxBuffs(A6),D0 @usebuffs LEA RxDescs,A1 MOVE.L D0,(A1) ; save buffer/descriptor count ; get memory for RDA LEA RDAptr,A1 MOVE.L MemStart(A6),(A1) ; set RD list pointer IF MMU THEN BSR @SetAddresses ; set log/phys addresses ENDIF LEA NextRxDesc,A1 MOVE.L MemStart(A6),(A1) ; set next poll address too MOVE.L RxDescs,D0 ; D0=# descriptors MOVE.L D0,D1 MULU #Rxpkt.RxRDAsize,D1 ; get actual allocated descriptor size ADD.L D1,MemStart(A6) SUB.L D1,MemSize(A6) SUBQ.L #1,D0 ; convert desc count to base zero MOVEA.L RDAptr,A0 ; A0->start of RDA @setRD TST.L D0 ; on last one? BNE.S @notLast SUB.L D1,D1 ; nil pointer BSET #EOL_Bit,D1 BRA.S @setLink ; link last to first @notLast PEA RxRDAsize(A0) MOVE.L (SP)+,D1 ; link to next IF MMU THEN SUB.L RDAptr+llg,D1 ADD.L RDAptr+lph,D1 ; D1=physical address ENDIF @setLink SMOVE D1,RxPkt.link(A0) ; set link MOVEQ #-1,D1 SMOVE D1,RxPkt.in_use(A0) ; mark descriptor as in use by SONIC LEA RxRDASize(A0),A0 DBRA D0,@setRD PEA -RxRDASize(A0) LEA LastDesc,A1 MOVE.L (SP)+,(A1) ; save ptr to last one in list ; set pointer to buffer area MOVE.L RxDescs,D0 MULU #Max_Pkt_Size,D0 ; get actual memory used for buffers LEA RBAptr,A1 MOVE.L MemStart(A6),(A1) ; save ptr to buffer area IF MMU THEN BSR @SetAddresses ; set log/phys addresses ENDIF ADD.L D0,MemStart(A6) ; update pointer SUB.L D0,MemSize(A6) ; update size ; init RRA descriptors MOVEA.L RRAPtr,A0 ; get pointer to RRA LEA CAMDescSz(A0),A1 ; add in enough for CAM operations PEA (A1) ; save for later MOVE.L A1,D0 IF MMU THEN SUB.L RRAptr+llg,D0 ADD.L RRAptr+lph,D0 ; A1=physical address ENDIF SMOVE D0,RRA_Read(A2) ; set up RRP SMOVE D0,RRA_Start(A2) ; save lower 16 bit of RRA addr ADDI.W #RRArecSz,D0 ; ONE descriptor SMOVE D0,RRA_End(A2) ; and set the end of RRA too SUB.L D0,D0 ; clear 32 bits SMOVE D0,RRA_Write(A2) ; force wrap-around RRA MOVE.L RBAptr,D0 IF MMU THEN SUB.L RBAptr+llg,D0 ADD.L RBAptr+lph,D0 ; D0=physical address ENDIF MOVEA.L (SP)+,A1 ; get ptr to descriptor SMOVE D0,buff_ptr0(A1) ; setup resource descriptor SWAP D0 SMOVE D0,buff_ptr1(A1) MOVE.L RxDescs,D1 MULU #Max_Pkt_Size/2,D1 ; D1=word count for buffer area SMOVE D1,buff_wc0(A1) ; setup buffer size SWAP D1 SMOVE D1,buff_wc1(A1) MOVEQ #-1,D0 ; reset all bits in ISR SMOVE D0,Int_Status(A2) BSR Set10Seconds ; init timer value SUB.L D0,D0 SMOVE D0,Command(A2) ; start chip running @wait SMOVE Command(A2),D0 ; wait for SONIC to start up BTST #SoftReset,D0 BNE.S @wait MOVEQ #ReadRRA,D0 BSR SONICSync ; read RRA descriptor @loadRD MOVE.L RDAptr,D0 IF MMU THEN SUB.L RDAptr+llg,D0 ADD.L RDAptr+lph,D0 ; D0= physical address ENDIF SMOVE D0,Current_RDA(A2) ; setup RDA pointers SWAP D0 SMOVE D0,Upper_RDA(A2) BSR RestartSONIC ; enable reception and start timer MOVE.L #OurIntsMask,D0 ; setup for our interrupts SMOVE D0,Int_Mask(A2) ; enable SONIC interrupts MOVE.L MemSize(A6),D0 ; return # unused bytes in usage area @IError MOVEA.L (SP)+,A2 UNLK A6 RTS IF MMU THEN @SetAddresses ; set lowest logical/physical addresses for pointer in A1 MOVE.L D0,-(SP) ; save mem size, ptr MOVE.L A1,-(SP) MOVE.L D0,-(SP) ; pass logical size MOVE.L MemStart(A6),-(SP) ; pass logical address MOVEA.L TransAddr(A6),A0 JSR (A0) ; D0=lowest physical addr ADDQ.W #8,SP MOVEA.L (SP)+,A1 MOVE.L D0,lph(A1) ; set lowest physical address MOVE.L MemStart(A6),llg(A1) ; set lowest logical address MOVE.L (SP)+,D0 ; get back mem size RTS ENDIF ENDWITH ENDPROC ;•••••••••••••••• SONIC ShutDown ; void SONICHALT() SONICHALT PROC EXPORT OurStack RECORD {A6Link} ; thru next Sz EQU * A6Link DS.L 2 SONICPtr DS.L 1 ENDR WITH SONICRegs LINK A6,#OurStack.Sz MOVE.L A2,-(SP) MOVEA.L IntDRtn,A0 ; A0->proc to disable interrupts JSR (A0) ; call it MOVE.L D0,-(SP) ; save SR returned MOVEA.L OurStack.SONICptr(A6),A2 ; A2->sonic regs BSR ResetSONIC ; disable packet reception SUB.L D0,D0 SMOVE D0,Int_Mask(A2) ; disable SONIC interrupts SMOVE D0,CAM_Enable(A2) ; wipe out the CAM MOVEA.L IntERtn,A0 ; JSR (A0) ; reenable interrupts ADDQ.W #4,SP ; MOVEA.L (SP)+,A2 UNLK A6 ; RTS ; all done ENDPROC ;•••••••••••••••• SONIC Transmit ; void SONICXMIT(wdsPtr) parms RECORD {A6Link} LocalSize EQU * SaveA2 DS.L 1 A6Link DS.L 2 SONICPtr DS.L 1 ; wdsPtr DS.L 1 ; write data structure pointer ENDR PROC EXPORT SONICXMIT WITH SONICRegs,TxPkt,parms noTDA MOVEA.L IntERtn,A0 MOVE.L D0,-(SP) ; save SR returned by proc JSR (A0) ; reenable interrupts MOVEQ #-1,D0 UNLK A6 RTS SONICXMIT LINK A6,#LocalSize ; make some room MOVEA.L IntDRtn,A0 ; A0->proc to disable interrupts JSR (A0) ; call it MOVE.L FreeTxTDA,D1 BEQ.S noTDA ; If no TDA available ; it's always faster to not branch MOVE.L A2,SaveA2(A6) ; save regs MOVE.L D0,-(SP) ; save SR returned by IntDRtn proc LEA FreeTxTDA,A0 MOVEA.L (A0),A1 ; A1->new TDA MOVE.L nextTD(A1),(A0) ; unlink from free list EXG A1,A2 ; save TDA ptr MOVEA.L IntERtn,A0 JSR (A0) ; reenable interrupts maybe EXG A1,A2 ; restore TDA ptr MOVEA.L wdsPtr(A6),A2 ; A2->wds, A1->this TDA SUB.L D0,D0 ; clear 32 bits SMOVE D0,frag_count(A1) ; init Tx fields SMOVE D0,pkt_size(A1) SMOVE D0,status(A1) SMOVE D0,config(A1) ; start timer after SFD, tx FCS, time ; excessive deferrals LEA frag_start(A1),A0 ; A0->fragment fields @fragloop SUB.L D0,D0 ; clear 32 bits MOVE.W (A2)+,D0 ; check wds segment len BEQ.S @chkpkt ; see if everything is ok IF SONIC32 THEN ADDQ.L #1,frag_count(A1) ADD.L D0,pkt_size(A1) ; and total packet size ELSE ADDQ.W #1,frag_count(A1) ; bump count ADD.W D0,pkt_size(A1) ; and total packet size ENDIF MOVE.L D0,-(SP) ; save frag size from wds MOVE.L (A2)+,D0 ; get wds ptr SMOVE D0,(A0)+ ; set lower 16 of frag_ptr SWAP D0 SMOVE D0,(A0)+ ; set upper 16 MOVE.L (SP)+,D0 SMOVE D0,(A0)+ ; set frag size in TDA BRA.S @fragloop ; keep going @chkpkt SMOVE pkt_size(A1),D0 EXT.L D0 ; make a 32 bit quantity SUBI.L #Min_Pkt_Size,D0 ; calc pad size BHS.S @sendit ; go do it if long enough NEG.L D0 ; convert to positive number IF SONIC32 THEN ADDQ.L #1,frag_count(A1) ; add another fragment for padding ADD.L D0,pkt_size(A1) ; bump total too ELSE ADDQ.W #1,frag_count(A1) ; add another fragment for padding ADD.W D0,pkt_size(A1) ; bump total too ENDIF MOVE.L RBAptr,D1 ; start of buffers IF MMU THEN SUB.L RBAptr+llg,D1 ADD.L RBAptr+lph,D1 ; convert to physical address ENDIF SMOVE D1,(A0)+ ; set lower 16 of frag_ptr SWAP D1 SMOVE D1,(A0)+ ; set upper 16 SMOVE D0,(A0)+ ; set frag size in TDA @sendit MOVEQ #(1<proc to disable interrupts JSR (A0) ; call it (SR already on stack from above) EXG A1,A2 ; restore curr frag ptr in A1 MOVEA.L SONICptr(A6),A2 ; A2->sonic regs MOVE.L NextTxTDA,D0 ; check if we already have a chain BNE.S @haveChain LEA NextTxTDA,A0 ; start a new chain MOVE.L A1,(A0) BRA.S @doxmit ; transmit new chain maybe @haveChain MOVEA.L D0,A0 ; A0->start of existing chain SMOVE Upper_TDA(A2),D1 SWAP D1 ; get upper TDA address @chkTDA SMOVE frag_count(A0),D0 ; D0=fragment count for this packet FragMul D0 ; calc an offset to the link field LEA frag_start(A0),A0 ; A0->start of fragments for this pkt LEA (A0,D0.W),A0 ; A0->link field for this pkt SMOVE (A0),D0 BTST #EOL_Bit,D0 ; at the last pkt in chain? BNE.S @setlink ; append to end if so MOVE.W D0,D1 ; D1=physical address of next TDA IF MMU THEN SUB.L TxTDAPtr+lph,D1 ADD.L TxTDAPtr+llg,D1 ; D1=logical addr (upper half) ENDIF MOVEA.L D1,A0 ; A0->next TDA BRA.S @chkTDA ; keep searching for last TDA @setlink MOVE.L A1,D0 ; D0=ptr to new TDA IF MMU THEN SUB.L TxTDAPtr+llg,D0 ADD.L TxTDAPtr+lph,D0 ; D0=physical address ENDIF SMOVE D0,(A0) ; update link of last TDA @doxmit LEA TxTDAptr,A0 TST.L (A0) BNE.S @done ; transmit already in progress LEA NextTxTDA,A1 MOVE.L (A1),(A0) ; start transmitting current chain MOVEQ #0, D0 MOVE.L D0, (A1) ; empty chain MOVE.L (A0),D0 IF MMU THEN SUB.L TxTDAPtr+llg,D0 ADD.L TxTDAPtr+lph,D0 ; D0=physical address of curr TDA ENDIF SMOVE D0,Current_TDA(A2) ; set current SONIC TDA ptr MOVEQ #(1< CAMentry DS.W 3 ; 48 bit address to set in CAM Loadit DS.L 1 ; true if adding CAM entry ENDR WITH parms,SONICRegs,CAMDesc LINK A6,#LocalSize ; save A6 MOVEM.L A2/D3/D4,-(SP) ; save reg MOVEA.L IntDRtn,A0 ; A0->proc to disable interrupts JSR (A0) ; call it MOVE.L D0,-(SP) ; save SR returned MOVEA.L SONICptr(A6),A2 ; A2->SONIC MOVE.L CAMentry(A6),D1 ; D1=upper/middle of 48 bit addr MOVE.W CAMentry+4(A6),D2 ; D2.W=lower " TST.L Loadit(A6) ; doing a load CAM? BEQ.S @doClear ; no ; search for an empty CAM entry @wait1 SMOVE Command(A2),D0 EORI.W #(1<RRA, D3=index to CAM cell ROR.W #8,D2 ; swap bytes in word SMOVE D2,Port0(A0) ; set lower 16 bits ROR.W #8,D1 ; swap bytes in word SMOVE D1,Port1(A0) ; set middle " SWAP D1 ROR.W #8,D1 ; swap bytes in word SMOVE D1,Port2(A0) ; set upper " SMOVE CAM_Enable(A2),D0 ; get current CAM map BSET D3,D0 ; set the new one SMOVE D0,enable(A0) ; set bit map for CAM BSR.S LoadCAMcells ; load descriptor and CAM enable reg MOVE.L D3,D0 ; set function result BRA.S @Xit @doClear BSR ResetSONIC ; let transmits/receives complete MOVEQ #15,D3 SMOVE CAM_Enable(A2),D0 ; get current CAM map @compCAM ASL.W #1,D0 ; check cell DBCS D3,@compCAM ; until we have an active one BCS.S @compareit @notfound BSR RestartSONIC ; get SONIC going again MOVE.W D3,D0 EXT.L D0 ; D0.L=-1 BRA.S @Xit ; return error (did'nt find entry) @more SUBQ #1,D3 ; keep index straight BMI.S @notfound BRA.S @compCAM @compareit ; compare CAM to addr in D1/D2 SMOVE D3,CAM_EntryPtr(A2) ; select CAM cell to read SMOVE CAM_Port1(A2),D4 ; get middle 16 bits ROR.W #8,D4 ; normalize it MOVE.W D4,-(SP) ; save it SMOVE CAM_Port0(A2),D4 ; get upper 16 bits ROR.W #8,D4 ; normalize it MOVE.W D4,-(SP) ; save it CMP.L (SP)+,D1 ; same as the one we want? BNE.S @more ; no, keep checking SMOVE CAM_Port2(A2),D4 ; get lower 16 ROR.W #8,D4 ; normalize it CMP.W D4,D2 ; same? BNE.S @more ; look again if not MOVEA.L RRAptr,A0 ; A0->CAM descriptor SUB.L D2,D2 SMOVE D2,Port0(A0) ; put zero entry in cell SMOVE D2,Port1(A0) SMOVE D2,Port2(A0) SMOVE CAM_Enable(A2),D0 ; get current CAM map BCLR D3,D0 ; clear the one to delete SMOVE D0,enable(A0) ; and do it BSR RestartSONIC ; get SONIC going again BSR.S LoadCAMcells ; clear the entry now SUB.L D0,D0 ; clear 32 bits @Xit MOVEA.L IntERtn,A0 ; JSR (A0) ; reenable interrupts ADDQ.W #4,SP ; MOVEM.L (SP)+,A2/D3/D4 UNLK A6 RTS LoadCAMcells ; setup CAM cells and enable register ; D3=base zero index of CAM cell, A0->CAM load descriptor SMOVE D3,Entry_ptr(A0) ; set base zero index to CAM cell MOVE.L A0,D0 IF MMU THEN SUB.L RRAptr+llg,D0 ADD.L RRAptr+lph,D0 ; D0=physical address ENDIF SMOVE D0,CAM_DescPtr(A2) ; set current descriptor ptr MOVEQ #1, D0 SMOVE D0,CAM_Count(A2) ; set one cell only MOVEQ #LoadCAM,D0 BRA SONICSync ; do it now & return ENDWITH ENDPROC END