;*****************************************************************************;
;*                                                                           *;
;*     File:   IBMTOKEN.ASM                                                  *;
;*     Auth:   Brian Fisher                                                  *;
;*             Queens University                                             *;
;*             Computing and Communications Services                         *;
;*             Rm 2-50 Dupuis Hall                                           *;
;*             Kingston Ontario                                              *;
;*                                                                           *;
;*     Date:   September 3 1989                                              *;
;*                                                                           *;
;*     Purp:   Ethernet (3C501) packet driver for IBM Token Ring.  This      *;
;*             driver requires the IBM LAN support program.  The current     *;
;*             implementation supports IP and ARP.                           *;
;*                                                                           *;
;*             This driver was derived from GENERIC.ASM, the driver          *;
;*             skeleton program distributed with the FTP Software            *;
;*             Ethernet drivers.                                             *;
;*                                                                           *;
;*===========================================================================*;
;*     Program Logic:                                                        *;
;*                                                                           *;
;*     This driver supports IP and ARP transmissions on Token Ring.  RARP    *;
;*     and BOOTP have not been tested, and probably don't work.              *;
;*                                                                           *;
;*     The application interface uses Ethernet Blue Book encapsulation.      *;
;*     This program translates that to IEEE 802.2 LLC for transmission on    *;
;*     Token Ring.  Received packets are translated back and passed up to    *;
;*     the application using a couple of call back routines.                 *;
;*                                                                           *;
;*     Ethernet and Token Ring hardware addresses are the same size.  No     *;
;*     translation is performed.                                             *;
;*                                                                           *;
;*     Token Ring uses source routing.  When a packet is received the source *;
;*     address is checked for RIF information.  If present it is stored in   *;
;*     a table local to the driver.  SEND_PKT checks the SA bit of the       *;
;*     Ethernet destination address.  If set, the RIF is pulled from the     *;
;*     table and used to build the LLC header.  The MSB of the destination   *;
;*     address is cleared.  The SA bit of the source address is set          *;
;*     to indicate the presence of RIF.  The RIF direction bit is toggled.   *;
;*                                                                           *;
;*     The minimum size of an Ethernet packet is 60.  The maximum size is    *;
;*     1514 bytes.  When a Token Ring packet is received, it can be smaller  *;
;*     than 60.  Packet sizes are rounded up to 60 so the upper level will   *;
;*     accept them.                                                          *;
;*                                                                           *;
;*     IP packets can be transmitted without modification.                   *;
;*                                                                           *;
;*     ARP packets must be modified.  On transmit, the hardware address      *;
;*     space field must be changed from 0001h to 0006h.  The process is      *;
;*     reversed on receive.  Also, the size of an ARP packet is 28, but      *;
;*     the Ethernet call uses 60.  I drop the size to 28 on transmit, then   *;
;*     bump it back up to 60 on receive.  I imagine something similar will   *;
;*     be required for BOOTP and RARP.                                       *;
;*                                                                           *;
;*===========================================================================*;
;*                                                                           *;
;*      Revs:  Sep 29 1989     Successful tests with TN3270, FTP, and FTPBIN.*;
;*             B. Fisher       Some fine tuning required.                    *;
;*                                                                           *;
;*             Oct 1  1989     Cleaning up code.  Found bug that prevented   *;
;*                             buffer1/buffer2..n copies from working.       *;
;*                                                                           *;
;*             Oct 15 1989     Rearranged buffer sizes to accomodate Adapt I *;
;*                                                                           *;
;*             Nov 15 1989     Added missing popf at end of recv_complt      *;
;*                             Fixed missing popf at end of recv_complt      *;
;*                                                                           *;
;*             Nov 19 1989     Added marker to show interrupt activity       *;
;*                             for debugging.                                *;
;*                                                                           *;
;*             Nov 20 1989     Add changes for Release 5 compatibility       *;
;*                                                                           *;
;*             Nov 21 1989     Fix es/bx reversal at get_size                *;
;*                                                                           *;
;*             Nov 22 1989     RIF not working because of confusion with     *;
;*                             SA bit acquired via ARP and SA bit acquired   *;
;*                             through Token Receive.  Send_Pkt now looks    *;
;*                             up all dest addresses in RIF table.           *;
;*                                                                           *;
;*             Nov 23 1989     Have DEBUG mode report RIF table address when *;
;*                             get_multicast_ call made.                     *;
;*                                                                           *;
;*             Nov 28 1989     Add phony RIF to 'local' addresses so the     *;
;*                             bridge will pass them on.  ARP acquired HW    *;
;*                             addresses need RIF to cross bridge.  This is  *;
;*                             done in send_pkt.                             *;
;*                                                                           *;
;*             Nov 29 1989     Fixed REPT macro in SA_blk so size of SA info *;
;*                             is correct.  Didn't hurt function, however.   *;
;*                                                                           *;
;*             Nov 29 1989     Clear broadcast bit in RIF before entry in    *;
;*                             table.  This prevents bridges from altering   *;
;*                             existing route.  FF,FF,FF,FF,FF,FF address    *;
;*                             uses broadcast bit so ARP will work.          *;
;*                                                                           *;
;*             Nov 29 1989     Predefined broadcast RIF entry direction bit  *;
;*                             polarity reversal prevented broadcast from    *;
;*                             going over the bridge.                        *;
;*                                                                           *;
;*****************************************************************************;

version        equ     9

       include defs.asm

debug          = 0                             ; set to 1 for IRQ markers on 25

toke_vect      = 5Ch                           ; LAN Support Pgm Vector
toke_wait      = 0FFh                          ; completion code wait status
pool_size      = 512                           ; 8K space for buffer pool area

num_rcv_buff   = 2                             ; <2 defaults to 8
recv_size      = 1536                          ; max size of card recieve buffer
tran_size      = 1536                          ; max size of transmit buffer

STATION_ID     = 0;                            ; talk using direct station

RECV_OPTIONS   = 0C0h                          ; contiguous mac/data in bufs

open_cmd       =       3h                      ; LAN Support OpCodes
interrupt_cmd  =       0h
initialize_cmd =       20h
close_cmd      =       4h
modify_cmd     =       1h
restore_cmd    =       2h
trans_dir_cmd  =       0Ah
free_ccb_cmd   =       27h
recv_can_cmd   =       29h
open_recv_cmd  =       28h
get_status_cmd =       21h

SA_table_size = 32                             ; RIF info table max entries

LLC_AC         = 10h                           ; access control LLC
LLC_FC         = 40h                           ; frame control  LLC
LLC_SSAP_DSAP  = 0AAAAh                        ; reversed for network <grin>
LLC_CON        = 03h

