home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
MODEMS
/
MODEM
/
X25.ARK
/
LEVEL3.ASM
next >
Wrap
Assembly Source File
|
1986-07-28
|
55KB
|
2,218 lines
title 'LEVEL3.ASM'
;************************************************
;* *
;* LEVEL3.ASM *
;* *
;* X.25 level 3 (packet) protocol handler *
;* (implements single logical channel DTE *
;* for use in a Virtual Call circuit (VC) *
;* or Permanent Virtual Circuit (PVC)) *
;* *
;* rev 1.30 08/21/84 E. Elizondo *
;* *
;* (c) 1984 E. Elizondo - all rights reserved. *
;* *
;* This program may be used freely for non- *
;* commercial applications. It may not be sold *
;* or used for commercial applications without *
;* written permission of the author. *
;* *
;************************************************
;
maclib Z80 ;DR Z80 macro library
; assembly time options
false equ 0
true equ not false
debug equ false ;display P(s) value
; design parameters
n3 equ 2 ;retry count for level 3 timeout
kpack equ 4 ;acknowledgement delay count
; X.25 standard parameters:
; logical group & channel number (para 2.4.1)
lchan equ 1 ;logical channel 1
lgroup equ 0 ;logical group 0
; flow control parameters:
wsize equ 2 ;window size (para 4.4.1.2)
; packet type identifiers (table 8/X.25)
; bit # 8765$4321
calrid: equ 0000$1011b ;call request
calaid: equ 0000$1111b ;call accepted
clrrid: equ 0001$0011b ;clear request
clrcid: equ 0001$0111b ;clear confirmation
intid: equ 0010$0011b ;interrupt
intcid: equ 0010$0111b ;interrupt confirmation
rrid: equ 0000$0001b ;RR
rnrid: equ 0000$0101b ;RNR
rstrid: equ 0001$1011b ;reset request
rstcid: equ 0001$1111b ;reset confirmation
starid: equ 1111$1011b ;restart request
stacid: equ 1111$1111b ;restart confirmation
diagid: equ 1111$0001b ;diagnostic
; misc constants
cr equ 0dh ;carriage ret
lf equ 0ah ;line feed
; hooks for main program
; subroutines
public initl3 ;initialize level 3 parameters
public txdpk ;transmit data packet if avail
public rxpk ;receive packet if available
public txintp ;transmit interrupt packet
public txstar ;transmit restart packet
public txclrr ;transmit clear request packet
; addresses
public chstat ;level 3 logical channel state
public l3stat ;level 3 flow control flags
; level 3 parameters
public ps ;P(s)
public pr ;P(r)
public lastpr ;last rx P(r)
public ltxpr ;last tx P(r)
public lrxps ;last rx P(s)
public ltxps ;last tx P(s)
public pvcmod ;PVC mode flag
public laddr ;local DTE address
public raddr ;remote DTE address
public caddr ;rx calling DTE address
public laddrl ;local DTE address length
public raddrl ;remote DTE address length
public caddrl ;rx calling DTE address length
public qbit ;Q bit
public dbit ;D bit
public chan ;logical channel #
public group ;logical group #
; diagnostic counters
public txpct ;total tx packets
public txfpct ;total tx file data packets
public txcpct ;total tx console data packets
public ntxbct ;errors due to no free tx buffer
;
public rxpct ;total rx packets
public rxdpct ;total rx data packets
public rbfpct ;total rx bad format packets
public rbcpct ;total rx bad channel packets
public rbapct ;total rx bad address packets
public rbipct ;total rx bad id packets
public rbgpct ;total rx bad group packets
public rxxpct ;total discarded rx packets
; definition of channel status (chstat) bits
; (note that state r1 (packet level ready)
; corresponds to level 2 link conn)
; bit set condition
; 0 flow control ready state (d1)
; 1 DTE restart request state (r2)
; 2 DTE waiting state (p2)
; 3 DTE reset request state (d2)
; 4 DTE clear request state (p6)
; 5 ready (p1)
; 6 undefined
; 7 undefined
; definition of flow control (l3stat) status bits
; bit set condition
; 0 DTE busy
; 1 DCE busy
; 2 DTE interrupt pending confirmation
; 3 undefined
; 4 undefined
; 5 ualled DTE address error semaphore (internal)
; 6 transmission completed - ready to clear
; 7 outgoing message waiting for call setup
; external subroutines
; from buffers module:
extrn inibuf ;initialize all buffers
extrn putbuf ;put char in buffer
extrn getbuf ;get char from buffer
extrn topbuf ;put char at beginning of buffer
extrn bpoint ;point to selected bcb
extrn rlsrxb ;release rx buffer
extrn clrbuf ;clear buffer for new use
extrn getbct ;get buffer count
extrn getrdy ;get state of buffer ready flag
extrn setrdy ;set buffer ready flag
extrn clrrdy ;clear ready flag
; from level1 module:
extrn t20on ;turn on timer T20
extrn t20off ;turn off timer T20
extrn t21on ;turn on timer T21
extrn t21off ;turn off timer T21
extrn t22on ;turn on timer T22
extrn t22off ;turn off timer T22
extrn t23on ;turn on timer T23
extrn t23off ;turn off timer T23
; from level2 module:
extrn gettxb ;get address of free tx bcb
; from files module
extrn gfdata ;get byte from transmit file
extrn pfdata ;write file to receive file
extrn ctxfil ;close transmit file
extrn crxfil ;close receive file
; from xutil module:
extrn ilprt ;in line print routine
extrn ctype ;print char in <a>
extrn pdec ;print <hl> in decimal
extrn phex ;print <a> in hex
extrn phex1 ;print nibble in <a> in hex
extrn delay ;wait a bit
; external addresses
; from level 1 module
extrn tistat ;timer status flags
extrn rxstat ;receive status
; from level 2 module
extrn lkstat ;level 2 status flags
; from buffers module
extrn rxplst ;A(list of rx packt buffer #'s)
extrn rxbtab ;A(table of rx bcb pointers)
extrn txbtab ;A(table of tx bcb pointers)
extrn ctbcb ;A(bcb for console transmit)
extrn crbcb ;A(bcb for console receive)
; from files module
extrn fstat ;file status flags
; from x25 module
extrn l4stat ;level 4 status flags
cseg ;code section
; **************************
; * initialization section *
; **************************
; initialize level 3 parameters
; (externally and internally called)
; on entry: no parameters
; on exit: all regs, flags clobbered
initl3:
mvi a,lchan ;get default channel number
sta chan ;and initialize logical channel #
mvi a,lgroup ;get default group number
sta group ;and initialize logical group #
mvi a,0000$0001b ;get modulo 8 general format id
sta gfi ;initialize gfi
call reset ;reset flow control variables
xra a
sta qbit ;clear Q bit
sta dbit ;clear D bit
sta chstat ;clear logical channel state
sta l3stat ;channel in ready (p1) state
ret
; reset flow control variables
; (internally called)
; on entry: no parameters
; on exit: <a>,flags clobbered
; all other regs unchanged
reset: xra a
sta pr ;clear P(r)
sta ps ;clear P(s)
sta lastpr ;clear last rx P(r)
sta ltxpr ;and last tx P(r)
mvi a,7 ;initialize last rx P(s)
sta lrxps ; /
sta ltxps ;and last tx P(s)
ret
****************************
; * packet transmit section *
; ****************************
; check and process timeout condition or
; transmit data packet if available and
; a) Level 3 timers not timed out
; b) level 2 link is connected
; c) level 3 in data transfer ready state
; d) DCE not busy
; e) new frame P(s) is within window
; f) free tx buffer available
; and either g) transmit file is open
; or h) console transmit buffer is ready
; (file data takes precedence, as it's
; transmitted as a continuous sequence)
; if no I is available, transmit RR or RNR if any
; received I packets need acknowledgement
; (externally called)
; on entry: no parameters
; on exit: all regs, flags clobbered
;
txdpk:
lxi h,tistat ;point to timer status flags
bit 1,m ;timer T20 timed out?
jnz t20to ;yes, service it
;
bit 2,m ;timer T21 timed out?
jnz t21to ;yes, service it
;
bit 3,m ;timer T22 timed out?
jnz t22to ;yes, service it
;
bit 4,m ;timer T23 timed out?
jnz t23to ;yes, service it
;
lxi h,lkstat ;point to level 2 status
bit 2,m ;link connected?
rz ;no, return
;
lxi h,chstat ;point back to channel status
bit 5,m ;ready state (p1)?
jnz setup ;yes, see if we want to call
;
bit 0,m ;flow control ready state (d1)?
rz ;no, return
;
lxi h,l3stat ;point to level 3 status
bit 1,m ;DCE busy?
rnz ;yes, return
;
bit 6,m ;message complete?
jnz clear ;yes, see if we want to clear
;
;
; did we transmit data packet already?
lda ltxps ;get last tx P(s)
mov b,a ;save in <b>
lda ps ;get current ps
cmp b ;same?
jz txackp ;yes, we did
;
; check that P(s) is within window
lda lastpr ;get last rx P(r)
adi wsize+1 ;calculate just past window
ani 7 ;..mod 7
mov b,a ;save in <b>
lda ps ;get P(s)
cmp b ;is P(s)=past top of window?
jz txackp ;yes, don't transmit it yet
;
txdp0: lxi h,fstat ;point to file status
bit 1,m ;tx file open?
jz txcdpk ;no, see if console packet avail
;
; transmit disk file data packet
; (default to category A packet)
call gettxb ;else get free tx bcb
rc ;return if none avail
;
xra a ;clear <a>
sta qbit ;clear Q bit
sta dbit ;clear D bit
call octet1 ;assemble octet 1 in <a>
call putbuf ;put octet 1 in tx buffer
lda chan ;get logical channel #
call putbuf ;put octet 2 in tx buffer
call octet3 ;assemble octet 3 in <a>
setb 4,a ;set M=1 for now
call putbuf ;store octet 3 in tx buffer
; fill up user data field in packet
mvi b,128 ;max user data octets in packet
txdp1:
call gfdata ;get data byte from file
jz txdp2 ;exit if end of file
call putbuf ;else put in buffer
dcr b ;is packet full?
jnz txdp1 ;no, keep going
;
jmp txdp3 ;else exit
;
; end of file - make into category B packet
txdp2:
call getbuf ;get octet 1
rc ;exit if not there
;
mov b,a ;save in <b>
call getbuf ;get octet 2
rc ;exit if not there
;
mov c,a ;save in <c>
call getbuf ;get octet 3
rc ;exit if not there
;
res 4,a ;set M=0
call topbuf ;put back octet 3
mov a,c ;get octet 2
call topbuf ;and put it back
mov a,b ;get octet 1
setb 6,a ;set D=1
call topbuf ;and put it back
call ctxfil ;close tx file
; common exit routine
txdp3:
call txgo ;transmit packet
lda ps ;P(s)=P(s)+1 mod 7
;
if debug ;if debug mode
call prtps ;print P(s)
endif
;
sta ltxps ;update last tx P(s)
inr a ;increment P(s)
ani 7 ; /
sta ps ;update P(s)
lhld txfpct ;increment tx file packet count
inx h ; /
shld txfpct ; /
mvi a,kpack ;initiaize ack delay count
sta packct ; /
call delay ;and wait a bit
ret
; transmit console data packet if available
; (internally called)
; on entry: no parameters
; on exit: all regs, flags clobbered
;
txcdpk:
lxi h,ctbcb ;point to console transmit bcb
call getrdy ;is it ready?
jz txackp ;no, see if we want to acknowledge
;
call gettxb ;else get free tx bcb
rc ;return if none avail
;
; this is a category B packet
push h ;save bcb address
lxi h,qbit ;point to Q bit
res 7,m ;set Q=0
lxi h,dbit ;point to D bit
;*** setb 6,m ;set D=1 (if desired)
pop h ;restore bcb address
call octet1 ;assemble octet 1 in <a>
call putbuf ;put octet 1 in tx buffer
lda chan ;get logical channel #
call putbuf ;put octet 2 in tx buffer
call octet3 ;build octet 3 in <a> with M=0
call putbuf ;store in tx buffer
mvi b,128 ;max user data octets in packet
; fill user data fields from console tx buffer
xchg ;save <hl> in <de>
lxi h,ctbcb ;point to console transmit bcb
txcdp1:
call getbuf ;get data byte from console buffer
jc txcdp2 ;exit if end of buffer
xchg ;restore tx buffer bcb address
call putbuf ;else put into tx buffer
xchg ;point back to console xmit bcb
dcr b ;is packet full?
jnz txcdp1 ;no, keep going
;
; common exit routine
txcdp2:
call clrrdy ;clear console xmit buffer flag
xchg ;point back to tx buffer bcb
call txgo ;transmit packet
lda ps ;P(s)=P(s)+1 mod 7
;
if debug
call prtps ;print P(s)
endif
;
sta ltxps ;update last tx P(s)
inr a ;incrment P(s)
ani 7 ; /
sta ps ;update P(s)
lxi h,l4stat ;point to level 4 status
setb 0,m ;signal prompt for next packet
lhld txcpct ;increment tx console packet count
inx h ; /
shld txcpct ; /
lxi h,l3stat ;point to flow status
res 7,m ;reset message waiting flag
mvi a,kpack ;initialize ack delay count
sta packct ; /
ret
; transmit acknowledgement (RR or RNR) packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txackp:
lda ltxpr ;get last tx P(r)
mov b,a ;save in <b>
lda pr ;get current P(r)
cmp b ;same?
rz ;yes, nothing to acknowledge
;
; check delay count to see if any data packets are coming
lda packct ;get delay count
dcr a ;decrement it
sta packct ; /
rnz ;wait a bit till it's 0
;
; delay completed, transmit acknowledge packet
mvi a,kpack ;initialize delay count
sta packct ; /
lxi h,l3stat ;point to level 3 flow status
bit 0,m ;DTE busy?
jnz txrnr ;yes, transmit RNR packet
;
jmp txrr ;else transmit RR packet
; print P(s)
; (internally called)
; on entry: <a>=P(s)
; on exit: all flags, regs unchanged
prtps: push psw ;save P(s)
mvi a,'['
call ctype
pop psw ;restore P(s)
push psw ;and save it again
adi '0' ;convert to ASCII
call ctype ;display it
mvi a,']'
call ctype
pop psw ;restore P(s)
ret
; set up call if in VC mode
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
setup:
lxi h,chstat ;point to channel status
bit 2,m ;DTE waiting state (p2)?
rnz ;yes, do nothing
;
lxi h,l3stat ;point to flow status
bit 7,m ;message ready?
rz ;no, return
;
; ready to set up call
res 7,m ;reset message waiting flag
lxi h,chstat ;point back to channel status
bit 0,m ;flow control ready state (d1)?
rnz ;yes, no setup needed
;
jmp txcr ;else transmit call request packet
; clear call if in VC mode
; (internally called)
clear:
lxi h,l3stat ;point to level 3 status
res 6,m ;reset message complete flag
lda pvcmod ;get VC/PVC mode flag
cpi 2 ;PVC?
rz ;yes, do nothing if PVC
;
jmp txclrr ;else transmit clear request packet
; transmit call request packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txcr:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,calrid ;get call request id
call put3oct ;put octets 1-3 in tx buffer
call putaddr ;put address octets in tx buffer
;*** (no facilities or call user data implemented)
mvi a,0 ;facilities length=0
call putbuf ;put octet in tx buffer
call txgo ;transmit packet
call reset ;reset flow control variables
call t21on ;start timer T21
lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state?
rnz ;yes, all done
;
setb 2,m ;else signal DTE waiting state (p2)
ret
; transmit call accepted packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txca:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,calaid ;get call accepted id
call put3oct ;put octets 1-3 in tx buffer
call putaddr ;put address octets in tx buffer
;*** (no facilities implemented)
call txgo ;transmit packet
ret
; transmit clear request packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txclrr:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,clrrid ;get clear request id
call put3oct ;put octets 1-3 in tx buffer
mvi a,0 ;clearing cause=0 for DTE
call putbuf ;put octet 4 in tx buffer
call txgo ;transmit packet
call reset ;reset flow control variables
call t23on ;start timer T23
lxi h,chstat ;indicate DTE clear reqest state (P6)
setb 4,m
call ctxfil ;close transmit file
call crxfil ;and receive file
ret
; transmit clear confirmation packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txclrc:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,clrcid ;get clear confirmation id
call put3oct ;put octets 1-3 in tx buffer
call txgo ;transmit packet
ret
; transmit interrupt packet
; (externally called)
; on entry: <b>=interupt user data for octet 4
; on exit: all flags, regs clobbered
txintp: lxi h,lkstat ;point to level 2 status
bit 2,m ;link connected?
jnz txint1 ;yes, keep going
;
call ilprt ;else tell operator
db 'L3: link not connected - ',0
jmp txint5 ;and exit
;
txint1: lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state?
jnz txint2 ;yes, keep going
;
call ilprt ;else tell operator
db 'L3: link not in data xfer state - ',0
jmp txint5 ;and exit
txint2: lxi h,l3stat ;point to level 3 flow status
bit 2,m ;interrupt already pending
jz txint3 ;no, keep going
;
call ilprt ;else tell operator
db 'L3: DTE interrupt is pending - ',0
txint5: call ilprt
db 'cannot send interrupt',cr,lf,0
ret
txint3: call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,intid ;get interrupt id
call put3oct ;put octets 1-3 in tx buffer
mov a,b ;get interrupt user data
call putbuf ;put octet 4 in tx buffer
call txgo ;transmit packet
lxi h,l3stat ;point to level 3 flow status
setb 2,m ;set interrupt pending flag
ret
; transmit interrupt confirmation packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txintc:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,intcid ;get interrupt confirmation id
call put3oct ;put octets 1- 3 in tx buffer
call txgo ;transmit packet
ret
; transmit RR packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txrr:
mvi c,rrid ;<c>=RR packet identifier
jmp txrcom ;and do common stuff
; transmit RNR packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txrnr:
mvi c,rnrid ;<c>=RNR packet identifier
;
; common stuff for RR and RNR
; on entry: <c>=packet identifier
txrcom: call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
xra a ;clear Q and D bits
sta qbit ; /
sta dbit ; /
call octet1 ;assemble octet 1 in <a>
call putbuf ;put octet 1 in tx buffer
lda chan ;get logical channel #
call putbuf ;put octet 2 in tx buffer
lda pr ;get P(r)
sta ltxpr ;update last tx P(r)
rlc ;move to bits 8-6
rlc ; /
rlc ; /
rlc ; /
rlc ; /
ani 1110$0000b ;zero other bits
ora c ;merge with P(r)
call putbuf ;put octet 3 in tx buffer
call txgo ;transmit packet
ret
; transmit reset request packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txrstr: call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,rstrid ;get reset request id
call put3oct ;put octets 1-3 in tx buffer
mvi a,0 ;resetting cause=0 for DTE
call putbuf ;put octet 4 in tx buffer
call txgo ;transmit packet
call reset ;reset flow control variables
call t22on ;start timer T22
lxi h,chstat ;point to channel status
setb 3,m ;signal reset request state
ret
; transmit reset confirmation packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txrstc: call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,rstcid ;get reset confirmation id
call put3oct ;put octets 1-3 in tx buffer
call txgo ;and transmit packet
ret
; transmit restart request packet
; (internally & externally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txstar: lxi h,lkstat ;point to level 2 status
bit 2,m ;link connected?
jnz txstar1 ;yes, keep going
;
call ilprt ;else tell operator
db 'L3: link not connected',cr,lf,0
ret
;
txstar1:
call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,starid ;get restart request packet id
call txscom ;do common stuff
mvi a,0 ;set restarting cause=0 for DTE
call putbuf ; /
call txgo ;transmit packet
call reset ;reset flow control variables
lxi h,chstat ;point to channel status
setb 1,m ;indicate restart request state
call t20on ;start timer T20
call t21off ;and stop all others
call t22off ; /
call t23off ; /
ret
; transmit restart confirmation packet
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
txstac: call gettxb ;get free tx bcb
jc enotxb ;error if none available
;
mvi c,stacid ;get restart confirmation id
call txscom ;do common stuff
call txgo ;transmit packet
ret
; common stuff for restart request/confirmation
; on entry: <c>=packet id
txscom: lda gfi ;get general format identifier
rlc ;move to bits 8-5
rlc ; /
rlc ; /
rlc ; /
ani 1111$0000b ;make sure lower bits are all 0
call putbuf ;put octet 1 in tx buffer
xra a ;next octet is all 0's
call putbuf ;put octet 2 in tx buffer
mov a,c ;get packet identifier
call putbuf ;put octet 3 in tx buffer
ret
; error handling routine for no available free tx buffer
; (internally called)
enotxb:
lhld ntxbct ;increment error count
inx h ; /
shld ntxbct ; /
ret
; *****************************************
; * utility routines for tx section *
; *****************************************
; transmit packet
; (internally called)
; on entry: <hl>=address of tx buffer bcb
; on exit: all regs, flags clobbered
txgo: call setrdy ;set tx buffer ready flag
lhld txpct ;increment total tx packet count
inx h ; /
shld txpct ; /
ret
; put octets 1-3 in tx buffer
; (internally called)
; on entry: <hl>=bcb address of tx buffer
; <c>=packet identifier
; on exit: <a>,flags clobbered
; all other regs unchanged
put3oct:
xra a ;clear Q and D bits
sta qbit ; /
sta dbit ; /
call octet1 ;assemble octet 1 in <a>
call putbuf ;put octet 1 in tx buffer
lda chan ;get logical channel #
call putbuf ;put octet 2 in tx buffer
mov a,c ;get packet identifier
call putbuf ;put octet 3 in tx buffer
ret
; build octet 1
; (internally called)
; on entry: no parameters
; on exit: <a>=octet 1
; flags clobbered
; all other regs unchanged
octet1: push b ;save <bc>
lda gfi ;get general format identifier
rlc ;move to bits 8-5
rlc ; /
rlc ; /
rlc ; /
mov b,a ;save result in <b>
lda qbit ;get q bit
ora b ;merge with gfi
mov b,a ;and save result in <b>
lda dbit ;get d bit
ora b ;merge with gfi
ani 0f0h ;make sure lower 4 bits are 0
mov b,a ;and save result in <b>
lda group ;get logical group #
ani 0fh ;zero upper 4 bits
ora b ;merge with gfi
pop b ;restore <bc>
ret
; build octet 3 of data packet
; (internally called)
; on entry: no parameters
; on exit: <a>= octet 3
; all other regs unchanged
octet3:
push b ;save <bc>
lda pr ;get P(r)
sta ltxpr ;update last tx P(r)
rlc ;move to bits 8-6
rlc ; /
rlc ; /
rlc ; /
rlc ; /
ani 1110$0000b ;zero other bits
mov b,a ;save result in <b>
lda ps ;get P(s)
rlc ;move to bits 4-2
ani 0000$1110b ;zero other bits
ora b ;merge with P(r) and M
pop b ;restore <bc>
ret
; put address octets in tx buffer
; (internally called)
; on entry: <hl>=tx bcb address
; on exit: <a>,flags clobbered
; all other regs unchanged
putaddr:
push b ;save <bc>
push d ;save <de>
;
; build octet 4 in tx buffer
lda laddrl ;get local address length
rlc ;move to bits 8-5
rlc ; /
rlc ; /
rlc ; /
ani 1111$0000b ;make sure lower 4 bits are 0
mov b,a ;save result in <b>
lda raddrl ;get remote address length
ani 0000$1111b ;make sure upper 4 bits are 0
ora b ;merge with local address length
call putbuf ;put octet 4 in tx buffer
;
; now build called DTE address, if present
lda raddrl ;get remote address length
ora a ;length=0?
jz putad2 ;yes, skip remote address
;
mov b,a ;save length in <b>
lxi d,raddr ;point <de> to remote address
putad1: ldax d ;get first char
rlc ;move to bits 8-5
rlc ; /
rlc ; /
rlc ; /
ani 1111$0000b ;make sure lower bits are 0
mov c,a ;save result in <c>
inx d ;point to next address digit
dcr b ;last digit?
jz putad3 ;yes, get local address
;
ldax d ;else get next char
ani 0000$1111b ;make sure upper bits are 0
ora c ;merge with last digit
call putbuf ;and put in tx buffer
inx d ;point to next address digit
dcr b ;last digit
jnz putad1 ;no, get another
;
;
; build calling DTE address, if present
; entry point if called DTE address is even # of digits
putad2: lda laddrl ;get local address length
ora a ;length=0?
jz putadexi ;yes, exit
;
mov b,a ;else save length in <b>
lxi d,laddr ;point <de> to local address
jmp putad4 ;and keep going
;
; entry point if called DTE address is odd # of digits
putad3: lda laddrl ;get local address length
ora a ;length=0?
jz putadexi ;yes, exit
;
mov b,a ;else save length in <b>
lxi d,laddr ;point <de> to local address
jmp putad5 ;and keep going
;
; build msb digit
putad4: ldax d ;get first char
rlc ;move to bits 8-5
rlc ; /
rlc ; /
rlc ; /
ani 1111$0000b ;make sure lower bits are 0
mov c,a ;save result in <c>
inx d ;point to next address digit
dcr b ;last digit?
jnz putad5 ;no, get next character
;
call putbuf ;else put last octet in tx buffer
jmp putadexi ;and exit
;
; build lsb digit
putad5: ldax d ;get next char
ani 0000$1111b ;make sure upper bits are 0
ora c ;merge with last digit
call putbuf ;and put in tx buffer
inx d ;point to next address digit
dcr b ;last digit
jnz putad4 ;no, get another
;
; common exit
putadexi:
pop d ;restore <de>
pop b ;restore <bc>
ret
; *************************
; * process timeouts *
; *************************
; process timeout of DTE timer T20
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
t20to:
call t20off ;turn off timer
lxi h,rtryct ;get retry count
dcr m ;last retry?
jz t20to1 ;yes, other end is dead
;
call ilprt ;else try once more
db 'L3: T20 timed out - '
db 'retransmitting restart request packet',cr,lf,0
call t20on ;restart timer T20
jmp txstar ;and transmit restart
;
t20to1: call ilprt
db 'L3: tx retry count exhausted - '
db 'no reply from DCE',cr,lf,0
ret
; process timeout of DTE timer T21
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
t21to:
call t21off ;turn off timer
call ilprt ;and tell operator
db 'L3: T21 timed out - '
db 'transmitting clear request packet',cr,lf,0
jmp txclrr ;and transmit clear request
; process timeout of DTE timer T22
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
t22to:
call t22off ;turn off timer
lxi h,rtryct ;get retry count
dcr m ;last retry?
jz t22to1 ;yes, other end is dead
;
call ilprt ;else try once more
db 'L3: T22 timed out - '
db 'retransmitting reset request packet',cr,lf,0
call t22on ;restart timer T22
jmp txrstr ;and transmit reset request
;
t22to1: call ilprt
db 'L3: tx retry count exhausted - '
db 'logical channel out of order',cr,lf,0
ret
; process timeout of DTE timer T23
; (internally called)
; on entry: no parameters
; on exit: all flags, regs clobbered
t23to:
call t23off ;turn off timer
lxi h,rtryct ;get retry count
dcr m ;last retry?
jz t23to1 ;yes, other end is dead
;
call ilprt ;else try once more
db 'L3: T23 timed out - '
db 'retransmitting clear request packet',cr,lf,0
call t23on ;restart timer T23
jmp txclrr ;transmit clear request
;
t23to1: call ilprt
db 'L3: tx retry count exhausted - '
db 'logical channel out of order',cr,lf,0
ret
; ***************************
; * packet receive section *
; ***************************
; receive packet if available
; on entry: no parameters
; on exit: <hl>=bcb address if packet avail
; <b>= packet octet # 3 (packet id)
; <c>= rx buffer #
rxpk: lxi h,rxplst ;point to received packet list
call getbuf ;any there?
rc ;no, exit
;
lhld rxpct ;increment rx packet count
inx h ; /
shld rxpct ; /
;
mov c,a ;else save rx buffer # in <c>
lxi h,rxbtab ;get buffer bcb address
call bpoint ; /
call getbuf ;get octet 1
jc rxfmer ;format error if not there
;
mov d,a ;save octet 1 in <d>
call getbuf ;get octet 2
jc rxfmer ;format error if not there
;
mov e,a ;save octet 2 in <e>
call getbuf ;get octet 3
jc rxfmer ;format error if not there
;
mov b,a ;save octet 3 in <b>
cpi starid ;restart request?
jz chkrst ;yes, check format
;
cpi stacid ;restart confirmation
jz chkrst ;yes, check format
;
cpi diagid ;diagnostic
jz chkrst ;yes, check format
;
; process packets having logical group and channel
lda chan ;get logical channel #
cmp e ;match?
jnz rxcher ;no, channel # error
;
mov a,d ;get back octet 1
ani 0011$0000b ;get bits 6-5
cpi 0001$0000b ;numbering modulo 8?
jnz rxfmer ;no, format error
;
mov a,d ;get back octet 1
ani 0000$1111b ;get bits 4-1
push b ;save <bc>
mov b,a ;save logical group # in <b>
lda group ;get our logical group #
cmp b ;same?
pop b ;restore <bc>
jnz rxgrer ;no, group # error
;
; branch to process known packet types
mov a,b ;get back octet 3
bit 0,a ;data packet?
jz rxdata ;yes, process it
;
cpi intid ;interrupt packet?
jz rxint ;yes, process it
;
cpi intcid ;interrupt confirmation?
jz rxintc ;yes, process it
;
cpi rstrid ;reset request?
jz rxrstr ;yes, process it
;
cpi rstcid ;reset confirmation?
jz rxrstc ;yes, process it
;
cpi calrid ;call request?
jz rxcr ;yes, process it
;
cpi calaid ;call accepted?
jz rxca ;yes, process it
;
cpi clrrid ;clear request?
jz rxclr ;yes, process it
;
cpi clrcid ;clear confirmation?
jz rxclc ;yes, process it
;
; process packets numbered modulo 8
rxpk3: ani 0001$1111b ;leave only bits 5-1
cpi rrid ;RR
jz rxrr ;yes, process it
;
cpi rnrid ;RNR?
jz rxrnr ;yes, process it
;
jmp rxpk4 ;else process id error
; check format of restart and diagnostic packets
chkrst: mov a,d ;get back octet 1
cpi 0001$0000b ;modulo 8 format?
jnz rxfmer ;no, format error
;
mov a,e ;get back octet 2
ora a ;all zero?
jnz rxfmer ;no, format error
;
; now branch to known restart packet types
mov a,b ;get back octet 3
cpi starid ;restart?
jz rxstar ;yes, process it
;
cpi stacid ;restart confirmation?
jz rxstac ;yes, process it
;
cpi diagid ;diagnostic?
jz rxdiag ;yes, process it
; else id is unrecognized so drop through to
;
; process unrecognized id error
rxpk4: call flush ;discard packet
lhld rbipct ;increment bad id counter
inx h ; /
shld rbipct ; /
ret
; process packet format error
rxfmer: call flush ;discard packet
lhld rbfpct ;increment bad format counter
inx h ; /
shld rbfpct ; /
ret
; process packet group error
rxgrer: call flush ;discard packet
lhld rbgpct ;increment bad group counter
inx h ; /
shld rbgpct ; /
ret
; process packet channel error
rxcher: call flush ;discard packet
lhld rbcpct ;increment bad channel counter
inx h ; /
shld rbcpct ; /
ret
; process rx data packet
; (internally called)
; on entry: <hl>=rx buffer bcb address
; <b>=packet octet #3
; <c>=rx buffer #
; on exit: all regs, flags clobbered
rxdata:
xchg ;save bcb address
lxi h,chstat ;point to channel state
bit 0,m ;flow control ready state?
xchg ;get back bcb address
jz flush ;no, discard packet
;
call ackd ;process P(s) and P(r)
jc dterst ;reset channel if invalid
;
xchg ;save bcb address
lxi h,fstat ;point to file status
bit 0,m ;receive file open?
xchg ;restore bcb address
jnz rxfdat ;yes, write packet in file
;
; else write packet to console receive buffer
lxi d,crbcb ;point to console rx buffer
rxcdat: call getbuf ;get data octet from rx buffer
jc rxcd1 ;exit when empty
;
xchg ;point to console rx buffer
call putbuf ;put octet in console rx buffer
xchg ;point back to rx buffer
jmp rxcdat ;and go back for more
;
; process end of packet to console
rxcd1: xchg ;point to console rx buffer
call setrdy ;set buffer ready flag
jmp rxdexi ;do common exit stuff
; write packet into disk file
rxfdat:
call getbuf ;get data octet from rx buffer
jc rxfd1 ;exit if empty
;
call pfdata ;else put data in file
jmp rxfdat ;and loop until empty
;
; process end of rx packet data
rxfd1:
mvi a,'+' ;tell console packet rx
call ctype ; /
jmp rxdexi ;do common exit stuff
; common exit for rx packet data
rxdexi:
mov a,c ;get rx buffer #
call rlsrxb ;release rx buffer
lhld rxdpct ;increment rx data packet count
inx h ; /
shld rxdpct ; /
ret
; process rx incoming call packet
; (internally called)
; on entry: <hl>=bcb address
; <b>=packet octet 3 (id)
; <c>=rx buffer #
rxcr:
call getbuf ;get octet 4
jc rxfmer ;format error if not there
;
sta rxoc4 ;save the octet
xchg ;save bcb address
lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state (d1)?
jnz rxcr1 ;yes, accept packet
;
bit 1,m ;DTE restart request state (r2)?
jnz flush ;yes, discard packet
;
bit 2,m ;waiting state (p2)?
jnz rxcr1 ;yes, accept packet
;
bit 3,m ;reset request state (d2)?
jnz rxcr1 ;yes, accept packet
;
bit 4,m ;clear request state (p6)?
jnz rxcr1 ;yes, accept packet
;
bit 5,m ;ready state (p1)?
jnz rxcr1 ;yes, accept packet
;
jmp flush ;else discard packet
;
rxcr1: xchg ;get back bcb address
call chkcadr ;check whether address is for us
jc badadr ;refuse call if invalid
;
call getbuf ;get facilities length octet
jc rxfmer ;format error if not there
;
;*** special facilities not implemented
ora a ;facilities requested?
jnz calrej ;yes, refuse call
;
mov a,c ;get rx buffer #
call rlsrxb ;release it
call reset ;reset flow control variables
call t21off ;turn off timer T21
call t22off ;turn off timer T22
call t23off ;turn off timer T23
call ilprt
db 'L3: rx incoming call ',0
lda caddrl ;get calling DTE address length
ora a ;=0?
jz rxca3 ;yes, keep going
;
inr a ;increment length by one
mov b,a ;and save in <b>
call ilprt ;display calling address
db 'from: ',0
lxi h,caddr ;point to calling address
rxca2: dcr b ;end of address?
jz rxca3 ;yes, exit loop
;
mov a,m ;else get nibble
call phex1 ;display it in hex
inx h ;bump pointer
jmp rxca2 ;and get next nibble
;
rxca3: call ilprt ;terminate message
db cr,lf,0
lxi h,l3stat ;point to flow status
res 1,m ;reset DCE busy flag
res 2,m ;reset interrupt pending flag
res 6,m ;reset end of message flag
lxi h,chstat ;point to channel status
setb 0,m ;set flow control ready state (d1)
res 3,m ;reset DTE reset request state (d2)
res 4,m ;reset DTE clear request state (p6)
res 5,m ;reset ready state (p1)
bit 2,m ;DTE waiting state (p2)?
jz txca ;no, tx call accepted packet
;
; call collision state (p5)
res 2,m ;else reset state p2
ret ;and exit
; process rx call connected packet
; (internally called)
; on entry: <hl>=bcb address
; <b>=packet octet 3 (id)
; <c>=rx buffer #
rxca:
xchg ;save bcb address
lxi h,chstat ;point to channel status
bit 2,m ;DTE waiting state (p2)?
jnz rxca0 ;yes, accept packet
;
jmp flush ;else discard packet
;
rxca0: xchg ;get back bcb address
call getbuf ;get octet 4 if present
jc rxca1 ;skip address check if not present
;
sta rxoc4 ;save octet 4 if present
call chkadr ;check addresses
jc badadr ;refuse call if invalid
;
call getbuf ;get octet 5 if present
jc rxca1 ;skip facilities check if not present
;
;*** special facilities not implemented
;
; checks all done, accept call
rxca1: mov a,c ;get rx buffer #
call rlsrxb ;release it
call reset ;reset flow control variables
call t21off ;turn off timer T21
call t22off ;turn off timer T22
call t23off ;turn off timer T23
call ilprt
db 'L3: rx call connected',cr,lf,0
lxi h,l3stat ;point to flow control status
res 1,m ;reset DCE busy flag
res 2,m ;reset interrupt pending flag
res 6,m ;reset message complete flag
lxi h,chstat ;point to channel status
setb 0,m ;set flow control ready state (d1)
res 2,m ;reset DTE waiting state (p2)
res 3,m ;reset DTE reset request state (d2)
res 4,m ;reset DTE clear request state (p6)
res 5,m ;reset ready state (p1)
ret ;and exit
; process invalid address
; (internally called)
; on entry: <c>=rx buffer #
; on exit: all flags, regs clobbered
;
badadr: call flush ;discard packet
lhld rbapct ;increment bad address count
inx h ; /
shld rbapct ; /
call ilprt
db 'L3: bad address - tx clear request',cr,lf,0
jmp txclrr ;transmit clear request
; refuse call
; (internally called)
; on entry: <c>=rx buffer #
; on exit: all flags, regs clobbered
calrej: call flush ;discard packet
call ilprt
db 'L3: call refused - tx clear request',cr,lf,0
jmp txclrr ;transmit clear request
; process rx clear indication (clear request) packet
; (internally called)
; on entry: <hl>=bcb address
; <b>=packet octet 3 (id)
; <c>=rx buffer #
rxclr:
call getbuf ;get octet 4
jc rxfmer ;format error if not there
;
mov b,a ;save octet 4 in <b>
xchg ;save bcb address
lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state (d1)?
jnz rxclr0 ;yes, accept packet
;
bit 4,m ;clear request state (p6)?
jnz rxclr0 ;yes, accept packet
;
jmp flush ;else discard packet
;
rxclr0: xchg ;restore bcb address
call ilprt ;tell operator
db 'L3: rx clear indication packet, cause: ',0
mov a,b ;get back octet 4
call phex ;print in hex
call ilprt ; /
db ' (hex)',cr,lf,0 ; /
call getbuf ;get octet 5
jc rxclr2 ;keep going if not there
;
mov b,a ;save octet 5 in <b>
call ilprt
db ' diagnostic code : ',0
mov l,b ;print diagnostic code
mvi h,0 ; /
call pdec ;print in decimal
call ilprt
db cr,lf,0
;
; perform flow control actions
rxclr2:
mov a,c ;get rx buffer #
call rlsrxb ;and release it
call reset ;reset flow control variables
call t21off ;stop timers
call t22off ; /
call t23off ; /
call ctxfil ;close transmit file
call crxfil ;close receive file
lxi h,chstat ;point to channel status
res 0,m ;reset state d1
res 2,m ;reset state p2
res 3,m ;reset state d2
setb 5,m ;set ready state p1
bit 4,m ;was it DTE clear request state (p6)?
jz txclrc ;no, tx clear confirmation packet
;
; DTE clear request state (p6)
res 4,m ;else reset clear request state
ret ;and exit
; process rx clear confirmation packet
; (internally called)
; on entry: <hl>=bcb address
; <b>=packet octet 3 (id)
; <c>=rx buffer #
rxclc:
call getbuf ;get octet 4
jnc rxfmer ;format error if there
;
lxi h,chstat ;point to channel status
bit 4,m ;DTE clear request state (p6)?
jz flush ;no, discard packet
;
; DTE clear request state (p6)
res 4,m ;reset back to ready state
mov a,c ;get rx buffer #
call rlsrxb ;and release it
call reset ;reset flow control variables
call ilprt ;tell operator
db 'L3: rx clear confirmation packet',cr,lf,0
ret
; process rx INT packet
; (internally called)
; on entry: <hl>=bcb address
; <c>=rx buffer #
rxint:
call getbuf ;get octet 4
jc rxfmer ;format error if not there
;
xchg ;save bcb address in <de>
lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state (d1)?
jz flush ;no, discard packet
;
xchg ;restore bcb address
mov b,a ;save octet 4 in <b>
call ilprt
db 'L3: rx INT packet - user data: ',0
mov a,b ;get back octet 4
call phex ;print in hex
call ilprt ;terminate line
db cr,lf,0
mov a,c ;get rx buffer #
call rlsrxb ;release it
jmp txintc ;and transmit int confirmation
; process rx INT confirmation packet
; (internally called)
; on entry: <hl>=bcb address
; <c>=rx buffer #
rxintc:
call getbuf ;tryto get octet 4
jnc rxfmer ;format error if there
;
lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state (d1)?
jz flush ;no, discard packet
;
lxi h,l3stat ;point to level 3 status
bit 2,m ;interrupt pending?
jz flush ;no, discard packet
;
res 2,m ;else clear int pending flag
call ilprt
db 'L3: rx interrupt confirmation packet',cr,lf,0
mov a,c ;get rx buffer #
call rlsrxb ;release it
ret
; process DIAG packet
; (internally called)
; on entry: <hl>=bcb address
; <b>=packet octet #3 (packet id)
; <c>=rx buffer #
rxdiag:
call getbuf ;get octet 4
jc rxfmer ;format error if not there
;
xchg ;save bcb address in <de>
mov b,a ;save octet 4 in <b>
call ilprt
db 'L3: rx DIAG packet - diagnostic # ',0
mov l,b ;print diagnostic code
mvi h,0 ; /
call pdec ; /
call ilprt ;print explanation if present
db cr,lf,'L3: diagnostic explanation: ',0
xchg ;get back bcb address
; print diagnostic explanation
diaglp: call getbuf ;get next octet
jc diagexi ;exit if not there
;
call phex ;print in hex
call ilprt ;and separator
db ' ',0
jmp diaglp ;and try for next byte
;
diagexi:
call ilprt ;terminate explanation line
db cr,lf,0
mov a,c ;get rx buffer
call rlsrxb ;release it
ret
; process rx RR packet
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxrr: lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state?
jz flush ;no, discard packet
;
call chkpr ;check P(r) and update window
jc dterst ;local procedure error
;
lxi h,l3stat ;point to level 3 status
res 1,m ;clear DCE busy
mov a,c ;release rx buffer
call rlsrxb ; /
ret
; process rx RNR packet
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxrnr: lxi h,chstat ;point to channel status
bit 0,m ;flow control ready state?
jz flush ;no, discard packet
;
call chkpr ;check P(r) & update window
jc dterst ;local procedure error
;
lxi h,l3stat ;point to level 3 status
setb 1,m ;signal DCE busy
mov a,c ;release rx buffer
call rlsrxb ; /
ret
; process received reset indication
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxrstr: lxi h,chstat ;point to channel status
bit 3,m ;reset request state (d2)?
jnz rxstr0 ;yes, accept packet
;
bit 0,m ;flow control ready state (d1)
jnz rxstr1 ;yes, accept packet
;
jmp flush ;else discard packet
;
; reset collision (para 4.4.3.3)
rxstr0:
res 3,m ;clear reset request state
setb 0,m ;set flow control ready state d1
call t22off ;stop timer T22
mov a,c ;get rx buffer #
call rlsrxb ;release buffer
ret ;and exit
;
; DTE in flow control ready (d1) state
rxstr1:
mov a,c ;release rx buffer
call rlsrxb ; /
call reset ;reset flow control variables
lxi h,l3stat ;point to level 3 status
res 1,m ;clear DCE busy status
jmp txrstc ;and transmit reset confirmation
; process received reset confirmation
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxrstc: lxi h,chstat ;point to channel status
bit 3,m ;DTE reset request state?
jz flush ;no, discard packet
;
; DTE in reset request state (d2)
res 3,m ;clear state d2
setb 0,m ;set state d1
call t22off ;stop timer T22
mov a,c ;get rx buffer #
call rlsrxb ;release buffer
lxi h,l3stat ;point to level 3 status
res 1,m ;clear DCE busy status
ret ;and exit
;
; process received restart indication
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxstar: lxi h,chstat ;point to channel status
res 0,m ;clear state d1 for now
res 2,m ;and state p2
res 3,m ;and state d2
res 4,m ;and state p6
setb 5,m ;set ready state p1
lda pvcmod ;get pvc mode flag
cpi 1 ;VC?
jz rstar1 ;yes, keep going
;
; set flow control ready if in PVC mode
res 5,m ;reset ready state p1
setb 0,m ;and set flow control state d1
;
rstar1:
mov a,c ;release buffer
call rlsrxb ; /
bit 1,m ;DTE restart request state (r2)?
jz rstar2 ;no, keep going
;
; DTE in restart request state (r2)
res 1,m ;clear state r2
call t20off ;stop timer T20
ret ;and exit
;
; DTE not in restart request state
rstar2:
call reset ;reset flow control variables
lxi h,l4stat ;point to level 4 status
setb 0,m ;signal console prompt
;*** do other things here?
;
call ilprt
db 'L3: rx restart',cr,lf,0
jmp txstac ;and transmit restart confirmation
; process received restart confirmation
; (internally called)
; on entry: <b>=octet 3
; <c>=rx buffer #
rxstac: lxi h,chstat ;point to channel status
bit 1,m ;DTE restart request state?
jz flush ;no, discard packet
;
; DTE in restart request state (r2)
res 0,m ;clear state d1 for now
res 1,m ;and state r2
res 2,m ;and state p2
res 3,m ;and state d2
res 4,m ;and state p6
setb 5,m ;set ready state p1
lda pvcmod ;get PVC mode flag
cpi 1 ;VC?
jz rstac1 ;yes, keep going
;
; set flow control ready if in PVC mode
res 5,m ;reset ready state p1
setb 0,m ;and set flow control state d1
;
rstac1:
call t20off ;stop timer T20
mov a,c ;get rx buffer #
call rlsrxb ;release buffer
lxi h,l4stat ;point to level 4 status
setb 0,m ;signal console prompt
ret ;and exit
;
; reset DTE
dterst:
mov a,c ;release buffer
call rlsrxb ; /
call ilprt
db cr,lf,'L3: local procedure error - '
db 'resetting channel',cr,lf,0
call reset ;reset flow control variables
jmp txrstr ;and transmit reset request
; *************************************************
; * utility subroutines for receive section *
; *************************************************
; discard data packet
; (internally called)
; on entry: <c>=rx buffer #
; on exit: all regs, flags clobbered
flush:
mov a,c ;get rx buffer #
call rlsrxb ;release it
lhld rxxpct ;increment discarded packet count
inx h ; /
shld rxxpct ; /
ret
; acknowledge received data packet & update window
; (internally called)
; on entry: <b>=octet 3 of data packet
; on exit: carry set if P(s) or P(r) sequence error
; <a>, other flags clobbered
; all other regs unchanged
ackd:
push h ;save regs
push d ; /
push b ; /
call chkpr ;check P(r) and update window
jc ackdexi ;exit with carry if invalid P(s)
; check for valid rx P(s)
mov a,b ;get octet 3
ani 0000$1110b ;extract rx P(s)
rrc ;move to bits 0-2
mov c,a ;save rx P(s) in <c>
;
; check for P(s) next in sequence
lda lrxps ;get last rx P(s)
inr a ;bump mod 7
ani 7 ; /
cmp c ;next in sequence?
jnz ackderr ;no, invalid
;
; calculate top of valid range for rx P(s)
lda pr ;get local P(r)
adi wsize+1 ;add window size+1
ani 7 ;mod 7
mov e,a ;and save in <e>
;
; check for rx P(s) within window
lda pr ;bottom edge= local P(r)
ackd3: cmp e ;past top of window?
jz ackderr ;yes, error
;
cmp c ;rx P(s) at bottom edge?
jz ackdok ;yes, valid rx P(r)
;
inr a ;else bump bottom edge
ani 7 ; /
jmp ackd3 ;and keep looping
;
; valid rx P(s)
ackdok: mov a,c ;get rx P(s)
sta lrxps ;update last rx P(s)
inr a ;let P(r)=P(s)+1..
ani 7 ;..mod 7
sta pr ;and update P(r)
stc ;clear carry bit
cmc ; /
jmp ackdexi ;and exit
;
; invalid rx P(s)
ackderr:
stc ;set carry bit
; common exit
ackdexi:
pop b ;restore regs
pop d ; /
pop h ; /
ret
; check received P(r) and update window if valid
; (internally called)
; on entry: <b>=octet 3 of rx packet
; on exit: carry set if invalid rx P(r)
; <a>,other flags clobbered
; all other regs unchanged
chkpr: push h ;save regs
push d ; /
push b ; /
mov a,b ;get octet 3
ani 1110$0000b ;extract rx P(r)
rrc ;move to bits 0-2
rrc ; /
rrc ; /
rrc ; /
rrc ; /
mov c,a ;save rx P(r) in <c>
lda lastpr ;get last rx P(r)
cmp c ;same as this one?
jz chkexi ;yes, nothing new
;
; calculate top of valid range for rx P(r)
lda ps ;get local P(s)
inr a ;increment mod 7...
inr a ;..past top of valid range
ani 7 ; /
mov e,a ;save in <e>
;
; check for rx P(r) within window
lda lastpr ;bottom edge=last rx P(r)
chkpr1: cmp e ;past top of window?
jz chkerr ;yes, invalid
cmp c ;rx P(r) at bottom edge?
jz chkpr2 ;yes, valid
inr a ;else bump bottom edge
ani 7 ;mod 7
jmp chkpr1 ;and keep looping
;
; valid rx P(r)
chkpr2: mov a,c ;get rx P(r)
sta lastpr ;update lower window edge
xra a ;clear carry flag
jmp chkexi ;and exit
;
; invalid rx P(r)
chkerr: stc ;set carry flag
;
; common exit
chkexi: pop b ;restore regs
pop d ; /
pop h ; /
ret
; check for valid local DTE address in call request packet
; (internally called)
; on entry: <hl>=bcb address
; <c>=rx buffer #
; on exit: carry set if address is invalid
; <a>,flags clobbered
; all other regs unchanged
chkcadr:
push b ;save regs
push d ; /
push h ; /
lxi h,l3stat ;point to level 3 status
res 5,m ;clear address error flag
pop h ;restore bcb address
lda rxoc4 ;get rx address length octet
mov b,a ;save in <b>
call getmsn ;get high nibble
sta caddrl ;store calling address length
mov a,b ;get back octet 4
ani 0000$1111b ;get called address length
mov b,a ;save it in <b>
lda laddrl ;get our address length
cmp b ;same?
cnz adderr ;no, signal error
lxi d,laddr ;<de> points to our address
inr b ;bump received called address length
;
; check to see if called address is ours
laddr1: dcr b ;last called address byte?
jz caddr1 ;yes, go read calling address
;
call getbuf ;else get next address octet
cc adderr ;signal error if not there
mov c,a ;save octet in <c>
call getmsn ;get high nibble
xchg ;point to our address
cmp m ;match?
cnz adderr ;no, signal error
xchg ;point back to bcb
inx d ;bump pointers
dcr b ;last called address byte?
jz caddr0 ;yes, go read calling address
;
mov a,c ;get back octet
ani 0000$1111b ;get low nibble
xchg ;point to our address
cmp m ;match?
cnz adderr ;no, signal error
xchg ;point back to bcb
inx d ;bump pointer
jmp laddr1 ;and look for next octet
;
; read and store calling address if at high nibble
caddr0: lda caddrl ;get calling address length
mov b,a ;save in <b>
lxi d,caddr ;point to calling address buffer
inr b ;bump received calling address length
jmp caddr3 ;and keep going
;
; read and store calling address if at low nibble
caddr1: lda caddrl ;get calling address length
mov b,a ;save in <b>
lxi d,caddr ;point to calling address buffer
inr b ;bump received calling address length
;
; read address if at low nibble
caddr2: dcr b ;last address nibble?
jz cadexi ;yes, exit
;
call getbuf ;get next octet
cc adderr ;else signal error
mov c,a ;save in <c>
call getmsn ;get high nibble
stax d ;save at pointer location
inx d ;bump pointers
;
; read address if at high nibble
caddr3: dcr b ;last address nibble?
jz cadexi ;yes, exit
;
mov a,c ;else get back octet
ani 0000$1111b ;get low nibble
stax d ;and save at pointer location
inx d ;bump pointer
jmp caddr2 ;and get next octet
;
; exit with carry set if error occured
cadexi: stc ;set carry flag
push h ;save bcb
lxi h,l3stat ;point to level 3 status
bit 5,m ;called address error?
pop h ;restore bcb
jnz cadexi1 ;and exit with carry
;
cmc ;else clear carry
cadexi1:
pop d ;restore regs
pop b ; /
ret
; get high nibble of octet
; (internally called)
; on entry: <a>=octet
; on exit: <a>=high nibble of octet
; all other regs unchanged
getmsn:
ani 1111$0000b ;strip out high nibble
rrc ;and move to bits 0-4
rrc ; /
rrc ; /
rrc ; /
ret
; signal called DTE address error
; (internally called)
; on entry: no parameters
; on exit: l3stat bit 5=1
; all regs unchanged
;
adderr: push h ;save <hl>
lxi h,l3stat ;point to level 3 status
setb 5,m ;set address error bit
pop h ;restore <hl>
ret
; check for valid addresses in call accepted packet
; (internally called)
; on entry: <hl>=rx bcb address
; on exit: carry set if address error
; all other regs unchaged
chkadr:
;*** not implemented yet
xra a
ret
; *****************
; * data area *
; *****************
dseg ;data area
; level 3 status indicators
l3stat: db 0 ;level 3 status flags
chstat: db 0 ;logical channel state
; X.25 address and channel parameters
group db 0 ;logical group number
chan db lchan ;logical channel number
gfi db 0 ;general format identifier
laddrl: db 0 ;local address length (# of hex chars)
raddrl: db 0 ;remote address length (# of hex chars)
caddrl: db 0 ;rx calling DTE address length
laddr: ds 15 ;local DTE address (1 hex char/byte)
raddr: ds 15 ;remote DTE address (1 hex char/byte)
caddr: ds 15 ;rx calling DTE address (1 hex char/byte)
rxoc4: db 0 ;received address length octet 4
; X.25 packet flow control variables
pr db 0 ;P(r)
ps db 0 ;P(s)
lastpr db 0 ;last rx P(r)
ltxpr db 0 ;last tx P(r)
lrxps db 7 ;last rx P(s)
ltxps db 7 ;last tx P(s)
qbit db 0 ;Q (qualifier) bit (bit 7)
dbit db 0 ;D (delivery confirmation) bit (bit 6)
rtryct db n3 ;tx retry count
pvcmod db 1 ;VC/PVC mode flag (1=VC,2=PVC)
packct db kpack ;packet acknowledgement delay count
; level 3 diagnostic counters
txpct: dw 0 ;total tx packets
txfpct: dw 0 ;tx file data packets
txcpct: dw 0 ;tx console data packets
ntxbct: dw 0 ;errors due to no free tx bcb
;
rxpct: dw 0 ;total rx packets
rxdpct: dw 0 ;total rx data packets
rbfpct: dw 0 ;total rx bad format packets
rbcpct: dw 0 ;total rx bad channel packets
rbapct: dw 0 ;total rx bad address packets
rbipct: dw 0 ;total rx bad id packets
rbgpct: dw 0 ;total rx bad group packets
rxxpct: dw 0 ;total discarded rx packets