mirror of
https://github.com/PDP-10/its.git
synced 2026-03-20 16:38:16 +00:00
637 lines
16 KiB
Groff
637 lines
16 KiB
Groff
;;; ITS Ethernet support -*-MIDAS-*-
|
|
;;;
|
|
;;; Copyright (C) 2018 Adam Sampson <ats@offog.org>
|
|
;;;
|
|
;;; This program is free software; you can redistribute it and/or modify
|
|
;;; it under the terms of the GNU General Public License as published by
|
|
;;; the Free Software Foundation, either version 2 of the License, or
|
|
;;; (at your option) any later version.
|
|
;;;
|
|
;;; This program is distributed in the hope that it will be useful,
|
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;;; GNU General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU General Public License
|
|
;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Generic Ethernet support
|
|
|
|
%NE32==37777777777
|
|
%NE16==177777
|
|
%NE8==377
|
|
|
|
.BEGIN %NEH ;Fields in the Ethernet header
|
|
PAD==2 ;padding at start of buffer
|
|
LENGTH==<PAD+6+6+2>
|
|
WORDS==<LENGTH/4>
|
|
|
|
;FIXME Would all the MAC stuff be easier if this was 3 16-bit fields?
|
|
DMAC0==<.BP <%NE16_4>,0>
|
|
DMAC12==<.BP <%NE32_4>,1>
|
|
SMAC01==<.BP <%NE32_4>,2>
|
|
SMAC2==<.BP <%NE16_20.>,3>
|
|
TYPE==<.BP <%NE16_4>,3> ;see %NETYP
|
|
.END %NEH
|
|
|
|
.BEGIN %NETYP ;Ethernet frame types
|
|
IP==4000
|
|
ARP==4006
|
|
.END %NETYP
|
|
|
|
.BEGIN %NEAH ;Fields in an Ethernet+IPv4 ARP packet
|
|
LENGTH==<2+2+1+1+2+6+4+6+4> ;should be this long
|
|
WORDS==<<LENGTH+3>/4>
|
|
|
|
HRD==<.BP <%NE16_20.>,0> ;see %NEAHR
|
|
PRO==<.BP <%NE16_4>,0> ;see %NETYP
|
|
HLN==<.BP <%NE8_28.>,1> ;must be 6 for Ethernet MAC
|
|
PLN==<.BP <%NE8_20.>,1> ;must be 4 for IPv4 address
|
|
OP==<.BP <%NE16_4>,1> ;see %NEAOP
|
|
SHA01==<.BP <%NE32_4>,2>
|
|
SHA2==<.BP <%NE16_20.>,3>
|
|
SPA0==<.BP <%NE16_4>,3> ;SPA split across words
|
|
SPA1==<.BP <%NE16_20.>,4>
|
|
THA0==<.BP <%NE16_4>,4>
|
|
THA12==<.BP <%NE32_4>,5>
|
|
TPA==<.BP <%NE32_4>,6>
|
|
.END %NEAH
|
|
|
|
.BEGIN %NEAHR ;ARP hardware type
|
|
ETHER==1
|
|
.END %NEAHR
|
|
|
|
.BEGIN %NEAOP ;ARP operation
|
|
REQ==1
|
|
REPLY==2
|
|
.END %NEAOP
|
|
|
|
%NEATS==64. ;Max number of ARP table entries
|
|
.BEGIN %NEAT ;ARP table entry
|
|
WORDS==3
|
|
|
|
HA01==<.BP <%NE32>,0> ;MAC address
|
|
HA2==<.BP <%NE16>,1>
|
|
PA==<.BP <%NE32>,2> ;IPv4 address
|
|
.END %NEAT
|
|
|
|
EBLK
|
|
|
|
ETHME: BLOCK %NEAT"WORDS ;Our MAC address, as ARP table entry
|
|
;(device driver must initialise this)
|
|
|
|
ETHHBF: BLOCK %NEH"WORDS ;Ethernet header unpacked
|
|
ETHABF: BLOCK %NEAH"WORDS ;ARP packet unpacked
|
|
|
|
ETHAT: BLOCK <%NEATS*%NEAT"WORDS> ;ARP table
|
|
ETHATS: 0 ;Number of entries in ARP table
|
|
|
|
BBLK
|
|
|
|
IPOGO: BUG INFO,[ipogo]
|
|
POPJ P,
|
|
IPKSNE: BUG INFO,[ipksne]
|
|
POPJ P,
|
|
|
|
;Process a received packet.
|
|
;R is buffer. Length in bytes is I. May clobber A-I, R.
|
|
;
|
|
ETHRX: MOVEI TT,ETHHBF ;Unpack Ethernet header into ETHHBF
|
|
HRL TT,R
|
|
BLTUB TT,<ETHHBF+%NEH"WORDS-1>
|
|
BUG INFO,[RX packet length ],OCT,I,[ ptr ],OCT,R
|
|
SUBI I,%NEH"LENGTH ;I now contains data length
|
|
ADDI R,%NEH"WORDS ;R points to data
|
|
|
|
LDB A,[%NEH"TYPE+ETHHBF]
|
|
BUG INFO,[etype=],OCT,A
|
|
CAIN A,%NETYP"IP
|
|
JRST ETHRXI
|
|
CAIN A,%NETYP"ARP
|
|
JRST ETHRXA
|
|
BUG INFO,[Unknown Ethernet type],OCT,A
|
|
POPJ P,
|
|
|
|
;Process an ARP packet.
|
|
;
|
|
ETHRXA: BUG INFO,[ARP length ],OCT,I
|
|
CAIGE I,%NEAH"LENGTH ;Check size first
|
|
JRST ETHRAS
|
|
|
|
MOVEI TT,ETHABF ;Unpack packet into ETHABF
|
|
HRL TT,R
|
|
BLTUB TT,<ETHABF+%NEAH"WORDS-1>
|
|
|
|
LDB A,[%NEAH"HRD+ETHABF]
|
|
LDB B,[%NEAH"HLN+ETHABF]
|
|
HRL B,A
|
|
CAME B,[%NEAHR"ETHER,,6]
|
|
JRST ETHRAH
|
|
|
|
LDB A,[%NEAH"PRO+ETHABF]
|
|
LDB B,[%NEAH"PLN+ETHABF]
|
|
HRL B,A
|
|
CAME B,[%NETYP"IP,,4]
|
|
JRST ETHRAP
|
|
|
|
LDB A,[%NEAH"SPA0+ETHABF] ;Get source IP address
|
|
LSH A,16.
|
|
LDB B,[%NEAH"SPA1+ETHABF]
|
|
IOR A,B
|
|
BUG INFO,[ARP source addr ],OCT,A
|
|
|
|
MOVE E,ETHATS ;Is the IP address in the table already?
|
|
SOS E
|
|
ETHRX1: JUMPL E,ETHRX2 ;Any more entries to check?
|
|
LDB B,[%NEAT"PA+ETHAT(E)]
|
|
BUG INFO,[ARP table entry ],OCT,E,[ of ],OCT,ETHATS,[ is ],OCT,B
|
|
CAMN A,B ;Match?
|
|
JRST ETHRX3 ; Yes - update it
|
|
SOJA E,ETHRX1 ;Prev entry
|
|
|
|
ETHRX2: MOVE E,ETHATS ;Not found in table - add new entry
|
|
CAIL E,%NEATS ;Table full?
|
|
JRST [ MOVEI E,1 ; Yes - flush it and start over
|
|
MOVEM E,ETHATS ; FIXME remove a random entry instead
|
|
SETZM E
|
|
JRST ETHRX3]
|
|
AOS ETHATS
|
|
|
|
ETHRX3: DPB A,[%NEAT"PA+ETHAT(E)] ;Store IP address
|
|
LDB C,[%NEAH"SHA01+ETHABF] ;Get sender's MAC
|
|
LDB D,[%NEAH"SHA2+ETHABF]
|
|
DPB C,[%NEAT"HA01+ETHAT(E)] ;Store MAC
|
|
DPB D,[%NEAT"HA2+ETHAT(E)]
|
|
BUG INFO,[Set ARP table entry ],OCT,E,[ to ip ],OCT,A,[ mac ],OCT,C,[ ],OCT,D
|
|
|
|
LDB E,[%NEAH"OP+ETHABF] ;Get op
|
|
BUG INFO,[ARP op ],OCT,E
|
|
CAIE E,%NEAOP"REQ ;Is this a request?
|
|
POPJ P, ;No - nothing more to do
|
|
|
|
LDB B,[%NEAH"TPA+ETHABF] ;Get the target PA
|
|
CAME B,[ETHUS] ;Is it our address?
|
|
POPJ P, ;No - nothing more to do
|
|
|
|
BUG INFO,[ARP reply]
|
|
MOVEI E,%NEAOP"REPLY ;Construct reply packet
|
|
DPB E,[%NEAH"OP+ETHABF]
|
|
DPB A,[%NEAH"TPA+ETHABF] ;Set sender PA as target PA
|
|
DPB B,[%NEAH"SPA1+ETHABF] ;Set our PA as source PA
|
|
LSH B,-16.
|
|
DPB B,[%NEAH"SPA0+ETHABF]
|
|
MOVE E,C ;Reshuffle sender's MAC from 01/2 to 0/12 form
|
|
LSH C,-16.
|
|
LSH E,16.
|
|
IOR D,E
|
|
DPB C,[%NEAH"THA0+ETHABF] ;Set as target HA
|
|
DPB D,[%NEAH"THA12+ETHABF]
|
|
DPB C,[%NEH"DMAC0+ETHHBF] ;Set as destination MAC in header
|
|
DPB D,[%NEH"DMAC12+ETHHBF]
|
|
LDB C,[%NEAT"HA01+ETHME] ;Get our HA
|
|
LDB D,[%NEAT"HA2+ETHME]
|
|
DPB C,[%NEAH"SHA01+ETHABF] ;Set as source HA
|
|
DPB D,[%NEAH"SHA2+ETHABF]
|
|
|
|
MOVEI R,ETHABF ;Send it!
|
|
MOVEI I,%NEAH"LENGTH
|
|
JRST ETHTX
|
|
|
|
ETHRAS: BUG INFO,[ARP packet too short, length ],OCT,I
|
|
POPJ P,
|
|
ETHRAH: BUG INFO,[ARP wrong hardware type ],OCT,B
|
|
POPJ P,
|
|
ETHRAP: BUG INFO,[ARP wrong protocol ],OCT,B
|
|
POPJ P,
|
|
|
|
;Process an IPv4 packet.
|
|
;
|
|
ETHRXI:
|
|
BUG INFO,[IP]
|
|
POPJ P,
|
|
|
|
;Send an Ethernet packet (if possible).
|
|
;Unpacked header is in ETHHBF. R contains pointer to unpacked data.
|
|
;I contains data length in bytes.
|
|
;
|
|
;FIXME IFN UNAP,...
|
|
ETHTX: BUG INFO,[ethtx ptr ],OCT,R,[ len ],OCT,I
|
|
PUSHJ P,UNAFF ;Find a free transmit buffer
|
|
POPJ P, ;None left. Give up.
|
|
BUG INFO,[ethtx buf ],OCT,B,[ desc ],OCT,D,[ len ],OCT,I
|
|
|
|
MOVE TT,B ;Convert header
|
|
HRLI TT,ETHHBF
|
|
BLTBU TT,<%NEH"WORDS-1>(B)
|
|
|
|
ADDI B,%NEH"WORDS ;Convert data
|
|
MOVE TT,B
|
|
HRL TT,R
|
|
MOVE A,I
|
|
ADDI A,3
|
|
LSH A,-2
|
|
ADD A,B
|
|
MOVE C,TT ;XXX
|
|
BUG INFO,[ethtx data bltbu tt ],OCT,C,[ a ],OCT,A
|
|
BLTBU TT,(A)
|
|
|
|
ADDI I,%NEH"LENGTH-%NEH"PAD ;Add length of header
|
|
BUG INFO,[ethtx end desc ],OCT,D,[ len ],OCT,I
|
|
JRST UNATX ;Transmit it
|
|
|
|
;Check whether the interface is able to send a packet.
|
|
;Skip-return if it is.
|
|
;
|
|
ETHCTS:
|
|
;FIXME check if we have an ARP entry, and if not, send a request
|
|
AOS (P)
|
|
POPJ P,
|
|
|
|
IFN UNAP,[
|
|
;;; DEUNA/DELNA driver
|
|
|
|
$INSRT DEUNA
|
|
|
|
%ETMTU==1492. ;MTU (could be 1500, but be conservative)
|
|
%ETMXL==(1500./4) ;Maximum packet length in 32-bit words
|
|
|
|
EBLK
|
|
|
|
;FIXME This memory stuff would be simpler given a routine
|
|
;that allocated and mapped a page, returning the PDP-10
|
|
;address and the two Unibus addresses. We could then
|
|
;allocate on the fly while setting up the rings.
|
|
;FIXME The setup stuff might also be cleaner with byte ptrs as above.
|
|
|
|
UNPAGS: BLOCK %UNNPG ;PDP-10 pages: 0,,addr
|
|
UNPAGU: BLOCK %UNNPG ;Unibus pages: first-addr,,second-addr
|
|
|
|
UNTXRP: BLOCK %UNNTB ;Transmit ring: descriptor addr,,buffer addr
|
|
UNRXRP: BLOCK %UNNRB ;As above for receive
|
|
;(setup code assumes those two are contiguous)
|
|
|
|
UNTXRI: 0 ;Transmit ring index (next free slot)
|
|
UNRXRI: 0 ;Receive ring index (next used slot)
|
|
|
|
UNADNI: CPOPJ ;Call this after a DNI interrupt
|
|
UNARCB: 0 ;Have we seen an RCBI interrupt?
|
|
;FIXME A flags word would make more sense (if we ever need a second flag!)
|
|
|
|
;FIXME For horrible hack below
|
|
BLOCK 2000-<.&1777> ;Align to a page boundary
|
|
UNPGHK: BLOCK 2000*%UNNPG
|
|
|
|
BBLK
|
|
|
|
;BUG INFO the UNA's registers.
|
|
UNREGS: PUSH P,A
|
|
PUSH P,B
|
|
PUSH P,C
|
|
PUSH P,D
|
|
IORDI A,%UNR0
|
|
IORDI B,%UNR1
|
|
IORDI C,%UNR2
|
|
IORDI D,%UNR3
|
|
BUG INFO,[UNA PCSR0=],OCT,A,[ PCSR1=],OCT,B,[ PCSR2=],OCT,C,[ PCSR3=],OCT,D
|
|
POP P,D
|
|
UNR3: POP P,C
|
|
POP P,B
|
|
POP P,A
|
|
POPJ P,
|
|
|
|
;BUG INFO the PCB.
|
|
UNDPCB: PUSH P,A
|
|
PUSH P,B
|
|
PUSH P,C
|
|
MOVE A,UNPAGS
|
|
MOVE B,0(A)
|
|
MOVE C,1(A)
|
|
BUG INFO,[UNA PCB:],OCT,B,[ ],OCT,C
|
|
JRST UNR3
|
|
|
|
;Initialise the DEUNA.
|
|
;
|
|
UNAINI: MOVEI D,%UNNPG-1
|
|
|
|
UNAIN1:
|
|
IFN 0,[
|
|
;FIXME This causes the core job to crash later...
|
|
PUSHJ P,TCALL ;Allocate a page of unshuffleable low core
|
|
JRST IOMQ
|
|
BUG HALT,[Couldn't allocate page for DEUNA]
|
|
|
|
MOVEI B,MUETH ;Tag what it's being used for
|
|
DPB B,[MUR,,MEMBLT(A)]
|
|
|
|
LSH A,10. ;Save the pointer
|
|
]
|
|
IFE 0,[
|
|
MOVEI A,2000 ;Use statically-allocated block above
|
|
IMUL A,D
|
|
ADDI A,UNPGHK
|
|
]
|
|
|
|
MOVEM A,UNPAGS(D)
|
|
|
|
MOVE B,A
|
|
LSH B,-9. ;DEC page number
|
|
;FIXME Could we use %UQFST? (How do we know nobody else is?)
|
|
; Could have a word with either 0 or the MU in...
|
|
IORI B,%UQVAL
|
|
|
|
PUSHJ P,UBAASL ;Allocate first UBA paging slot
|
|
IOWRI B,UBAPAG(A)
|
|
HRLZ C,A
|
|
PUSHJ P,UBAASL ;Allocate second slot
|
|
AOS B
|
|
IOWRI B,UBAPAG(A)
|
|
HRR C,A
|
|
LSH C,11.
|
|
MOVEM C,UNPAGU(D)
|
|
|
|
MOVE A,UNPAGS(D)
|
|
;BUG INFO,[UNA block ],OCT,D,[ entry ],OCT,B,[ pdp10 ],OCT,A,[ unibus ],OCT,C
|
|
|
|
SOJGE D,UNAIN1
|
|
|
|
;Initialisation sequence from DELUA user guide, page 3-8 (42).
|
|
;This needs us to wait for an interrupt in several places, so
|
|
;UNACMD has magic interrupt-continuation behaviour.
|
|
|
|
;Enable interrupts.
|
|
MOVEI A,%UN0"INTE
|
|
IOWRI A,%UNR0
|
|
|
|
;Set the PCB address.
|
|
HLRZ A,UNPAGU ;Low bits
|
|
IOWRI A,%UNR2
|
|
LSH A,-16. ;High bits
|
|
IOWRI A,%UNR3
|
|
MOVEI A,%UN0"DNI+%UNCMD"GETPCB ;Ack DNI as it's on after reset
|
|
PUSHJ P,UNACMD ;We've really returned to the caller here
|
|
|
|
;Get the MAC address for ARP to use
|
|
MOVE A,UNPAGS
|
|
MOVSI TT,%UNAF"RDDPA
|
|
MOVEM TT,0(A)
|
|
MOVEI A,%UNCMD"GETCMD
|
|
PUSHJ P,UNACMD
|
|
PUSHJ P,UNDPCB ;Show results
|
|
MOVEI TT,ETHHBF ;Unpack MAC address into ETHHBF
|
|
HRL TT,UNPAGS
|
|
BLTUB TT,ETHHBF+1
|
|
MOVE TT,[.BP <%NE16_4.>,ETHHBF]
|
|
LDB A,TT
|
|
LSH A,16.
|
|
ILDB B,TT
|
|
IOR A,B
|
|
BUG INFO,[my MAC 01 =],OCT,A
|
|
DPB A,[%NEAT"HA01+ETHME]
|
|
ILDB A,TT
|
|
BUG INFO,[my MAC 2 =],OCT,A
|
|
DPB A,[%NEAT"HA2+ETHME]
|
|
|
|
;Set up transmit and receive rings
|
|
MOVE D,UNPAGS ;Point to TDR (RDR follows)
|
|
ADDI D,<%UNTDR/4>
|
|
|
|
SETZM C ;Buffer index
|
|
|
|
UNAIN2: MOVE TT,C ;Work out index into UNPAGS/UNPAGU
|
|
LSH TT,-1
|
|
AOS TT
|
|
HLRZ B,UNPAGU(TT) ;Get Unibus addr. Even on the left...
|
|
TRNE C,1
|
|
HRRZ B,UNPAGU(TT) ;... and odd on the right
|
|
ADDI B,%UNBOF ;Add word-aligning padding at start
|
|
|
|
;FIXME This bit for now, until I rewrite this whole section..
|
|
HRLM D,UNTXRP(C) ;Descriptor pointer
|
|
MOVE A,UNPAGS(TT)
|
|
TRNE C,1
|
|
ADDI A,1000
|
|
HRRM A,UNTXRP(C) ;Data pointer
|
|
MOVE A,UNTXRP(C)
|
|
;BUG INFO,[ring],OCT,C,[ptr],OCT,A
|
|
|
|
MOVSI A,%UNBSZ ;SLEN (only used for recv)
|
|
HRR A,B ;SEGB low bits
|
|
MOVEM A,0(D)
|
|
;BUG INFO,[t/rdr ],OCT,C,[ at ],OCT,D,[ 0/1 = ],OCT,A
|
|
|
|
LSH B,-16. ;SEGB high bits
|
|
CAIL C,%UNNTB ;in the receive ring?
|
|
IORI B,%UNTD"OWN ; yes - give this buffer to DEUNA
|
|
HRLZS B ;zero other flags
|
|
MOVEM B,1(D)
|
|
;BUG INFO,[t/rdr ],OCT,C,[ at ],OCT,D,[ 2/3 = ],OCT,B
|
|
|
|
ADDI D,2 ;Next descriptor
|
|
AOS C ;Next buffer
|
|
CAIGE C,<%UNNTB+%UNNRB>
|
|
JRST UNAIN2
|
|
|
|
;Set ring descriptor format
|
|
MOVSI A,%UNAF"WRDRF ;Set up for UDB-using command
|
|
PUSHJ P,UNAPCB
|
|
|
|
HLRZ B,UNPAGU ;Get Unibus address of TDR
|
|
ADDI B,%UNTDR
|
|
HRLZ A,B ;TDRB low bits
|
|
IORI A,<4_8> ;TELEN
|
|
LSH B,-16.
|
|
IOR A,B ;TDRB high bits
|
|
MOVEM A,0(D) ;Store word 0/1
|
|
;BUG INFO,[rdf 0/1 =],OCT,A
|
|
|
|
MOVSI A,%UNNTB ;TRLEN
|
|
HLRZ B,UNPAGU ;Get Unibus address of RDR
|
|
ADDI B,%UNRDR
|
|
HRR A,B ;RDRB low bits
|
|
MOVEM A,1(D) ;Store word 2/3
|
|
;BUG INFO,[rdf 2/3 =],OCT,A
|
|
|
|
LSH B,-16. ;RDRB high bits
|
|
IORI B,<4_8> ;RELEN
|
|
HRLZS B
|
|
IORI B,%UNNRB ;RRLEN
|
|
MOVEM B,2(D) ;Store word 4/5
|
|
;BUG INFO,[rdf 4/5 =],OCT,B
|
|
|
|
MOVEI A,%UNCMD"GETCMD
|
|
PUSHJ P,UNACMD
|
|
|
|
;Start the interface
|
|
MOVEI A,%UNCMD"START
|
|
PUSHJ P,UNACMD
|
|
|
|
BUG INFO,[DEUNA setup done]
|
|
MOVEI A,CPOPJ ;Reset UNADNI behaviour
|
|
MOVEM A,UNADNI
|
|
POPJ P,
|
|
|
|
;Set up the PCB for a command that uses the UDB.
|
|
;A=Unibus word 0 (command),,LH of Unibus word 3 (0 for most commands).
|
|
;Returns: D pointing to UDB.
|
|
;
|
|
UNAPCB: HLRZ B,UNPAGU ;Get Unibus address of UDB
|
|
ADDI B,%UNUDB
|
|
MOVE D,UNPAGS ;Point to PCB
|
|
MOVE C,B ;UDBB low bits
|
|
HLL C,A ;Command
|
|
MOVEM C,0(D) ;Store word 0/1
|
|
HRRZS A ;(command-specific)
|
|
LSH A,8.
|
|
LSH B,-16. ;UDBB high bits
|
|
IOR A,B
|
|
HRLZS A ;Zero word 3
|
|
MOVEM A,1(D) ;Store word 2/3
|
|
MOVE D,UNPAGS ;Point to UDB
|
|
ADDI D,<%UNUDB/4>
|
|
POPJ P,
|
|
|
|
;Send a command (in A). Then set the next instruction in the caller as UNADNI,
|
|
;and return to the caller's caller -- this means that it'll appear to return
|
|
;(in interrupt context) when the command is done.
|
|
;
|
|
UNACMD: POP P,UNADNI ;Back to the caller when DNI is set
|
|
IORI A,%UN0"INTE ;Keep interrupts on
|
|
IOWRI A,%UNR0 ;Send command
|
|
POPJ P, ;Return to caller's caller
|
|
|
|
;Handle a DEUNA interrupt.
|
|
;
|
|
;FIXME Should this use a low-priority interrupt for the actual copying?
|
|
;(as CHAOS does)
|
|
EBLK
|
|
UNABRK: 0
|
|
BBLK
|
|
JSR UTCSAV
|
|
|
|
IORDI H,%UNR0 ;Read status
|
|
;BUG INFO,[DEUNA interrupt, PCSR0=],OCT,H,[ UNADNI=],OCT,UNADNI
|
|
|
|
TRNE H,%UN0"SERI+%UN0"PCEI+%UN0"FATL+%UN0"USCI
|
|
BUG HALT,[DEUNA error, PCSR0=],OCT,H ;FIXME do something better!
|
|
|
|
TRNE H,%UN0"RXI
|
|
PUSHJ P,UNARX
|
|
|
|
;FIXME %UN0"TXI
|
|
|
|
TRNE H,%UN0"RCBI
|
|
SETOM UNARCB ;Just remember for when a buffer is freed
|
|
|
|
MOVE A,H
|
|
ANDI A,%UN0"ALLINT+%UN0"INTE
|
|
IOWRI A,%UNR0 ;Acknowledge interrupts
|
|
|
|
TRNE H,%UN0"DNI ;DNI handler *after* acknowledgement
|
|
PUSHJ P,@UNADNI ;(because it will probably issue a new cmd)
|
|
|
|
JRST DSKEX ;Return from interrupt
|
|
|
|
;Check the receive ring for new packets, and process them.
|
|
;Ideally, we want to process packets in the same order that the DEUNA is
|
|
;writing them to the ring -- but just in case we get out of sync, we check the
|
|
;whole ring each time, starting with the first empty slot (UNRXRI) we found
|
|
;last time.
|
|
;
|
|
UNARX: MOVSI W,<-%UNNRB> ;W = -Number to check,,buffer index
|
|
HRR W,UNRXRI
|
|
SETOM UNRXRI ;Haven't found an empty buffer yet
|
|
|
|
UNARX1: HRRZ A,W ;Wrap index around
|
|
CAIL A,%UNNRB
|
|
HLLZS W
|
|
|
|
HLRZ J,UNRXRP(W) ;Descriptor
|
|
|
|
MOVE Q,1(J) ;Is there a packet in this buffer? (OWN=0)
|
|
TLNE Q,%UNTD"OWN
|
|
JRST [ SKIPGE UNRXRI ;No. First empty buffer we've found?
|
|
HRRZM W,UNRXRI ; Yes, start here next time
|
|
JRST UNARX2]
|
|
|
|
TLNE Q,%UNTD"STF ;STF+ENF should always be true, but check...
|
|
TLNN Q,%UNTD"ENF
|
|
JRST UNARSF
|
|
|
|
TLNE Q,%UNTD"ERRS+%UNTD"OFLO
|
|
JRST UNARER
|
|
|
|
HRRZ R,UNRXRP(W) ;Buffer
|
|
LDB I,[.BP 7777,1(J)] ;Get MLEN
|
|
SUBI I,4 ;Ignore CRC at end
|
|
PUSHJ P,ETHRX
|
|
|
|
UNARX3: TLO Q,%UNTD"OWN ;Set OWN=1 to mark as free
|
|
MOVEM Q,1(J)
|
|
|
|
SKIPE UNARCB ;Have we had an RCB interrupt?
|
|
JRST [ SETZM UNARCB ;Yes. Tell the DEUNA there's a free buffer now.
|
|
MOVEI A,%UN0"INTE+%UNCMD"PDMD
|
|
IOWRI A,%UNR0
|
|
JRST UNARX2]
|
|
|
|
UNARX2: AOBJN W,UNARX1 ;Repeat if we haven't looked at all buffers
|
|
SKIPGE UNRXRI ;Repeat if we haven't found a free buffer
|
|
JRST UNARX1 ; FIXME: Or maybe HRRZM W,UNRXRI ?
|
|
|
|
POPJ P,
|
|
|
|
;These are only here because you can't use BUG inside []...
|
|
UNARSF: BUG INFO,[DEUNA split frame ],OCT,Q
|
|
JRST UNARX3
|
|
UNARER: BUG INFO,[DEUNA frame with errors ],OCT,Q
|
|
JRST UNARX3
|
|
|
|
;Find an empty buffer in the transmit ring.
|
|
;On success, skip return with descriptor in D, buffer in B.
|
|
;On failure (the ring is full), normal return.
|
|
;Clobbers A-TT.
|
|
;
|
|
UNAFF: MOVSI E,<-%UNNTB> ;E = -Number to check,,buffer index
|
|
HRR E,UNTXRI
|
|
|
|
UNAFF1: HRRZ A,E ;Wrap index around
|
|
CAIL A,%UNNTB
|
|
HLLZS E
|
|
|
|
HLRZ D,UNTXRP(E) ;Descriptor
|
|
MOVE A,1(D) ;Is this buffer free? (OWN=0)
|
|
BUG INFO,[unaff checking slot ],OCT,E,[ status ],OCT,A
|
|
TLNE A,%UNTD"OWN
|
|
JRST [ AOBJN E,UNAFF1 ;Try the next one
|
|
POPJ P,] ;No buffers free.
|
|
|
|
HRRZM E,UNTXRI ;Yes. Start at this buffer + 1 next time.
|
|
AOS UNTXRI ; (Doesn't matter if it wraps.)
|
|
|
|
HRRZ B,UNTXRP(E) ;Buffer
|
|
BUG INFO,[unaff found free buffer ],OCT,E,[ desc ],OCT,D,[ buff ],OCT,B
|
|
AOS (P) ;Skip return
|
|
POPJ P,
|
|
|
|
;Transmit a packet from a descriptor obtained by UNAFF.
|
|
;Descriptor in D, data length in bytes in I.
|
|
;Clobbers A.
|
|
;
|
|
UNATX: BUG INFO,[unatx desc ],OCT,D,[ length ],OCT,I
|
|
HRLM I,0(D) ;Set SLEN
|
|
MOVSI A,%UNTD"OWN+%UNTD"STF+%UNTD"ENF
|
|
IORM A,1(D) ;Set flags
|
|
|
|
MOVEI A,%UN0"INTE+%UNCMD"PDMD ;Tell the DEUNA to check the ring
|
|
IOWRI A,%UNR0
|
|
|
|
POPJ P,
|
|
|
|
;FIXME
|
|
;Stylewise, trying to match IMP...
|
|
;See:
|
|
;receive packet - IMPRM (uses IPGIPT, IPRDGM)
|
|
;send packet - IMPOB0 (uses IPGIOQ)
|
|
|
|
] ;UNAP
|