MAC_hdr_size   = 22                            ; MAC+LLC+SNAP header length

not_SA_mask    = 7Fh
SA_mask        = 80h
RIF_size_mask  = 1Fh
RIF_dir_bit    = 80h

broadcast_byte = 0FFh                          ; broadcast is FF,FF,FF,FF,FF,FF

ARP_type       = 0608h                         ; reversed for network order
ARP_Eth_hwr    = 0100h                         ; reversed for network order
ARP_Tok_hwr    = 0600h                         ; reversed for network order
ARP_packet_size= 28                            ; fixes Ethernet assumption...
phony_RIF      = 2082h                         ; used for bridges...



LLC_info_size  = 8                             ; LLC (3) + SNAP(5) = 8

mark           = 0F90h                         ; marker debug pos on screen 25

marker         macro   st,nd

               IF  debug NE 0                    ; do marker if debug <> 0

               pushf                             ; show 2 char marker on
               push es                           ; 25th line, 1st column
               push ax
               mov  ax,0B800h
               mov  es,ax
               mov  al,'&st&'
               mov  byte ptr es:[mark],al
               mov  al,byte ptr es:[mark+1]      ; get color value
               inc  al
               and  al,0Fh
               or   al,1
               mov  byte ptr es:[mark+1],al      ; advance it to show activity
               mov  al,'&nd'
               mov  byte ptr es:[mark+2],al
               mov  al,byte ptr es:[mark+3]
               inc  al
               and  al,0Fh
               or   al,1
               mov  byte ptr es:[mark+3],al
               pop  ax
               pop  es
               popf

               ENDIF

               endm

call_token     macro   ccb
;--------------------------------------;
; loads address of CCB in ES:BX, then  ;
; calls the Token Ring Interface.      ;
;--------------------------------------;
               mov     ax,cs
               mov     es,ax
               mov     bx,offset &ccb&
               int     toke_vect
               endm


call_wait      macro
               local   wait_loop
;---------------------------------------;
; assumes ES:BX points to a CCB.  Waits ;
; for a pending command to terminate.   ;
;---------------------------------------;
wait_loop:
               mov     al,es:[bx+2]
               cmp     al,toke_wait
               je      wait_loop
               endm


set_ptr        macro   dest,addr
;---------------------------------------;
; loads a far pointer with a near       ;
; address offset and CS:                ;
;---------------------------------------;
               mov     ax,offset &addr&
               mov     word ptr [&dest&],ax
               mov     ax,cs
               mov     word ptr [&dest&+2],ax
               endm


print$         macro   string
;---------------------------------------;
;  sends $ terminated string to screen  ;
;---------------------------------------;
               mov     ah,9
               mov     dx,offset &string&      ; print $ terminated string
               int     21h
               endm


clr_struc      macro   name
;---------------------------------------;
;  clear all bytes of the named struc   ;
;  to zero.  Useful for Token calls.    ;
;---------------------------------------;
               mov     cx,SIZE &name&          ; uses ax, es:di, cx
               mov     ax,ds
               mov     es,ax
               mov     di,offset &name&
               xor     al,al
               cld
               rep     stosb                   ; set all elements to 0
               endm


rdupb          macro   l,n
&l&            db      ?
               rept    &n&-1
               db      ?
               endm
               endm

       IF debug NE 0

regs   struc                           ; from head.asm, [bp] references the
_RES   dw      ?                       ; IRQ service parameters with these.
_RDS   dw      ?
_RBP   dw      ?
_RDI   dw      ?
_RSI   dw      ?
_RDX   dw      ?
_RCX   dw      ?
_RBX   dw      ?
_RAX   dw      ?
_RIP   dw      ?
_RCS   dw      ?
_RCF   dw      ?               ;flags
regs   ends

       ENDIF


ccb_blk        struc                   ; CCB used for local token ring calls.
adapter        db      ?               ; adapter 0 or 1
command        db      ?               ; command code
retcode        db      ?               ; command completion code
work           db      ?               ; scratch
pointer        dd      ?               ; queue pointer
complt         dd      ?               ; command complete appendage
parms          dd      ?               ; pointer to CCB parameter table
ccb_blk ends


init_parms     struc                   ; parameters for DIR_INITIALIZE
bring_ups      dw      ?               ; result of bring up tests
sram_addr      dw      ?               ; shared ram address
reserved       dd      ?               ; not used...
chk_exit       dd      ?               ; adapter check exit
status         dd      ?               ; ring status exit
error          dd      ?               ; PC error exit
init_parms     ends


o_parms        struc                   ; ccb parms for DIR_OPEN
adapt          dd      ?               ; ptr to adapter parms
direct         dd      ?               ; ptr to direct parms
dlc            dd      ?               ; ptr to dlc parms
msg            dd      ?               ; ptr to msg parms
o_parms        ends


ada_blk        struc                   ; adapter open parms table format
err_code       dw      ?               ; open errors detected
options        dw      ?               ; various options
node_addr      dd      ?               ; node address ( 4 bytes here +  )
               dw      ?               ; rest of node address ( 2 here  )
group_add      dd      ?               ; group address
func_add       dd      ?               ; functional address
rcv_buff       dw      ?               ; number of receive buffers
rcv_len        dw      ?               ; receive buffer length
dhb_len        dw      ?               ; length of transmit buffers
hold_buf       db      ?               ; number of tx buffers
               db      ?               ; reserved
lock_code      dw      ?               ; lock code
id_addr        dd      ?               ; address of id code
ada_blk        ends


dir_blk        struc                   ; dir interfce open parms table format
buf_size       dw      ?               ; size of direct station buffer
pool_blks      dw      ?               ; number of 16 byte blocks in buff pool
pool_addr      dd      ?               ; address of direct station buffer pool
chk_exit       dd      ?               ; the rest can be 0 (defaults) for now
stat_exit      dd      ?               ; ring status appendage pointer
err_exit       dd      ?               ; PC error exit
work_addr      dd      ?               ; work area segment value
len_req        dw      ?               ; work area length requested
len_act        dw      ?               ; actual length obtained
dir_blk        ends


dlc_blk        struc                   ; dlc interface open parms table format
max_sap        db      ?               ; max number of saps
max_sta        db      ?               ; max number of stations
max_gsap       db      ?               ; max group saps
max_gmem       db      ?               ; max members per group
t1_tick1       db      ?               ; DLC timer t1 interval
t2_tick1       db      ?               ; DLC timer t2 interval
TI_tick1       db      ?               ; DLC timer TI interval
t1_tick2       db      ?               ; DLC timer t1 group 2
t2_tick2       db      ?               ; DLC timer t2 group 2
TI_tick2       db      ?               ; DLC timer TI group 2
dlc_blk        ends


