PGM EMCSEG
         REL
***********************************************************
*                                                         *
*       ASSEMBLER ROUTINES USED BY THE LAN HANDLER        *
*       (Version to interpret port number.)               *
*                                                         *
***********************************************************
EMCS     DW EMCEND-EMCS/2         LENGTH
         DW SECWORD
         DB 17,"EMCSEG  26-Nov-80"
*
******************************************************
*                                                    *
*         ETXBUFF(DESC, TXPKT)                       *
*                                                    *
* SUBROUTINE TO FILL TRANSMIT FIFO AND GO            *
*                                                    *
******************************************************
         DB 7,"ETXBUFF"
ETXBUFF  ADD BP,DX                STD BCPL ENTRY
         MOV 4(BP),DX
         POP (BP)
         POP 2(BP)
         MOV 6(BP),SI
         MOV 8(BP),BX             SAVE ARGUMENTS
         MOV 10(BP),AX
*
         SHL BX                   GET MC ADDRESS OF DESCRIPTOR
         MOV SI,2(BX)             DATA ADDRESS
         SHL SI
         MOV 14(BP),SI
         MOV BX,(BX)              HEADER ADDRESS
         SHL BX
         MOV 12(BP),BX
         MOV CX,!3                3 BYTES OF TYPE 1 PREAMBLE
ETXBLP   MOV AL,!PREAMBL1         FIRST LOAD PREAMBLE BYTE
         CALL ETXPUSH
         LOOP ETXBLP
         MOV AL,!PREAMBL2
         CALL ETXPUSH
         ADD BX,!BLOCKOFF
         MOV SI,!7
         XOR DX,DX                RESIDUE
         CALL CALCCRC             CALCULATE CRC AND PUT IN AX
         MOV BX,14(BP)
         MOV SI,12(BP)
         MOV CX,E.LEN(SI)
         XCHG CH,CL
         AND CH,!#7F
         MOV SI,CX
         MOV DX,AX
         CALL CALCCRC
         MOV SI,AX                SAVE CRC
         MOV BX,12(BP)
         MOV DX,E.LEN(BX)         GET LENGTH
         XCHG DH,DL               -> INTEL CONVENTION
         AND DH,!#7F              REMOVE POSSIBLE CONTROL BIT
         ADD BX,!BLOCKOFF         TRUE START OF BLOCK
*
         MOV CX,!HEADSIZE
*    LOOP FOR LOADING BLOCK
ETXBLK   MOV AL,(BX)
         CALL ETXPUSH
         INC BX
         LOOP ETXBLK
*
         OR DX,DX
         JLE ETXBLK3        MAY BE 0
         MOV CX,DX                LENGTH OF DATA PART
         MOV BX,14(BP)
ETXBLK2  MOV AL,(BX)
         CALL ETXPUSH
         INC BX
         LOOP ETXBLK2
* LOAD CRC
ETXBLK3  MOV AX,SI
         CALL ETXPUSH
         MOV AL,AH
         CALL ETXPUSH
*
*    DISABLE INTERRUPTS FOR CRITICAL SECTION
         CLI
         MOV SI,G.EDCB(DI)
         SHL SI
         MOV AX,10(BP)        PKT FOR DCB
         MOV D.TXPKT(SI),AX
*    SET OK TO TRANSMIT
         MOV CX,!16
ETXBLP2  LOOP ETXBLP2
         MOV AL,!OKTOTX
         OUT PORTB
         MOV AL,!STROBE
         OUT CONTROL              THIS IS THE WAY WE STROBE
         DEC AL
         OUT CONTROL
         STI                      ENABLE INTERRUPTS
*    RETURN
         MOV SI,4(BP)
         SUB BP,SI
         JIS (BP,SI)
*
* ETXPUSH ROUTINE CALLED BY ETXBUFF TO LOAD 1 BYTE INTO TRANSMIT
* FIFO. BX,SI,AH AND CX MUST BE PRESERVED. BYTE IS IN AL.
*
ETXPUSH  OUT PORTB                PUT BYTE ON DATA LINES
         MOV AL,!ISTROBE
         OUT CONTROL
         DEC AL
         OUT CONTROL
         RET
*******************************************************
*                                                     *
*                 OKTORX(RXPKT)                       *
*                                                     *
* STROBES THE OKTORX CONTROL LINE ON THE CONTROLLER   *
* AND SETS RXPKT FIELD IN DCB (WE MUST DISABLE        *
* INTERRUPTS)                                         *
*                                                     *
*******************************************************
         DW LIBWORD
         DB 7,"OKTORX"
