sys7.1-doc-wip/DeclData/DeclNet/Sonic/Sonic.a

1378 lines
41 KiB
Plaintext

;
; 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
; <P2> 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<<TimerElapsed),D0
SMOVE D0,Int_Status(A2) ; make sure no pending timer interrupt
ENDM
INCLUDE 'SonicEqu.a'
IF Txpkt.frag_esize = 6 THEN
MACRO
&L FragMul &DReg
&L ADD.W &DReg, &DReg
MOVE.W &DReg, -(A7)
ADD.W &DReg, &DReg
ADD.W (A7)+, &DReg
ENDM
ELSEIF Txpkt.frag_esize = 12 THEN
MACRO
&L FragMul &DReg
&L ADD.W &DReg, &DReg
MOVE.W &DReg, -(A7)
ADD.W &DReg, &DReg
ADD.W (A7)+, &DReg
ADD.W &DReg, &DReg
ENDM
ELSE
AError 'Unexpected value for Txpkt.frag_esize'
ENDIF
SONICdata PROC
; For machines with MMUs present, pointers may have additional values associated with them.
; One is the lowest possible logical address the pointer may have. This value is used to
; calculate an offset that can be added to the second value which is the lowest possible
; physical address to quickly translate logical to physical addresses, and vice-versa.
ENTRY TxTDAptr,NextTxTDA,FreeTxTDA,RRAptr,RBAptr,RDAptr
ENTRY RECVParms,RECVProc,TRANParms,TRANProc,TxRetries,RxDescs
ENTRY NextRxDesc,LastDesc,HoldDescHdr,IntDRtn,IntERtn,NetStatp,FreeQ
TxTDAptr DC.L 0,0,0 ; pointer to current Tx descriptor area
NextTxTDA DC.L 0 ; pointer to next TDA to transmit
FreeTxTDA DC.L 0 ; pointer to available TDA
RRAptr DC.L 0,0,0 ; pointer to read resource area
RBAptr DC.L 0,0,0 ; pointer to read buffer area
RDAptr DC.L 0,0,0 ; pointer to read descriptor area
RECVParms DC.L 0 ; parm ptr to pass to user receive routine
RECVProc DC.L 0 ; addr of user receive routine
TRANParms DC.L 0 ; parm ptr to pass to xmit complete routine
TRANProc DC.L 0 ; address of xmit complete routine
TxRetries DC.L 0 ; retry counter for failed xmits
RxDescs DC.L 0 ; number of recv descriptors allocated
NextRxDesc DC.L 0 ; ptr to next recv descriptor to poll
LastDesc DC.L 0 ; ptr to last descriptor
HoldDescHdr DC.L 0 ; hdr to held descriptor list
IntDRtn DC.L 0 ; ptr to proc to disable interrupts
IntERtn DC.L 0 ; ptr to proc to enable interrupts
NetStatp DC.L 0 ; ptr to network stats. array
; We keep a queue to manage the freeing of receive descriptors. Since descriptors may be
; freed out of the received sequence order, we cannot permit the SONIC to use a given descriptor
; unless it is being freed in the same sequence as it was received. Thus as packets are
; received, their descriptors are placed onto a queue (we have a head & tail for fast queue
; insertions/deletions), and a "freed" flag is set to false. When the descriptor is freed,
; the flag is set to true, and if the descriptor is at the front of the queue it, and any others
; directly behind it that have been freed are given back to the SONIC. This will prevent a
; "buffer wrap-around".
FreeQ DC.L 0,0 ; head/tail pointer for queuing
ENDPROC
llg EQU 4 ; offset to lowest logical addr
lph EQU 8 ; offset to lowest physical addr
; Routines for initializing SONIC chip and reading/writing packets on Ethernet.
;••••••••••••••••
; Setup SONIC clock to go off in 10 seconds. Assumes A2-> 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 <T2>
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<<RxEnable)+(1<<StartTimer),D0
SMOVE D0,Command(A2) ; start receiving packets again
@waitmore
SMOVE Command(A2),D0
BTST #RxDisable,D0
BNE.S @waitmore ; wait for receiver to enable
RTS
ENDPROC
;••••••••••••••••
; Reset SONIC. Assumes A2-> SONIC registers
ResetSONIC PROC EXPORT ; <T2>
WITH SONICRegs
MOVEQ #(1<<RxDisable),D0
SMOVE D0,Command(A2) ; disable packet reception
@wait
SMOVE Command(A2),D0
BTST #RxEnable,D0
BNE.S @wait ; wait until it disables
@wait1
SMOVE Command(A2),D0
EORI.W #(1<<StartTimer)+(1<<RxDisable),D0
BNE.S @wait1 ; make sure no other commands active
BSET #SoftReset,D0
SMOVE D0,Command(A2) ; so we can look at CAM cells
RTS
ENDPROC
;••••••••••••••••
; Set a bit in command register and wait for it to clear.
; D0=bit # to set/poll.
; A2->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<<EOL_Bit), D0 ; mark us at the end
SMOVE D0,Rxpkt.link(A1) ; new last one
MOVEQ #-1,D0
SMOVE D0,Rxpkt.in_use(A1) ; mark entry as in use by SONIC
LEA LastDesc,A0 ; A0->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} ; <Z3>
Sz EQU * ; <Z3>
A6Link DS.L 2 ; <Z3>
SONICPtr DS.L 1 ; <Z3>
ENDR ; <Z3>
; void SONICInterrupt()
WITH SONICRegs
LINK A6,#OurStack.Sz ; <Z3>
MOVEM.L A2-A3, -(SP) ; save those we use
@more
MOVEA.L OurStack.SONICptr(A6),A2 ; A2->SONIC regs <Z3>
SMOVE Int_Status(A2),D0 ; get ISR from SONIC
MOVE.W D0,D1 ; save it
MOVE.W #(1<<RecdPkt),D2
AND.W D2,D0 ; a receive?
BEQ.S @chkXmit
SMOVE D2,Int_Status(A2) ; clear interrupt status
BSR PKTRX ; yes, process it right away
BRA.S @more
@chkXmit
MOVE.W #(1<<TransDone),D2
AND.W D2,D1 ; a xmit complete?
BEQ.S @chkOther
SMOVE D2,Int_Status(A2) ; clear interrupt status
BSR TXDN ; yes, do it right now
BRA.S @more
@chkOther
SMOVE Int_Status(A2),D0 ; get ISR from SONIC again
ANDI.W #OurIntsMask,D0 ; only the ones we want
BEQ.S @done ; finished
MOVEQ #15, D1 ; loop count
@chkBit
BTST.L D1, D0 ; check a bit
DBNE D1, @chkBit ; loop until a bit is on
SUB.L D2, D2 ; clear 32 bits
BSET D1, D2 ; set the bit that is on
ADD.W D1, D1 ; get table offset
LEA IntTable, A0
ADDA.W (A0, D1.W), A0 ; Get address of routine
SMOVE D2,Int_Status(A2) ; clear interrupt status
JSR (A0) ; and execute it
BRA.S @more ; look for more work to do
@done
MOVEM.L (SP)+, A2-A3
UNLK A6 ; <Z3>
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<<SoftReset,D0
SMOVE D0,Command(A2)
BSR RestartSONIC ; and get it going again
SMOVE (SP)+,Command(A2) ; continue with whatever we were doing
@done
RTS
;________________
TXER ; transmit error
RTS
;________________
TXDN ; transmit complete
LEA TxRetries,A0 ; A0->retry count
SMOVE Int_Status(A2),D0 ; check for errors
ANDI.W #(1<<TransError),D0 ; any abort errors?
BEQ.S @nextXmit ; no, see if more to xmit
SMOVE D0,Int_Status(A2) ; clear error status
ADDQ.L #1,(A0) ; inc retry count
CMPI.L #TxMaxRetries,(A0) ; past the treshhold?
BLO.S @nextXmit ; give up if so
SMOVE Current_TDA(A2),D0
BTST #EOL_Bit,D0 ; make sure we have a Tx descriptor
BNE.S @nextXmit ; give up if not
MOVEQ #(1<<TxEnable), D0
SMOVE D0,Command(A2) ; try retransmitting
RTS ; get out
@nextXmit
CLR.L (A0) ; reset retry count
MOVEA.L IntDRtn,A1
JSR (A1)
MOVE.L D0,-(SP) ; disable interrupts and save old SR
LEA TxTDAptr,A0
MOVE.L (A0),A3 ; save ptr to transmitted chain
LEA NextTxTDA,A1
MOVE.L (A1),(A0) ; set any pending chain to current
BEQ.S @callTxComp ; were'nt any pending
CLR.L (A1) ; pending chain now being xmitted
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 SONIC TDA ptr
MOVEQ #(1<<TxEnable), D0
SMOVE D0,Command(A2) ; start transmitting pending chain
@callTxComp
MOVEA.L IntERtn,A0
JSR (A0) ; reenable interrupts
@doTxComp ; A3->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<<TransmitOK),D1 ; fast check
BNE.S @chkErrs ; not a normal result
ADDQ.L #1, NetStats.TxOK(A0) ; pkt transmitted OK
BRA.S @nxtTDA
@chkErrs
BTST #TransmitOK,D1
BEQ.S @latechk ; not transmitted
ADDQ.L #1, NetStats.TxOK(A0) ; pkt transmitted OK
BTST #DeferredTx,D1
BEQ.S @latechk
ADDQ.L #1, NetStats.DefTx(A0) ; had a deferred transmission
@latechk
BTST #OutWindow,D1
BEQ.S @aborts
ADDQ.L #1, NetStats.LateColl(A0) ; out of window
@aborts
BTST #ExcessColl,D1
BEQ.S @aborts1
ADDQ.L #1, NetStats.ExcessColl(A0) ; excess collisions
@aborts1
BTST #ExcessDefer,D1
BEQ.S @aborts2
ADDQ.L #1, NetStats.ExcessDef(A0) ; excess deferrals
@aborts2
BTST #FIFOUnderRun,D1
BNE.S @aborts3 ; internal error
BTST #BCMismatch,D1
BEQ.S @nxtTDA
@aborts3
ADDQ.L #1, NetStats.InMACTxErr(A0) ; internal MAC xmit error
@nxtTDA
MOVEA.L IntDRtn,A1
JSR (A1) ; disable interrupts
SMOVE Txpkt.frag_count(A3),D0 ; get count of fragments in this TDA
FragMul D0 ; calc offset to link field
LEA FreeTxTDA,A0
MOVE.L (A0),TxPkt.nextTD(A3) ; put this TDA on the free list
MOVE.L A3,(A0)
LEA Txpkt.frag_start(A3),A3
LEA (A3,D0.W),A3 ; A3->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<<ReceivedOK),D0
AND.W D1,D0 ; see if errors occured
BEQ.S @Checkerrs
ADDQ.L #1, NetStats.RxOK(A0) ; pkt received OK
MOVE.W #(1<<MultiRecd),D0
AND.W D1,D0
BEQ.S @chkB
ADDQ.L #1, NetStats.MultiRxOK(A0) ; multicast received OK
@chkB
ANDI.W #(1<<BroadRecd),D1
BEQ checkDesc
ADDQ.L #1, NetStats.BroadRxOK(A0) ; broadcast received OK
BRA checkDesc
@Checkerrs
BTST #CRCErr,D1
BEQ.S @chkFAE
ADDQ.L #1, NetStats.FCSerr(A0) ; CRC error
@chkFAE
BTST #FramAlignErr,D1
BEQ checkDesc
ADDQ.L #1, NetStats.FAerr(A0) ; frame alignment error
BRA checkDesc
@done
Set10 ; set timer for 10 seconds
RTS
;________________
PINT ; programmable interrupt
RTS
;________________
LCD ; load CAM done
RTS
;________________
HBL ; heartbeat lost
RTS
;________________
Reserved ; reserved
RTS
ENDWITH
ENDPROC
;•••••••••••••••• SONIC Initializaiton
; int SONICINIT( SONICbase,IntInstall,RECVRtn,RECVPrms,
; TRANRtn,TRANPrms,MemStart,MemSize,IntDisable,IntEnable,NetStatArray)
SONICINIT PROC EXPORT
parms RECORD {A6Link}
LocalSize EQU * ; no local vars
A6Link DS.L 2 ; link and return address
initp DS SONICinitParms ; parameters passed to us
ENDR
WITH parms,initp,SONICRegs,TxPkt,RRArec,Rxpkt,CAMDesc
LINK A6,#LocalSize ; save A6
MOVE.L A2,-(SP) ; save this reg
MOVEQ #-1,D0 ; possible error
CMPI.L #Min_Mem_Size,MemSize(A6) ; enough memory?
BLO @Ierror ; no
MOVEA.L SONICbase(A6),A2 ; A2->SONIC registers <Z3>
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} ; <Z3> thru next <Z3>
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 <Z3>
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 ; <Z3>
JSR (A0) ; reenable interrupts <Z3>
ADDQ.W #4,SP ; <Z3>
MOVEA.L (SP)+,A2
UNLK A6 ; <Z3>
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 ; <Z3>
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<<EOL_Bit), D0
SMOVE D0,(A0) ; set link field
EXG A1,A2 ; save curr frag ptr
MOVEA.L IntDRtn,A0 ; A0->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 <Z3>
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<<TxEnable), D0 ; set command bit
SMOVE D0,Command(A2) ; start transmitting
@done
MOVEA.L IntERtn,A0
JSR (A0) ; reenable interrupts maybe
ADDQ.W #4,SP
MOVEA.L SaveA2(A6),A2 ; restore reg
UNLK A6
SUB.L D0,D0 ; good return code
RTS
ENDWITH
ENDPROC
;•••••••••••••••• SONIC CAM Load
; SONICCAMLOAD(CAMentry,Loadit)
; Set/Clear Content Addressable Memory. Return CAM index when setting an entry.
SONICCAMLOAD PROC EXPORT
parms RECORD {A6Link}
LocalSize EQU *
A6Link DS.L 2
SONICPtr DS.L 1 ; <Z3>
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 <Z3>
JSR (A0) ; call it <Z3>
MOVE.L D0,-(SP) ; save SR returned <Z3>
MOVEA.L SONICptr(A6),A2 ; A2->SONIC <Z3>
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<<StartTimer)+(1<<RxEnable),D0
BNE.S @wait1 ; make sure no other commands active
SMOVE CAM_Enable(A2),D0 ; get current CAM map
MOVEQ #15,D3
@chkMap
ASL.W #1,D0 ; check a bit
DBCC D3,@chkMap ; do them all
BCC.S @haveit ; until we find one
MOVE.W D3,D0
EXT.L D0 ; D0.L=-1
BRA @Xit ; return error
@haveit
MOVEA.L RRAptr,A0 ; A0->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 ; <Z3>
JSR (A0) ; reenable interrupts <Z3>
ADDQ.W #4,SP ; <Z3>
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