mod_blk        struc                   ; modify open parms parameter block
buf_size       dw      ?               ; size of SAP buffers
pool_blks      dw      ?               ; length in 16-byte, of buffer pool
pool_adrs      dd      ?               ; address of direct interface buf pool
chkt_exit      dd      ?               ; appendage, adapter check
stat_exit      dd      ?               ; appendage, ring status
err_exit       dd      ?               ; appendage, PC error exit
new_opts       dw      ?               ; new options (wrap is ignored)
mod_blk        ends


tx_blk         struc                   ; transmit.dir.frame parameter block
station        dw      ?               ; defines station sending data
trans_fs       db      ?               ; * stripped FS field (returned)
rsap           db      ?               ; RSAP (remote sap value)
queue_1        dd      ?               ; address of TX queue 1
queue_2        dd      ?               ; address of TX queue 2
buf_len_1      dw      ?               ; length of buffer 1
buf_len_2      dw      ?               ; length of buffer 2
buffer_1       dd      ?               ; address of the first transmit buffer
buffer_2       dd      ?               ; address of the second transmit buffer
tx_blk         ends


free_blk       struc                   ; buffer.free parameters
st_id          dw      ?               ; station id
buf_left       dw      ?               ; returns number of buffers left
resrvd         dd      ?               ;
first_buf      dd      ?               ; address of 1st buffer to free
free_blk       ends


get_blk        struc                   ; buffer.get parameters
stn_id         dw      ?               ; station id
buffr_lef      dw      ?               ; buffers left
buff_get       db      ?               ; number of buffers to get
resrv1         db      ?               ; 3 bytes not used
resrv2         db      ?
resrv3         db      ?
frst_buff      dd      ?               ; address of buffer obtained
get_blk        ends


rx_blk         struc                   ; receive parms
id             dw      ?               ; station id
user_len       dw      ?               ; size of user space in buffer
receiver       dd      ?               ; appendage for received data
first_bf       dd      ?               ; pointer to 1st buffer
opts           db      ?               ; receive options
rx_blk         ends


status_blk     struc                   ; parameter table for dir.status
encoded_addr   dw      ?               ; adapters permanent ring address
               dd      ?
node_adrs      dw      ?               ; ring address set by open
               dd      ?
group_adrs     dd      ?
func_adrs      dd      ?
               REPT    16
               db      ?
               ENDM
               REPT    3
               dd      ?
               ENDM
               dw      ?
status_blk     ends





mac_header     struc                   ; token ring mac header format
AC             db      ?               ; access control byte
FC             db      ?               ; frame control byte
               rdupb   dest,EADDR_LEN
               rdupb   source,EADDR_LEN
dsap           db      ?               ; LLC dsap
ssap           db      ?               ; LLC ssap
control        db      ?               ; control byte
ptype          db      ?
               db      ?               ; SNAP header
               db      ?
ethtype        dw      ?               ; EtherType
rif            dw      ?               ; optional RIF information
               REPT    8               ; segment information
               dw      ?               ; this is not its real position.
               ENDM                    ; this is a variable length field...
mac_header     ends


SA_blk         struc
               rdupb   s_addr,EADDR_LEN
RCF            db      ?
               db      ?
route          dw      ?
               REPT    7
               dw      ?
               ENDM    ?
SA_blk         ends


ether_hdr      struc                           ; ethernet header format
               rdupb   ether_dest,EADDR_LEN
               rdupb   ether_src,EADDR_LEN
ether_type     dw      ?
ether_hdr      ends


first_buffer   struc                           ; Token receive 1st buffer
next_buffer    dd      ?                       ; ptr to next buffer
xx1            dw      ?
data_size      dw      ?                       ; size of user data
               REPT    12
               db      ?
               ENDM
user_data      db      ?
first_buffer   ends

user_data2     =       12                      ; 2nd buffer slightly different



;=============================================================================;
;======================= START OF PROGRAM CODE ===============================;
;=============================================================================;

code           segment word public
               assume  cs:code, ds:code

Token_address  db      EADDR_LEN dup (0)       ; holds my address after init.

ccb            ccb_blk <>                      ; scratch CCB
free_ccb       ccb_blk <>
tx_parms       tx_blk  <>                      ; transmit.dir.frame parms
iparms         init_parms <>                   ; init parms
pool_buff      db (pool_size+1)*16 dup (?)     ; buffer pool
free_parms     free_blk <>                     ; buffer.free parameters
get_parms      get_blk  <>                     ; buffer.get parameters

rx_ccb         ccb_blk <>                      ; receive CCB
rx_parms       rx_blk  <>                      ; receive parms


id_code        db 18 dup (0)   ; phony product id code
                               ; (a real space saver...)

ada_parm       ada_blk <0,0,0,0,0,0,num_rcv_buff,recv_size,tran_size,1,0,0,0>

dir_parm       dir_blk <0,pool_size,0,0,0,0,0,0,0>
dlc_parm       dlc_blk <0,0,0,0,0,0,0,0,0,0>
init_parm      o_parms <0,0,0,0>
initccb        ccb_blk <0,0,0,0,0,0,0>
toke_status    status_blk <>

modparms mod_blk <0,pool_size,0,0,0,0,0>
modccb ccb_blk < 0,modify_cmd,0,0,0,0,0>

lan_header  mac_header  <LLC_AC,LLC_FC>


;      SA RIF table has 1 entry in it by default, for the broadcast address.
;
SA_index dw            1
SA_table SA_blk <0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,82h,0A0h>
         db    ((SIZE SA_blk - 1)*SA_table_size) dup (0)


dir_interrupt  proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               clr_struc ccb                   ; initialize CCB
               mov     ax,token_card
               mov     [ccb.adapter],al
               mov     al,interrupt_cmd
               mov     [ccb.command],al
               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion
               xor     ah,ah
               mov     al,[ccb.retcode]        ; return the result in AX
               ret
dir_interrupt  endp


dir_initialize proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit:                                ;
;       Ah = bring up return code       ;
;       Al = ccb return code            ;
;---------------------------------------;
               clr_struc ccb                   ; clear ccb
               clr_struc iparms                ; clear init parms (defaults)

               mov     ax,token_card           ; assign parameters
               mov     [ccb.adapter],al
               mov     al,initialize_cmd       ; command....
               mov     [ccb.command],al

               set_ptr ccb.parms,iparms        ; link to init parms

               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion

               mov     ah,byte ptr iparms.bring_ups
               mov  al,[ccb.retcode]

               ret