OKRX     CLI
         MOV SI,G.EDCB(DI)
         SHL SI
         MOV D.RXPKT(SI),BX         SET FIELD IN DCB
         MOV AL,!OKTORX
         OUT PORTB
         MOV AL,!STROBE
         OUT CONTROL
         DEC AL
         OUT CONTROL
         STI
         RETS
*
************************************************
*                                              *
*         LANRESET()                           *
*                                              *
* SUBROUTINE TO RESET ETHERNET BOARD           *
*                                              *
************************************************
         DW LIBWORD
         DB 7,"LANRESE"
LANRESET XOR AL,AL
         OUT PORTC                SET CONTROL LINES INACTIVE
         MOV AL,!RESETB
         OUT PORTB
         MOV AL,!STROBE
         OUT CONTROL
         DEC AL
         OUT CONTROL
         RETS
*
************************************************
*                                              *
*         ERXBUFF(RXPKTQ)                      *
*                                              *
* SUBROUTINE TO READ A BLOCK FROM THE RECEIVE  *
* FIFO, THIS ASSUMES SOMETHING HAS BEEN        *
* RECEIVED.                                    *
*                                              *
* The queue of receive buffers is checked      *
* to see if there are any reads outstanding    *
* on this port.                                *
************************************************
         DB 7,"ERXBUFF"
ERXBUFF  ADD BP,DX                STD BCPL ENTRY
         MOV 4(BP),DX
         POP (BP)
         POP 2(BP)
         MOV 6(BP),SI
         MOV 8(BP),BX         Save argument
*
         MOV BX,!THEAD        Read header into here
         MOV 10(BP),BX
         INC BX               start of block + 1 (no dest.)
         MOV CX,!HEADSIZE-1
         CALL ERXRD               READ THE HEADER
         MOV -7(BX),!NODAD[B]     PUT OUR ADDRESS AS DEST!
         MOV AX,-6(BX)
         AND AH,!#F0              Get bits of port number
* any requests outstanding for this port?
*  look down list of waiting packets.
         MOV SI,8(BP)        head of rxpktq
NXRP     SHL SI
         JNE ERXQ            end of list?
         XOR BX,BX           yes, return 0
         B ERXB2
*
ERXQ     MOV BX,P.ARG1(SI)   get address of header part
         SHL BX
         MOV BX,E.P1(BX)        dest. port (swapped bytes)
         AND BH,!#F0
         CMP AX,BX
         JE FERP             found port
         MOV SI,(SI)
         JMP NXRP            try next
*
FERP     MOV CX,!7           now copy header
         MOV 12(BP),SI       save pkt
         MOV BX,P.ARG1(SI)
         SHL BX
         MOV SI,10(BP)
FERP2    MOV DL,(SI)
         MOV BLOCKOFF(BX),DL
         INC SI
         INC BX
         LOOP FERP2
*
         MOV CX,-2(SI)            GET LENGTH INFO
         XCHG CH,CL               -> INTEL CONVENTION
         AND CX,!#7FFF            REMOVE ANY CONTROL BIT
         JE ERXBA2                COUNT=0
*************************************************
*************************************************
*************************************************
         CMP CX,!#120
         JBE ERXBA1
         MOV CX,!#1234       ABORT!!!!!!!!!
         INT 34              !!!!!!!!!!!!!!
ERXBA1   EQU $
*************************************************
*************************************************
*************************************************
         MOV BX,12(BP)            get address of data part
         MOV BX,P.ARG2(BX)
         SHL BX
         CALL ERXRD               READ REST OF BLOCK
ERXBA2   MOV BX,12(BP)
         SHR BX
*    RETURN
ERXB2    MOV SI,4(BP)
         SUB BP,SI
         JIS (BP,SI)
*
* ROUTINE USED BY ERXBUFF TO READ CX BYTES FROM BLOCK
* STARTING AT ADDRESS IN BX, WHICH REGISTER IS CHANGED.
*
ERXRD    EQU $
ERXRD2   MOV AL,!OSTROBE          SET UP FOR INPUT STROBE
         OUT CONTROL
         DEC AL
         DEC AL
         DEC AL
         INC AL
         INC AL
         OUT CONTROL
         IN PORTA                 READ BYTE
         MOV (BX),AL              STUFF INTO BLOCK
         INC BX                   BUMP POINTER INTO BLOCK
         LOOP ERXRD2
*
         RET