dir_initialize endp


dir_open_adapter proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               mov     ax,token_card           ; get adapter number
               mov     [initccb.adapter],al

               mov     al,open_cmd
               mov     [initccb.command],al    ; 03h open_adapter command

               set_ptr ada_parm.id_addr,id_code
               set_ptr dir_parm.pool_addr,pool_buff
               set_ptr init_parm.adapt,ada_parm
               set_ptr init_parm.direct,dir_parm
               set_ptr init_parm.dlc,dlc_parm
               set_ptr initccb.parms,init_parm

               xor     al,al
               mov     [initccb.retcode],al

               call_token initccb              ; call the interface
               call_wait  initccb              ; wait for completion

               xor  ah,ah
               mov  al,[initccb.retcode]

               ret
dir_open_adapter endp


dir_close_adapter proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               clr_struc ccb                   ; clear CCB

               mov     ax,token_card           ; initialize local CCB
               mov     [ccb.adapter],al
               mov     al,close_cmd            ; close command
               mov     [ccb.command],al

               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion

               xor     ah,ah
               mov     al,[ccb.retcode]        ; return the result
               ret
dir_close_adapter endp


dir_modify_open proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               mov     ax,token_card           ; get adapter number
               mov     [modccb.adapter],al     ; command is a constant in ccb

               set_ptr modparms.pool_adrs,pool_buff
               set_ptr modccb.parms,modparms

               xor     al,al
               mov     [modccb.retcode],al

               call_token modccb       ; call the interface
               call_wait  modccb       ; wait for completion

               xor     ah,ah
               mov     al,[modccb.retcode]
               ret
dir_modify_open endp


dir_restore_open proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               clr_struc ccb                   ; initialize CCB

               mov     ax,token_card
               mov     [ccb.adapter],al
               mov     al,restore_cmd          ; dir.restore.open.parms
               mov     [ccb.command],al

               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion

               xor     ah,ah
               mov     al,[ccb.retcode]        ; return the result in AX
               ret
dir_restore_open endp


transmit_dir_frame proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;                                       ;
; Stack:                                ;
;       sp+14  dw [usr seg]             ;
;       sp+12  dw [usr ofs]             ;
;       sp+10  dw [usrsize]             ;
;       sp+8   dw [hdr seg]             ;
;       sp+6   dw [hdr ofs]             ;
;       sp+4   dw [hdrsize]             ;
;       sp+2   dw [ret ofs]             ;
;       sp+0   dw [  bp   ]             ;
;                                       ;
;  Exit:                                ;
;       Ah = stripped FS field          ;
;       al = CCB return code            ;
;---------------------------------------;

trans_frame    struc                           ; transmit_dir_frame parms
_x_bp          dw      ?                       ; old bp register
_x_ret_ofs     dw      ?                       ; near return address
header_size    dw      ?                       ; MAC header size
llc_header     dd      ?                       ; address of header
user_dsize     dw      ?                       ; user data size
user_daddress  dd      ?                       ; user data address
trans_frame    ends

               push    bp
               mov     bp,sp

               mov     ax,cs
               mov     ds,ax

               clr_struc ccb                   ; zero the CCB
               clr_struc tx_parms              ; zero parameters

               mov     ax,token_card           ; initialize local CCB
               mov     [ccb.adapter],al
               mov     al,trans_dir_cmd        ; transmit.dir.frame
               mov     [ccb.command],al
;
;      Set up transmit parameters, and link them to
;      the CCB...
;
               mov     ax,offset tx_parms
               mov     word ptr [ccb.parms],ax

               mov     ax,cs
               mov     word ptr [ccb.parms+2],ax
;
               mov     ax,STATION_ID

               mov     word ptr [tx_parms.station],ax

               mov     ax,[bp][user_dsize]
               mov     word ptr [tx_parms.buf_len_2],ax

               mov     ax,word ptr [bp][user_daddress]
               mov     word ptr [tx_parms.buffer_2],ax
               mov     ax,word ptr [bp][user_daddress+2]
               mov     word ptr [tx_parms.buffer_2+2],ax

               mov     ax,[bp][header_size]
               mov     word ptr [tx_parms.buf_len_1],ax

               mov     ax,word ptr [bp][llc_header]
               mov     word ptr [tx_parms.buffer_1],ax
               mov     ax,word ptr [bp][llc_header+2]
               mov     word ptr [tx_parms.buffer_1+2],ax

               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion

               mov     ah,[tx_parms.trans_fs]
               mov     al,[ccb.retcode]

               pop     bp

               ret     (SIZE trans_frame)-4    ; (forget bp and ret address)
transmit_dir_frame endp


buffer_free    proc
;---------------------------------------;
; Entry:                                ;
;      sp+4    [ seg 1st buffer ]       ;
;      sp+2    [ ofs 1st buffer ]       ;
;      sp+0    [ bp             ]       ;
;                                       ;
;  Exit: AX = CCB return code           ;
;---------------------------------------;
               mov     ax,cs
               mov     ds,ax

               push    bp
               mov     bp,sp

               clr_struc free_ccb
               clr_struc free_parms

               mov     ax,token_card           ; initialize local CCB
               mov     [free_ccb.adapter],al

               mov     al,free_ccb_cmd         ; buffer.free
               mov     [free_ccb.command],al

               mov     ax,offset free_parms
               mov     word ptr [free_ccb.parms],ax

               mov     ax,cs
               mov     word ptr [free_ccb.parms+2],ax
;
;      Load free_parms to release buffer
;
               mov     ax,STATION_ID
               mov     word ptr [free_parms.st_id],ax

               mov     ax,word ptr [bp][4]
               mov     word ptr [free_parms.first_buf],ax
               mov     ax,word ptr [bp][6]
               mov     word ptr [free_parms.first_buf+2],ax

               call_token free_ccb             ; call the interface

               xor     ah,ah
               mov     al,[free_ccb.retcode]   ; return the result in AX

               pop     bp

               ret     4
buffer_free    endp


recv_cancel    proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;                                       ;
;  Exit:                                ;
;       AX = CCB return code            ;
;                                       ;
;---------------------------------------;
               mov     ax,token_card           ; initialize local CCB
               mov     [ccb.adapter],al

               mov     al,recv_can_cmd         ; receive.cancel
               mov     [ccb.command],al

               xor     ax,ax                   ; word/bytes set to 0
               mov     [ccb.retcode],al
               mov     [ccb.work],al
               mov     word ptr [ccb.pointer],ax
               mov     word ptr [ccb.pointer+2],ax
               mov     word ptr [ccb.complt],ax
               mov     word ptr [ccb.complt+2],ax
               mov     word ptr [ccb.parms+2],ax

               mov     ax,STATION_ID
               mov     word ptr [ccb.parms],ax

               call_token ccb                  ; call the interface
               call_wait  ccb                  ; wait for completion

               xor     ah,ah
               mov     al,[ccb.retcode]        ; return the result in AX
               ret

recv_cancel    endp


open_receive proc
;---------------------------------------;
; Entry: token_card (0 or 1)            ;
;                                       ;
; Stack:                                ;
;       sp+6   dw [ret ofs]             ;
;       sp+4   dw [  bp   ]             ;
;       sp+2   dw [  ds   ]             ;
;       sp+0   dw [  es   ]             ;
;                                       ;
;  Exit:                                ;
;       AX = CCB return code            ;
;---------------------------------------;
               mov     ax,cs
               mov     ds,ax

               clr_struc rx_ccb
               clr_struc rx_parms;

               mov     ax,token_card           ; initialize local CCB
               mov     [rx_ccb.adapter],al

               mov     al,open_recv_cmd        ; receive
               mov     [rx_ccb.command],al

               mov     ax,offset recv_complt
               mov     word ptr [rx_ccb.complt],ax
               mov     ax,cs
               mov     word ptr [rx_ccb.complt+2],ax
;
;      Link receive parameters table
;
               mov     ax,offset rx_parms
               mov     word ptr [rx_ccb.parms],ax
               mov     ax,cs
               mov     word ptr [rx_ccb.parms+2],ax

               mov     ax,STATION_ID
               mov     word ptr [rx_parms.id],ax

               mov     al,RECV_OPTIONS
               mov     rx_parms.opts,al

               mov     ax,offset _receiver
               mov     word ptr [rx_parms.receiver],ax
               mov     ax,cs
               mov     word ptr [rx_parms.receiver+2],ax
;
;       Note: The receive request is submitted, but it doesn't 'complete'
;             here.  So DON'T wait for a completion code!
;
               call_token rx_ccb               ; call the interface

               xor     ah,ah
               mov     al,[rx_ccb.retcode]     ; return the result
               ret
open_receive endp


recv_complt proc
;
;      This procedure is an interrupt routine called by the Token Card
;      when receive completion occurs after an error.  All it should have
;      to do is resubmit the receive request, then exit.
;
               pushf
               push ax
               push bx
               push cx
               push dx
               push si
               push di
               push ds
               push es
               push bp
               sti

               marker E,R
               call open_receive               ; restart communications

               pop  bp
               pop  es
               pop  ds
               pop  di
               pop  si
               pop  dx
               pop  cx
               pop  bx
               pop  ax
               popf
               iret
recv_complt    endp


recv_frame     struc                           ; stack frame for receiver
               RIF_size dw     ?               ; size of RIF in packet.
               user_buff dd    ?               ; pointer to users buffer
               user_size dw    ?               ; size of buffer needed/used
               _BP     dw      ?               ; register set on stack
               _ES     dw      ?
               _DS     dw      ?
               _DI     dw      ?
               _SI     dw      ?
               _DX     dw      ?
               _CX     dw      ?
               _BX     dw      ?
               _AX     dw      ?
recv_frame     ends


_receiver      proc
;
;      This interrupt procedure is called when the Token Ring
;      card has data.
;
;      On entry, ds:si points to the CCB
;                es:bx points to the receive first_buffer in the chain.
;
;
                pushf                          ; save CPU environment
                push   ax
                push   bx
                push   cx
                push   dx
                push   si
                push   di
                push   ds
                push   es
                push   bp

                push   ax                      ; local variables: size of receive data
                push   ax                      ; segment of user supplied buffer
                push   ax                      ; offset of user supplied buffer
                push   ax

                mov    bp,sp                   ; set stack frame reference

                sti                            ; enable further int activity

                marker R,X
;
;      If the source address is mine, ignore the transmission!
;
               mov     ax,cs
               mov     ds,ax
               mov     si,offset Token_address
               mov     di,bx
               add     di,user_data+source     ; ds:si -> token address
                                               ; es:di -> pkt
               mov     cx,EADDR_LEN
               push    word ptr es:[di]        ; save msb of address

               mov     al,es:[di]
               and     al,not_SA_mask          ; drop SA indicator
               mov     es:[di],al              ; fix the bit during compare

               repe    cmpsb                   ; do string compare now
               jne     not_mine

               mov     di,bx
               add     di,user_data+source
               pop     word ptr es:[di]        ; restore token address

               jmp     drop_buffer             ; from me, ignore it!

;      es:bx -> 1st receive buffer
not_mine:
               mov     di,bx
               add     di,user_data+source
               pop     word ptr es:[di]        ; save MSB of source address

               xor     ax,ax
               mov     cx,ax
               mov     word ptr [bp][RIF_size],ax ; zero RIF size value

get_size:
               mov     ax,es
               or      ax,bx
               je      got_size
               add     cx,word ptr es:[bx][data_size]
               push    word ptr es:[bx][next_buffer]
               push    word ptr es:[bx][next_buffer+2]
               pop     es                      ; ************************
               pop     bx                      ; ************************
               jmp     get_size

got_size:
               sub     cx,LLC_info_size        ; adjust size for Token

               mov     ax,word ptr [bp][_ES]
               mov     es,ax
               mov     bx,word ptr [bp][_BX]   ; get buffer address
               mov     al,es:[bx][user_data+source]
               and     al,SA_mask              ; if SA, adjust total
               je      got_no_fix

;
;      How big is the RIF?
;
               mov     ax,word ptr es:[bx][user_data+source+EADDR_LEN]
               and     ax,RIF_size_mask
               mov     word ptr [bp][RIF_size],ax
;
;      Take away size of RIF.  Upper level doesn't get it.
;
               sub     cx,ax                   ; subtract RIF size

got_no_fix:
;
;      Min Ethersize is RUNT, so check it first, round up if necessary
;
               cmp     cx,RUNT
               jge     no_adjust_cx
               mov     cx,RUNT

no_adjust_cx:
               mov     word ptr [bp][user_size],cx ; save size of receive data
;
;      Call recv_find to determine if the receiver wants the data,
;      and where it should be stored.
;
               mov     ax,word ptr [bp][_ES]   ; reload segment/offset
               mov     es,ax
               mov     di,word ptr [bp][_BX]   ; points to E-type in Token
               add     di,40                   ; assumes Buffer 1 format,
               add     di,word ptr [bp][RIF_size] ; RIF size added now...