*
*
* THIS SUBROUTINE WILL CALCULATE THE CRC ON A BLOCK TO BE
* TRANSMITTED ON THE LOCAL AREA NETWORK.
* ON ENTRY BX CONTAINS THE MC ADDRESS OF THE START OF THE BLOCK.
* SI = LENGTH IN BYTES
* DX = RESIDUE FROM ANY PREVIOUS PART OF BLOCK
* RETURN WITH NEW RESIDUE IN AX
* THIS NEED NOT BE PRESERVED.
*
* THE POLYNOMIAL USED IS X**16 + X**15 + X**2 + 1,
* THE SAME AS THAT USED
* BY THE MOTOROLA CRC CHIPS AND ALSO THE SAME AS THE STANDARD
* IBM BISYNC POLYNOMIAL.
*
* First, SI is set to the number of words in the block, ignoring
* any odd byte. CX is set if there is an odd number of bytes.
*
CALCCRC  XOR CX,CX
         SHR SI                   make word count
         RCR CX                   CX := non-zero if carry is set
         MOV AX,DX
* step through words calculating residue term by term.
* get message word
CC0      DEC SI
         JL CC33
         MOV AX,(BX)
         XOR AX,DX
         MOV DX,AX                save message+residue
*
* 1st term
*
         SHR AX                   *x
         XOR AX,DX                +N
         SHR AX                   *x
*
* 2nd term
*
*    if lsb of N = 0 then -> 0 else -> #X5000
*
         TEST DX,!1
         JE CC1
         XOR AX,!#5000            add to 1st term
*
* 3rd term
*
*    if bit 1 = 0 -> 0 else -> #XA001
*
CC1      TEST DX,!2
         JE CC2
         XOR AX,!#A001
*
* 4th term, if even parity -> 0 else -> #XC001
*
*
CC2      XOR DL,DH                (FOR PARITY MUST BE IN 8-BIT REGISTER)
         JPE CC3                  jump on parity even
         XOR AX,!#C001
* see if finished all complete words
CC3      MOV DX,AX                NEW RESIDUE TO DX
         INC BX
         INC BX
         JMP CC0
* finished words, see if odd byte
CC33     JCXZ CC4                 no , cx=0
* do calculation for odd byte
*
* 1st term
*
         XOR AL,AL
         XCHG AH,AL               shift low part of residue by 8
*
* 2nd term
*
         MOV CX,DX                get residue
         XOR CH,CH                leave only high part of residue
         XOR CL,(BX)               +message
         XCHG CH,CL               CHANGE SIGNIFICANCE
         MOV SI,CX
         SHR CX
         XOR CX,SI
         SHR CX
         XOR AX,CX                +first term
*
* 3rd term
*
         MOV CL,DL                get upper part of residue
         XOR CL,(BX)              +message
         JPE CC5                  jump if parity even
         XOR AX,!#C001
CC5      INC BX
*
*
* return with BX changed
CC4      RET
*
*
*
* GLOBAL INITIALIZATION
*
         EVEN
         DW 0
         DW G.OKRX/2
         DW OKRXI-EMCS
         DW G.LANR/2
         DW LANRESEI-EMCS
         DW G.ETXB/2
         DW ETXBUFFI-EMCS
         DW G.ERXB/2
         DW ERXBUFFI-EMCS
         DW G.OKRX/2
*
EMCEND   EQU $
*
*
UG       EQU 150
SECWORD  EQU 12345
LIBWORD  EQU 23456
G.ETXB   EQU UG*2
G.ERXB   EQU UG+1*2
G.LANR   EQU UG+2*2
G.OKRX   EQU UG+3*2
G.EDCB   EQU UG+13*2
D.RXPKT  EQU 28
D.TXPKT  EQU 26
PREAMBL1 EQU #AA
PREAMBL2 EQU #2A
HEADSIZE EQU 7
BLOCKOFF EQU 16
E.P1     EQU BLOCKOFF+1
E.P2     EQU BLOCKOFF+2
P.ARG1   EQU 10
P.ARG2   EQU 12
E.LEN    EQU BLOCKOFF+5
*
*
PORTA    EQU #C8
PORTB    EQU #CA
PORTC    EQU #CC
CONTROL  EQU #CE
*
*
OKTOTX   EQU #FF
OKTORX   EQU #9F
RESRXEND EQU #5F
*
*
ISTROBE  EQU 0*2+1
OSTROBE  EQU 1*2+1
RESETB   EQU 2*2+1
STROBE   EQU 3*2+1
*
NODAD    EQU 1
*
         EVEN
         DSEG
         EVEN
DEMCS    DW DEMCSEND-DEMCS/2
OKRXI    DW OKRX
         DW 0
LANRESEI DW LANRESET
         DW 0
ETXBUFFI DW ETXBUFF
         DW 0
ERXBUFFI DW ERXBUFF
         DW 0
THEAD    RES 10,0            2 EXTRA FOR 'SAFETY' (!)
DEMCSEND EQU $
         END