;
;      if ARP type, convert hardware address space value back to Ethernet...
;
               mov     ax,word ptr es:[di]     ;  -> EtherType in SNAP
               cmp     ax,ARP_type             ; ARP packet?
               jne     not_rx_arp
               mov     ax,ARP_Eth_hwr
               mov     word ptr es:[di][2],ax  ; fix 1st field of data

not_rx_arp:
               call    recv_find               ; es:di -> Snap E-Type, CX = data size

               mov     word ptr [bp][user_buff],di
               mov     ax,es
               mov     word ptr [bp][user_buff+2],ax

               or      ax,di
               jne     do_copy
               jmp     drop_buffer             ; if ptr is 0, doesn't want it!
;
;      Copy Token Ring data to users Ethernet format receive buffer.
;
do_copy:
;
;      Since the upper level wants it, better keep the RIF info in case we
;      want to send a return message.
;
               mov     ax,word ptr [bp][_ES]
               mov     ds,ax
               mov     si,word ptr [bp][_BX]
               add     si,user_data+source     ; 1st byte of source address
               call    add_entry               ; do table maintenance
;
;      Copy data to the users buffer
;
               mov     di,word ptr [bp][user_buff] ; Ethernet destination buffer
               mov     ax,word ptr [bp][user_buff+2]
               mov     es,ax

               mov     si,word ptr [bp][_BX]   ; Token Ring Source buffer
               mov     bx,si
               mov     ax,word ptr [bp][_ES]
               mov     ds,ax
               add     si,user_data            ; offset to user data
;
;      ds:si points to 1st token buffer
;      es:di points to ethernet format buffer

;      1. Copy Token/Ether  dest/source field to users buffer
;         after stripping off the SA bit in the source address.

               mov    al,byte ptr ds:[si+6]    ;****************
               and    al,07Fh                  ;***** NEW ******
               mov    byte ptr ds:[si+6],al    ;****************

               mov    cx,EADDR_LEN*2           ; 2 address fields
               add    si,2                     ; offset to dest field
               cld
               rep     movsb

               add     si,LLC_info_size-2      ; assume no RIF,
               add     si,word ptr [bp][RIF_size] ; compensate for RIF

               movsw                           ; get ethertype

               mov     cx,[bx][data_size]      ; get length of data
               sub     cx,MAC_hdr_size         ; drop MAC header stuff
               sub     cx,[bp][RIF_size]       ; compensate for RIF

copy_buffer:
               cld
               rep     movsb                   ; copy rest of data to Ether..
               push    word ptr [bx][next_buffer]
               push    word ptr [bx][next_buffer+2]
               pop     ds
               pop     si
               mov     bx,si                   ; bx is base of buffer
               add     si,user_data2           ; si offsets to user data
               mov     cx,[bx][data_size]      ; length of user data
               mov     ax,ds
               or      ax,bx                   ; NULL pointer?
               jne     copy_buffer             ; no, keep copying...
;
;      Tell Upper layer I copied the data into his buffer...
;
               mov     si,word ptr [bp][user_buff]
               mov     ax,word ptr [bp][user_buff+2]
               mov     ds,ax
               mov     cx,word ptr [bp][user_size]

               call    recv_copy
;
;      _ES:_BX points to 1st receive buffer, drop it, then exit
;
drop_buffer:
               mov     ax,word ptr [bp][_ES]   ; tell Token, drop buffer
               mov     bx,word ptr [bp][_BX]
               push    ax
               push    bx
               call    buffer_free             ; seg:offs of 1st buff on stack

               pop     ax                      ; vacuum local variables off
               pop     ax                      ; the stack
               pop     ax
               pop     ax

               pop     bp
               pop     es
               pop     ds
               pop     di
               pop     si
               pop     dx
               pop     cx
               pop     bx
               pop     ax
               popf

               marker  r,x

               iret

_receiver      endp


comp_adr       proc
;
;      Compare two token address values, set flags without affecting any
;      other registers.  JE or JNE to test result.
;
               push    si
               push    di
               push    cx
               cld
               mov     cx,EADDR_LEN
               repe    cmpsb
               pop     cx
               pop     di
               pop     si
               ret
comp_adr       endp


make_adr       proc
;
;      Given CX as an index into the SA_table (1..n), build the resulting
;      address, in ES:DI.
;
               push    ax
               push    cx
               dec     cx
               xor     di,di
               cmp     cx,0
               je      make_adr1

make_adr0:
               add     di,SIZE SA_blk          ; build index into SA_table
               loop    make_adr0

make_adr1:
               add     di,offset SA_table      ; complete the address
               mov     ax,cs
               mov     es,ax
               pop     cx
               pop     ax
               ret
make_adr       endp



add_entry      proc
;
;      DS:SI -> Token Address, make an entry in the SA routing table
;
               mov     cx,EADDR_LEN            ; don't add broadcasts to table
               push    si                      ; leave the default intact
               cld

add_entry0:
               lodsb                           ; Broadcast?
               cmp     al,broadcast_byte
               loope   add_entry0
               pop     si
               je      add_done                ; yes...

               mov     al,ds:[si]              ; is it a source route address?
               and     al,SA_mask
               je      add_done
;
;      Is it in the table?

               call    loc_entry
               jc      add_done                ; c = 1, its in the table.
;
;      if its not in the table, lets add it, if there is room.
;
               mov     cx,word ptr cs:[SA_index]
               cmp     cx,SA_table_size
               je      add_done                ; table full, ignore new entries...

               inc     cx
               mov     word ptr cs:[SA_index],cx ; advance the table index

;      before entry is made, the broadcast bit must be cleared so bridges
;      will leave the segment values alone.

               mov     al,byte ptr ds:[si+EADDR_LEN]
               and     al,07Fh                 ; clear broadcast bit
               mov     byte ptr ds:[si+EADDR_LEN],al

               call    make_adr                ; build destination address
               push    si
               mov     cx,SIZE SA_blk
               cld
               rep     movsb                   ; copy RIF into table
               pop     si

add_done:
               ret
add_entry      endp




loc_entry      proc
;
;      DS:SI -> Token Address, locate entry in the SA routing table
;      ES:DI -> SA information in table, if carry is set.
;
               mov     cx,word ptr cs:[SA_index]  ; any entries in table?
               cmp     cx,0
               je      loc_done


loc_entry0:
               call    make_adr                ; build address into table
               call    comp_adr                ; compare Token Addresses
               je      loc_found               ; if matches, in table
               loop    loc_entry0

loc_done:
               clc
               ret

loc_found:                                     ; ES:DI points to matching entry
               stc
               ret

loc_entry endp


;==============================================================================;
;=================== DRIVER CODE  =============================================;
;==============================================================================;
;
;      Token Ring Card number, parsed from command line
;
token_card     dw      0                       ; default adapter number 0
               dw      0                       ; this prevents type error.

toke_ad_name   db      "Adapter (0 or 1) ",'$'

dir_open_mode  db      0                       ; set <> 0 if modify_open used
msg_modify     db      "  ERROR:  Card Already in Use.",CR,LF
               db      "          REBOOT and try again.",CR,LF
               db      '$'
msg_initialize db      "INSTALL: initializing the adapter...",CR,LF,'$'
msg_opening    db      "         opening the adapter...",CR,LF,'$'
msg_complete   db      "         Adapter initialization is complete.",CR,LF,'$'
msg_bad_adapter db     "ERROR: adapter must be 0 or 1.",CR,LF,'$'
msg_bad_init   db      "  ERROR: Initialization Failed.",CR,LF
               db      "         Installation aborted.",CR,LF,'$'
msg_receiving  db      "         setting up receive process...",CR,LF,'$'

       public  int_no
int_no db      0,0,0,0                 ;must be four bytes long for get_number.

       public  driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class   db      1               ;from the packet spec
driver_type    db      1               ;from the packet spec
driver_name    db      'IBMTokenR',0   ;name of the driver.
driver_function	db	2
parameter_list	label	byte
	db	1	;major rev of packet driver
	db	9	;minor rev of packet driver
	db	14	;length of parameter list
	db	EADDR_LEN	;length of MAC-layer address
	dw	GIANT	;MTU, including MAC headers
	dw	MAX_MULTICAST * EADDR_LEN	;buffer size of multicast addrs
	dw	0	;(# of back-to-back MTU rcvs) - 1
	dw	0	;(# of successive xmits) - 1
int_num	dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,

       public  rcv_modes
rcv_modes      dw      4               ;number of receive modes in our table.
               dw      0,0,0,rcv_mode_3


sp_frame       struc
__bp           dw      ?
u_size         dw      ?
u_data         dd      ?
u_snap         dw      ?
sp_frame       ends


	public	as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
;   interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
;   es:di and interrupt enable flag preserved on exit.
as_send_pkt:
	ret

	public	drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
	assume	ds:nothing
	ret

	public	xmit
; Process a transmit interrupt with the least possible latency to achieve
;   back-to-back packet transmissions.
; May only use ax and dx.
xmit:
	assume	ds:nothing
	ret


               public  send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
               assume  ds:nothing
               sti
               marker  T,X

;
;      construct local stack frame
;
               mov     ax,word ptr ds:[si][ether_type]
               push    ax
               push    ds                      ; pointer to user data packet
               mov     ax,si
               add     ax,SIZE ether_hdr
               push    ax                      ; adjusted userdata pointer
               sub     cx,SIZE ether_hdr       ; adjust size for send
               push    cx
               push    bp
               mov     bp,sp                   ; use sp_frame for reference


               mov     ax,cs                   ; set dest pointer to LLC header
               mov     es,ax

               mov     di,offset lan_header.dest
               mov     cx,EADDR_LEN*2          ; copy dest|source fields
               cld
               rep  movsb
;
;      ds:si -> dest address, locate it in the SA RIF table
;
               mov     ax,cs
               mov     ds,ax
               mov     si,offset lan_header.dest ; building LAN header info here

; Set bit 7 of address, force lookup in table.  If it is found, add RIF,
; otherwise, assume the local net.

               mov     al,byte ptr ds:[si]       ; ********************
               or      al,080h                   ; ****** NEW *********
               mov     byte ptr ds:[si],al       ;*********************

               call loc_entry
               jc   send_pkt1                  ; yes, copy out RIF information
;
;      add the rest of the fields to the LLC header, and set the size
;      for the transmit call.
;
               mov     al,byte ptr ds:[si]     ; dest address 1st byte,
                                               ; lop off SA bit
               and     al,not_SA_mask
               mov     byte ptr ds:[si],al

               mov     al,byte ptr ds:[si][EADDR_LEN]  ; force U bit high
               or      al,080h
               mov     byte ptr ds:[si][EADDR_LEN],al

               add     si,EADDR_LEN*2          ; point after dest/source addrs.
               mov     ax,es
               mov     bx,ds
               mov     ds,ax
               mov     es,bx
               xchg    si,di

;      addresses acquired from ARP default to broadcast mode, using phony
;      RIF info so the bridge will pass the packet along.
;
               mov     ax,phony_RIF            ; copy phony RIF to LLC buffer,
               cld                             ; so 'local' can cross bridge..
               stosw
               jmp     send_pkt2

send_pkt1:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;
;      source/dest address need to be adjusted for routing.
;
               mov     cx,EADDR_LEN
               push    si

send_pkt3:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
               mov     al,byte ptr ds:[si]
               inc     si
               cmp     al,broadcast_byte
               loope   send_pkt3
               pop     si
               je      send_pkt4               ; broadcast address?


               mov     al,byte ptr ds:[si]     ; dest, lop off SA bit
               and     al,not_SA_mask
               mov     byte ptr ds:[si],al

send_pkt4:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
               mov     al,byte ptr ds:[si][EADDR_LEN]
               or      al,SA_mask              ; set SA bit for RIF
               mov     byte ptr ds:[si][EADDR_LEN],al

               add     si,EADDR_LEN*2          ; ds:si -> dest for RIF info.
               add     di,EADDR_LEN            ; es:di -> RIF info, addr fld

               mov     bx,si                   ; keep RIF pos in LLC header

               mov     ax,word ptr es:[di]     ; get rif info
               and     ax,RIF_size_mask        ; get length of RIF
               mov     cx,ax
               mov     ax,es
               mov     dx,ds
               mov     ds,ax
               mov     es,dx
               xchg    si,di
               cld
               rep     movsb

;
;      Got RIF, fix direction bit.
;
               mov     al,es:[bx][1]           ; RIF+1 = dir bit location
               xor     al,RIF_dir_bit          ; change direction
               mov     es:[bx][1],al

send_pkt2:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;
;      es:di -> target in LLC for the rest of the header,
;      add LLC and SNAP information.
;
               mov     ax,LLC_SSAP_DSAP        ; build LLC portion
               stosw
               mov     al,LLC_CON
               stosb
               xor     ax,ax                   ; 3 bytes for Ptype in SNAP
               stosw
               stosb
               mov     ax,word ptr [bp][u_snap]
               stosw
;
;      header is complete.
;
               sub     di,offset lan_header    ; calculate size of LLC header
;
;      stack parameters for Transmit
;
               pop     bp

               mov     ax,cs
               mov     ds,ax                   ; set ds for transmit entry
               push    ax
               mov     ax,offset lan_header
               push    ax
               push    di

;--------------------------------------------------------------------
;       If Users data is an ARP packet, 0806 type, then the 1st field
;       must be changed from 0001 to 0006 for Token Ring broadcast, or
;       no one will respond.  Funny, eh?  Another thing.  Since an ARP
;       packet is smaller than the min size for Ethernet, the caller rounds
;       up the size.  I have to change it back, or some Token hosts won't
;       ARP for me.  The nerve!
;
               push    bp
               mov     bp,sp
               mov     ax,word ptr [bp][14]    ; offset to ARP type on stack...
               pop     bp

               cmp     ax,ARP_type             ; network byte order is backwards
               jne     not_tx_arp
;
;      The first field of the ARP data has to be changed from 0001 to 0006
;
               push    bp                      ; use trans_frame
               push    bp                      ; dummy entry on stack...
               mov     bp,sp
               mov     ax,word ptr [bp][user_daddress+2] ; addr of user data
               mov     es,ax
               mov     di,word ptr [bp][user_daddress]

               mov     ax,ARP_Tok_hwr
               mov     word ptr es:[di],ax     ; change ARP type to 0006

               mov     ax,ARP_packet_size      ; 28  bytes in an ARP packet
               mov     word ptr [bp][user_dsize],ax ; reset users data size

               pop     bp                      ; pop fake return address
               pop     bp                      ; pop real bp

not_tx_arp:
               call    transmit_dir_frame      ; send the packet

               pop     ax                      ; vacuum
               clc
               ret


               public  get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
               assume  ds:code
;
               push    di
               push    es
               push    cx

               mov     ax,cs
               mov     ds,ax
               clr_struc ccb
               clr_struc toke_status

               mov     al,get_status_cmd       ; get status returns address
               mov     ccb.command,al
               mov     ax,offset toke_status
               mov     word ptr [ccb.parms],ax
               mov     ax,ds
               mov     word ptr [ccb.parms+2],ax

               call_token ccb
               call_wait  ccb

;Andr'e PIRARD suggested changing the following line:
;was           mov     si,offset toke_status.encoded_addr
               mov     si,offset toke_status.node_adrs
               pop     cx
               pop     es
               pop     di
               cmp     cx,EADDR_LEN            ; got the room for it?
               jb      get_address_fail

               cld
               mov     cx,EADDR_LEN
               rep     movsb
               mov     cx,EADDR_LEN
               clc
               ret

get_address_fail:
               stc
               ret


               public  set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
               assume  ds:nothing
               clc
               ret


rcv_mode_3:
;receive mode 3 is the only one we support, so we don't have to do anything.
       ret


       public  set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
       mov     dh,NO_MULTICAST
       stc
       ret


	public	terminate
terminate:
	ret

               public  reset_interface
reset_interface:
;reset the interface.
               assume  ds:code
               ret


;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
               extrn   recv_find: near

;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
               extrn   recv_copy: near

               extrn   count_in_err: near
               extrn   count_out_err: near

               public  recv
recv:
;called from the recv isr.  All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
               assume  ds:code
               ret


       public  recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
       assume  ds:nothing
       ret




;any code after this will not be kept after initialization.
end_resident   label   byte


               public  usage_msg
usage_msg      db      CR,LF
               db      "usage: IBMTOKEN [-d -n] <packet_int_no> <adapter>",CR,LF,LF
               db      "where: packet_int_no     is the interrupt vector",CR,LF
               db      "       adapter           is adapter 0 or 1",CR,LF,CR,LF
               db      " note: IBM LAN Support Program required.",CR,LF,'$'

               public  copyright_msg
copyright_msg  db      "*************************************************",CR,LF
               db      "*   Ethernet Packet Driver for IBM Token Ring   *",CR,LF
               db      "*                                               *",CR,LF
               db      "*   3C501 Emulation Mode, Version 1.",'0'+version
               db      "           *",CR,LF
               db      "*   portions -                                  *",CR,LF
               db      "*   Copyright 1989, Queens University           *",CR,LF
               db      "*   Computing and Communications Services       *",CR,LF
               db      "*   Written by Brian Fisher                     *",CR,LF
               IF  debug NE 0
               db      "*   DEBUG VERSION                               *",CR,LF
               ENDIF
               db      "*************************************************",CR,LF
               db      LF,LF
               db      '$'

               extrn   set_recv_isr: near

;enter with si -> argument string, di -> wword to store.
;if there is no number, don't change the number.
               extrn   get_number: near

               public  parse_args
parse_args:
;
;      si points to next argument of command line...
;
               mov     bx,offset toke_ad_name  ; next argument is card number
               mov     di,offset token_card    ; default is zero
               call    get_number
               ret


               public  etopen
;
;      Initialize the IBM Token Ring Adapter, or use
;      Modify Open if its already in use.
;
etopen:
               mov     ax,cs
               mov     ds,ax

               mov     ax,token_card           ; check token card for range
               cmp     ax,1
               jg      et_error

               call    dir_modify_open
               cmp     ax,9
               jne     et_modify_error         ; can't modify open adapter!

do_init:
               print$  msg_initialize          ; initialize the adapter
               call    dir_initialize
               cmp     ax,0
               jne     et_init_error

               print$  msg_opening             ; open the adapter
               call    dir_open_adapter
               cmp     ax,0
               jne     et_init_error

no_init:
               print$  msg_receiving           ; set up receive routines
               call    open_receive
               cmp     ax,0ffh
               jne     et_init_error           ; no receive open...

               print$  msg_complete            ; card ready to go


               mov     ax,cs
               mov     es,ax                   ; get Token Address for _receiver
               mov     di,offset Token_address
               mov     ds,ax
               mov     cx,6
               call    get_address

all_ok:
;if all is okay,
               mov     dx,offset end_resident
               clc
               ret

	public	print_parameters
print_parameters:
	ret


et_error:
;if we got an error,
               print$  msg_bad_adapter
               stc
               ret

et_init_error:
               call    dir_close_adapter       ; make sure adapter is closed
               print$  msg_bad_init
               stc
               ret

et_modify_error:
               print$  msg_modify
               stc
               ret

code           ends
               end
