www.pudn.com > commutil.zip > BBPLUS.ASM
page 55,132
title "BBPLUS - Compuserve B+ Protocol Module"
; ---------------------------------------------------------------------
;
; Maintenance Log:
;
; Version Date Description Who
; ------- ------- ---------------------------------- ----------------
; 1.0 08Feb91 Initial version complete Flanders/Holmes
;
; ---------------------------------------------------------------------
protocol segment para public 'code'
assume cs:protocol
start: jmp bpmbplus ; jump to start
db "BD" ; backdown protocol ID
db 1 ; nbr of protocols
db "B" ; invocation letter
db "CIS B+ V1.0 ", 0 ; name to display
db 1 ; protocol number
; --------------------------------------------------------------------
; Macro(s)
; --------------------------------------------------------------------
FARRET macro ; far return macro
db 0cbh
endm
NSTATE macro nxtstate ; go to next state
ifnb ; if next state given
mov state, nxtstate ; move in next state
endif
stc ; tell state_exec to do it
ret ; .. continue
endm
XSTATE macro nxtstate ; exit the state machine
ifnb ; if next state given
mov state, nxtstate ; move in next state
endif
clc ; tell state_exec to do it
ret ; .. continue
endm
; ---------------------------------------------------------------------
; Protocol characters equates
; ---------------------------------------------------------------------
$ETX = 03h ; Trailer byte
$ENQ = 05h ; Enquiry code
$CR = 0Dh ; Carriage return
$DLE = 10h ; Lead in byte
$NAK = 15h ; Negative acknowledgement
; ---------------------------------------------------------------------
; Local stack
; ---------------------------------------------------------------------
even
bpmstack db 32 dup ("St") ; local stack area
bpmstackend db "Es" ; End of local stack
stackoffset dw offset bpmstackend ; New stack pointer
; ---------------------------------------------------------------------
; State definitions
; ---------------------------------------------------------------------
$ST_INIT = -1 ; Awaiting initialization
$ST_DLE = 0 ; DLE seen
$ST_DLEB = 2 ; B seen
$ST_GETDATA = 4 ; Getting data in packet
$ST_GETCKS = 6 ; Getting checksum
$ST_GETCRC = 8 ; Getting crc
$ST_VERCRC = 10 ; Verifying CRC
$ST_VERCKS = 12 ; Verifying Checksum
$ST_VERPKT = 14 ; Verifying Packet
$ST_SNDNAK = 16 ; Sending a NAK
$ST_SNDACK = 18 ; Sending an ACK
$ST_GETDLE = 20 ; Wait for a DLE
state dw $ST_INIT ; current state
state_jmp dw bpdle ; 0: DLE seen
dw bpdleb ; 2: B seen
dw bpgetdata ; 4: Getting data in packet
dw bpgetcks ; 6: Getting checksum
dw bpgetcrc ; 8: Getting crc
dw bpvercrc ; 10: Verifying CRC
dw bpvercks ; 12: Verifying Checksum
dw bpverpkt ; 14: Verifying Packet
dw bpsndnak ; 16: Sending a NAK
dw bpsndack ; 18: Sending an ACK
dw bpgetdle ; 20: Wait for a DLE
; ---------------------------------------------------------------------
; Transport Parameter Packet
;
; This protocol supports the following:
;
; Send window: 1 packet Receive window: 2 packets
; Block size: 512 bytes Check method: CRC
; Transport Layer: No Download resume: No
; Upload Resume: No File information: No
;
; Data Quoting: and
; ---------------------------------------------------------------------
tp_seq db ? ; sequence number
tp_msg db "+", 0, 1, 4, 1, 1, 0 ; Transport parmameter pkt
tp_msgq db 14h, 0, 0d4h, 0 ; quoting level
db $ETX
tp_msg_len = $-tp_seq ; Length of message
; ---------------------------------------------------------------------
; Data areas
; ---------------------------------------------------------------------
flgs db 0 ; various flags
flgfnc = 10000000b ; 1... .... callback active
flgrcv = 01000000b ; .1.. .... character rcv'd
flgcan = 00100000b ; ..1. .... cancel req'd
flgtmo = 00010000b ; ...1 .... timeout
flgopen = 00001000b ; .... 1... file is open
flgrec = 00000100b ; .... .1.. record available
bparea label byte ; internal BPAREA
bpflg db 0 ; BPLUS flags
bpfcrc = 10000000b ; 1... .... Use CRC checking
bpfqut = 01000000b ; .1.. .... Next is quoted char
bpseq db '0' ; current sequence number
bpq db 8 dup (0ffh) ; default quoting set
ubp_area dd ? ; users' save area
ubp_areao equ word ptr ubp_area ; offset
ubp_areas equ word ptr ubp_area+2 ; segment
bpqc00 db 32 dup (0) ; 00h-1fh quoting
bpqc80 db 32 dup (0) ; 80h-9fh quoting
ticks dw ? ; entry nbr ticks
jmptab dw init ; 0: initialization call
dw fncok ; 2: last function ok
dw fncfail ; 4: last function failed
dw commchar ; 6: character received
dw kbdchar ; 8: keystroke encountered
MAXCODE = 8 ; maximum entry code
CHRTMOUT = ((10*182)/10) ; 10 second char timeout
PKTTMOUT = ((25*182)/10) ; 25 second char timeout
MAXERRS = 10 ; maximum errors in row
errors db 0 ; error counter
filename db 65 dup (0) ; space for filename
packet db 520 dup (?) ; packet buffer
rcvaddr dw offset packet ; address for next receive
ckbyte2 db ? ; byte 2 of chk val (crc)
ckbyte1 db ? ; byte 1 (crc or cksum)
crcval equ word ptr ckbyte2 ; .. to access it as a word
chksumval equ byte ptr ckbyte1 ; .. to access it as a byte
oldds dw ? ; caller's ds
oldsp dw ? ; caller's sp
oldss dw ? ; caller's ss
init2x db 13, 'B+: Init called 2x', 13, 0
badfnc db 13, 'B+: Bad fnc code', 13, 0
openerr db 13, 'B+: Unable to open requested file', 13, 0
timedout db 13, 'B+: Timeout, protocol ended', 13, 0
toomany db 13, 'B+: Too many errors', 13, 0
notinited db 13, "B+: Not init'd first", 13, 0
sendercan db 13, 'B+: Download cancelled', 13, 0
success db 13, 'B+: Download completed OK', 13, 0
writerr db 13, 'B+: Aborted: write error', 13, 0
openok db 13, 'B+: Receiving ', 0
alrdyopen db 13, 'B+: File opened twice', 13, 0
unknownpkt db 13, 'B+: Unknown packet type', 13, 0
noupload db 13, "B+: Can't upload", 13, 0
clsb4open db 13, 'B+: Close before open', 13, 0
staterr0 db 13, 'B+: Staterr 0', 13, 0
staterr1 db 13, 'B+: Staterr 1', 13, 0
panicmsg dw 7
db 10h, 10h, 10h, 10h, 10h, "++"
; ---------------------------------------------------------------------
; Protocol Mainline
; ---------------------------------------------------------------------
bpmbplus proc ; entry point
push es ; save caller's regs
push ds
mov cs:oldds, ds ; keep copy of caller's ds
mov cs:oldss, ss ; save caller's stack
mov cs:oldsp, sp
mov cs:ticks, ax ; save entry ticks
mov ax, cs ; ax -> our segment
mov ds, ax ; ds -> our segment
mov es, ax ; es -> our segment
cli ; no interrupts ..
mov ss, ax ; ss -> our segment
mov sp, stackoffset ; sp -> end of stack
sti ; .. allow ints again
cmp di, MAXCODE ; q. is code ok?
ja bpmbplus90 ; a. no .. return "done"
test di, 1 ; q. is code even?
jnz bpmbplus90 ; a. no .. return "done"
jmp jmptab[di] ; .. call requested routine
bpmbplus90: lea bx, badfnc ; bx -> bad function code
jmp proto_err ; issue error and leave
bpmbplus endp
; ---------------------------------------------------------------------
; Initialization call; ds:bx -> operands (filename)
; ---------------------------------------------------------------------
init proc
cmp state, $ST_INIT ; q. already init'd?
je init00 ; a. no .. continue
lea bx, init2x ; bx -> error message
jmp proto_err ; .. kill the protocol
init00: mov ubp_areao, si ; save user's offset
push oldds ; push user's ds
pop ubp_areas ; .. set bparea segment
push ds ; save our ds
lds si, ubp_area ; ds:si -> user's area
lea di, bparea ; di -> our bp area
mov cx, 10 ; cx = bytes to move
cld ; .. in upward direction
rep movsb ; .. move it in.
pop ds ; restore our ds
lea si, bpq ; si -> quote parameters
lea di, bpqc00 ; di -> quote array
call bd_setq ; setup quote array
mov state, $ST_DLE ; set state to DLE SEEN
mov bx, CHRTMOUT ; bx = ticks to wait
call setimer ; .. set the timer
jmp proto_ok ; .. return to caller
init endp
; ---------------------------------------------------------------------
; Last function executed ok -or- simple dispatch
; flgfnc = 0: simple dispatch
; fncfnc = 1: state return
; -- Carry is cleared
; ---------------------------------------------------------------------
fncok proc
test flgs, flgfnc ; q. callback return?
jz fncok05 ; a. no .. dispatch
clc ; show function succeeded
ret ; .. rtn to callback caller
fncok05: cmp state, $ST_INIT ; q. init'd yet?
jne fncok10 ; a. yes .. continue
lea bx, notinited ; bx -> not init'd msg
jmp proto_err ; .. kill the protocol
fncok10: jmp short check_rec ; see if record received
fncok endp
; ---------------------------------------------------------------------
; Previous function failed Carry set to show failure
; ---------------------------------------------------------------------
fncfail proc ; last function failed
test flgs, flgfnc ; q. callback return?
jnz fncfail10 ; a. yes .. continue
lea bx, staterr1 ; bx -> state error msg
jmp proto_err ; .. tell user & die
fncfail10: stc ; set carry to show fail
ret ; return to callback caller
fncfail endp
; ---------------------------------------------------------------------
; Process Communications Character dl = character
; ---------------------------------------------------------------------
commchar proc
cmp state, $ST_INIT ; q. init'd yet?
jne commchar05 ; a. yes .. continue
lea bx, notinited ; bx -> not init'd msg
jmp proto_err ; .. kill the protocol
commchar05: test flgs, flgfnc ; q. callback return?
jz commchar10 ; a. no .. continue
lea bx, staterr0 ; bx -> state error msg
jmp proto_err ; .. tell user & die
commchar10: mov bx, rcvaddr ; bx -> receive address
mov [bx], dl ; save received character
or flgs, flgrcv ; show a char was received
jmp short check_rec ; see if record received
commchar endp
; ---------------------------------------------------------------------
; Process keyboard character dl = character
; ---------------------------------------------------------------------
kbdchar proc
cmp state, $ST_INIT ; q. init'd yet?
jne kbdchar05 ; a. yes .. continue
lea bx, notinited ; bx -> not init'd msg
jmp proto_err ; .. kill the protocol
kbdchar05: test flgs, flgfnc ; q. callback return?
jz kbdchar10 ; a. no .. continue
lea bx, staterr0 ; bx -> state error msg
jmp proto_err ; .. tell user & die
kbdchar10: cmp dx, 4F00H ; q. END key?
jnz kbdchar20 ; a. no .. skip it
or flgs, flgcan ; show cancel requested.
kbdchar20: jmp short check_rec ; see if record received
kbdchar endp
; ---------------------------------------------------------------------
; See if record received
; ---------------------------------------------------------------------
check_rec proc
call state_exec ; exec the state machine
test flgs, flgrec ; q. record received?
jz chk_rec90 ; a. no .. exit
and flgs, not flgrec ; shut off indicator
cmp packet+1, '+' ; q. transport parms?
jne chk_rec10 ; a. no .. check next
call proc_tport ; process transport rec
jmp proto_ok ; .. continue
chk_rec10: cmp packet+1, 'T' ; q. transfer packet?
jne chk_rec20 ; a. no .. check next
call proc_trec ; process T record
jmp proto_ok ; .. continue
chk_rec20: cmp packet+1, 'N' ; q. data packet?
jne chk_rec30 ; a. no .. check next
call proc_nrec ; process N record
jmp proto_ok ; .. continue
chk_rec30: lea bx, sendercan ; else .. sender cancelled it
cmp packet+1, 'F' ; q. failure packet?
je chk_rec40 ; a. yes .. kill protocol
lea bx, unknownpkt ; unknown packet type
chk_rec40: call panic ; panic abort!
jmp proto_err ; .. the the users
chk_rec90: jmp proto_ok ; .. continue later
check_rec endp
; ---------------------------------------------------------------------
; Process transport (+) record
; ---------------------------------------------------------------------
proc_tport proc
lea si, packet+8 ; point at quoting set
lea di, bpq ; di -> quoting set
mov cx, 4 ; words to move
cld ; .. in upward direction
rep movsw ; .. move in the quote set
mov al, packet+5 ; al = tp's crc flag
push ax ; .. save on stack
mov al, packet ; get his packet #
mov bpseq, al ; .. save it in ours
call nxtseq ; set next sequence nbr
mov al, bpseq ; al = next sequence no
mov tp_seq, al ; .. save in seq record
lea si, tp_seq ; si -> our tport record
lea di, packet ; di -> send packet area
mov cx, tp_msg_len ; cx = length o'packet
call bd_setout ; .. set up output
pop ax ; restore crc flag
and bpflg, not bpfcrc ; assume checksum
or al, al ; q. use CRC?
jz tport10 ; a. no .. continue
or bpflg, bpfcrc ; .. set on CRC
tport10: mov bx, di ; bx -> string to send
mov di, 18 ; di = send 2cnt string
call callback ; .. send it out
lea si, bpq ; si -> quote parameters
lea di, bpqc00 ; di -> quote array
call bd_setq ; setup new quote array
and flgs, not flgtmo ; reset timout
mov bx, PKTTMOUT ; .. use packet timout
call setimer ; .. reset timer
mov state, $ST_GETDLE ; .. set next state
ret ; .. and return to caller
proc_tport endp
; ---------------------------------------------------------------------
; Process T record
; ---------------------------------------------------------------------
proc_trec proc
cmp packet+2, 'D' ; q. send to us (download)?
jne trec10 ; a. no .. check next
lea bx, alrdyopen ; file already open
test flgs, flgopen ; q. file open?
jnz trec06 ; a. yes.. error
lea si, packet+4 ; si -> file name
lea di, filename ; di -> output area
cld ; .. address ascending
trec03: lodsb ; al = filename char
cmp al, ' ' ; q. above blank?
ja trec05 ; a. yes ..
xor al, al ; else .. clear al
trec05: stosb ; .. put char in filename
or al, al ; q. end of filename?
jnz trec03 ; a. no .. continue
lea bx, filename ; bx -> filename to open
mov di, 2 ; di = open a file
call callback ; ask for open
jnc trec07 ; .. continue protocol
call panic ; kill the protocol
lea bx, openerr ; bx -> open error msg
trec06: jmp proto_err ; .. die now
trec07: lea bx, openok ; bx -> opened ok
mov di, 12 ; di = display ASCIIZ
call callback ; .. display the message
lea bx, filename ; bx -> filename opened
mov di, 12 ; di = display ASCIIZ
call callback ; .. display the message
mov bl, $CR ; bx -> carriage return
mov di, 14 ; di = display character
call callback ; .. display the message
call nxtseq ; set next sequence number
call bpsndack ; .. ack the pack
or flgs, flgopen ; .. show file open
ret ; .. continue
trec10: cmp packet+2, 'C' ; q. closing file?
jne trec20 ; a. no .. check next
call nxtseq ; get next sequence number
call bpsndack ; send an ack
lea bx, clsb4open ; close before open
test flgs, flgopen ; q. file open?
jz trec90 ; a. no .. error
and flgs, not flgopen ; reset open flag
lea bx, success ; bx -> success
jmp proto_err ; .. return to caller
trec20: lea bx, noupload ; bx -> no upload msg
cmp packet+3, 'U' ; q. upload from us?
je trec30 ; a. no .. uh oh.
lea bx, unknownpkt ; bx -> unknown packet type
trec30: call panic ; panic abort!
trec90: jmp proto_err ; .. tell the user
proc_trec endp
; ---------------------------------------------------------------------
; Process data packet
; ---------------------------------------------------------------------
proc_nrec proc
mov cx, rcvaddr ; cx = next rcv addr
sub cx, offset packet ; cx = len of packet
sub cx, 3 ; cx = len of data
jz nrec10 ; .. skip if no data
mov word ptr packet, cx ; .. save in packet
lea bx, packet ; bx -> packet
mov di, 4 ; di = write data
call callback ; q. write that data ok?
jc nrec20 ; a. yes .. continue
nrec10: call nxtseq ; .. set for next sequence
call bpsndack ; else .. send an ack
ret ; .. let it keep running
nrec20: call panic ; abort the protocol
lea bx, writerr ; bx -> write error message
jmp proto_err ; .. kill the protocol
proc_nrec endp
; ---------------------------------------------------------------------
; Run the state machine
; ---------------------------------------------------------------------
state_exec proc
cmp ticks, 0 ; q. timeout on ticks?
jg state10 ; a. no .. continue
or flgs, flgtmo ; timeout occurred
state10: mov bx, state ; bx = state offset
call state_jmp[bx] ; .. go to appropriate state
jc state10 ; .. next state
ret ; return to caller
state_exec endp
; ---------------------------------------------------------------------
; State: Wait for DLE
; ---------------------------------------------------------------------
bpgetdle proc
test flgs, flgcan ; q. user cancel?
jz bpgdle00 ; a. no .. continue
call panic ; panic abort
and flgs, not flgcan ; .. and shut off flag
bpgdle00: test flgs, flgrcv ; q. character received?
jnz bpgdle10 ; a. yes .. check it out
mov bx, PKTTMOUT ; bx = packet timeout
call chktimer ; q. timeout?
jz bpgdle05 ; a. no .. continue
lea bx, timedout ; bx -> timeout message
jmp chk_err ; see if too many errors
bpgdle05: XSTATE ; .. exit the state machine
bpgdle10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = received char
and flgs, not flgrcv ; reset receive flag
cmp al, $DLE ; q. seen?
je bpgdle50 ; a. yes .. process it
cmp al, $ENQ ; q. seen?
jne bpgdle15 ; a. no .. continue
NSTATE $ST_SNDACK ; else .. send an ACK
bpgdle15: cmp al, $NAK ; q. received?
jne bpgdle20 ; a. no .. continue
and flgs, not flgtmo ; bx -> protocol error msg
XSTATE ; .. stay in state
bpgdle20: cmp al, $ETX ; q. ETX?
jne bpgdle05 ; a. no .. continue
NSTATE $ST_SNDNAK ; else .. send a NAK
bpgdle50: mov bx, CHRTMOUT ; set character timout
call setimer ; .. reset timer
mov errors, 0 ; .. reset errors
and flgs, not flgtmo ; .. reset timout
XSTATE $ST_DLE ; .. state = DLE seen
bpgetdle endp
; ---------------------------------------------------------------------
; State: DLE Seen
; ---------------------------------------------------------------------
bpdle proc
test flgs, flgrcv ; q. character received?
jnz bpdle10 ; a. yes .. check it out
mov bx, CHRTMOUT ; bx = character timeout
call chktimer ; q. timeout?
jz bpdle05 ; a. no .. continue
lea bx, timedout ; bx -> timeout message
jmp chk_err ; see if too many errors
bpdle05: XSTATE ; exit the state machine
bpdle10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = received char
and flgs, not flgrcv ; .. shut off char rcvd
cmp al, 'B' ; q. B seen?
je bpdle50 ; a. yes .. process it
cmp al, '0' ; q. possible ack?
jb bpdle12 ; a. no .. continue
cmp al, '9' ; q. is it an ACK?
ja bpdle12 ; a. no .. continue
cmp al, bpseq ; q. it it THE ACK?
jne bpdle15 ; a. no .. continue
jmp short bpdle15 ; .. continue
bpdle12: cmp al, $ENQ ; q. Enquiry?
jne bpdle15 ; a. no .. continue
NSTATE $ST_SNDACK ; .. resend an ack
bpdle15: mov bx, PKTTMOUT ; bx = packet timout
call setimer ; .. set the timer
XSTATE $ST_GETDLE ; else .. get a DLE
bpdle50: mov bx, CHRTMOUT ; bx = character timeout
call setimer ; set timer to 10 sec
mov errors, 0 ; reset errors
and flgs, not flgtmo ; reset timout
mov rcvaddr, offset packet ; address of sequence
XSTATE $ST_DLEB ; set next state
bpdle endp
; ---------------------------------------------------------------------
; State: B seen
; ---------------------------------------------------------------------
bpdleb proc
test flgs, flgrcv ; q. character received?
jnz bpdleb10 ; a. yes .. check it out
mov bx, CHRTMOUT ; bx = character timeout
call chktimer ; q. timeout?
jz bpdleb90 ; a. no .. continue
NSTATE $ST_SNDNAK ; send a NAK
bpdleb10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = char received
cmp al, $ENQ ; q. ENQ received?
jne bpdleb20 ; a. no .. save character
XSTATE $ST_SNDACK ; send an ACK
bpdleb20: call charcvd ; receive the char
and flgs, not flgtmo ; reset any timeout
mov bx, CHRTMOUT ; .. set for char timeout
call setimer ; .. reset the timer
mov errors, 0 ; .. reset error counter
XSTATE $ST_GETDATA ; .. state = get data
bpdleb90: XSTATE ; .. continue
bpdleb endp
; ---------------------------------------------------------------------
; State: Get data
; ---------------------------------------------------------------------
bpgetdata proc
test flgs, flgrcv ; q. character received?
jnz bpgdat10 ; a. yes .. check it out
mov bx, CHRTMOUT ; bx = character timeout
call chktimer ; q. timeout?
jz bpdleb90 ; a. no .. continue
NSTATE $ST_SNDNAK ; send a NAK
bpgdat10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = char received
test bpflg, bpfqut ; q. awaiting quoted?
jz bpgdat20 ; a. no .. continue
cmp al, 60h ; q. upper set?
pushf ; .. save results
and al, 1fh ; set off upper bits
popf ; q. add 80 hex?
jb bpgdat15 ; a. no .. leave as is.
add al, 80h ; else .. 80h-9fh
bpgdat15: mov [bx], al ; save unquoted char
and bpflg, not bpfqut ; .. shut off quoting
jmp short bpgdat85 ; .. and continue protocol
bpgdat20: cmp al, $DLE ; q. quoted char?
jne bpgdat25 ; a. no .. continue
and flgs, not flgrcv ; set off received flag
or bpflg, bpfqut ; .. and set on quoted flag
jmp short bpgdat90 ; .. and continue
bpgdat25: cmp al, $ETX ; q. ETX received?
jne bpgdat30 ; a. no .. check next
call charcvd ; save the ETX
mov state, $ST_GETCKS ; get first byte of check
jmp short bpgdat90 ; .. continue processing
bpgdat30: cmp al, $ENQ ; q. ENQ received?
jne bpgdat85 ; a. no .. received char
mov state, $ST_SNDACK ; .. send out an ack
jmp short bpgdat90 ; .. and continue
bpgdat85: call charcvd ; .. and process it
bpgdat90: and flgs, not flgtmo ; reset timeout
mov bx, CHRTMOUT ; .. set to CHAR timout
call setimer ; .. reset the timer
XSTATE ; .. and continue
bpgetdata endp
; ---------------------------------------------------------------------
; Wait for checksum char
; ---------------------------------------------------------------------
bpgetcks proc
test flgs, flgrcv ; q. character received?
jnz bpgcks10 ; a. yes .. check it out
mov bx, CHRTMOUT ; bx = character timeout
call chktimer ; q. timeout?
jz bpgcks90 ; a. no .. continue
XSTATE $ST_SNDNAK ; send a NAK
bpgcks10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = char received
test bpflg, bpfqut ; q. quoting active?
jz bpgcks50 ; a. no .. check/save char
and bpflg, not bpfqut ; .. reset quoting flag
cmp al, 60h ; q. 80-9f range?
pushf ; .. save the answer
and al, 1fh ; .. upper bits off
popf ; .. restore answer ..
jb bpgcks70 ; a. no .. store as is
add al, 80h ; else.. reconstitute 80-9f
jmp short bpgcks70 ; .. and save it
bpgcks50: cmp al, $DLE ; q. quoted char?
jne bpgcks70 ; a. no .. continue
and flgs, not flgrcv ; "receive" the quote
or bpflg, bpfqut ; show quoted char expected
XSTATE ; .. and say in state
bpgcks70: and flgs, not flgrcv ; show byte processed
mov ckbyte1, al ; save as checksum
mov state, $ST_GETCRC ; .. assume CRC checking
test bpflg, bpfcrc ; q. using CRC?
jnz bpgcks90 ; a. yes .. continue
NSTATE $ST_VERCKS ; else .. verify checksum
bpgcks90: XSTATE ; .. continue
bpgetcks endp
; ---------------------------------------------------------------------
; Wait for CRC character
; ---------------------------------------------------------------------
bpgetcrc proc
test flgs, flgrcv ; q. character received?
jnz bpgcrc10 ; a. yes .. check it out
mov bx, CHRTMOUT ; bx = character timeout
call chktimer ; q. timeout?
jz bpgcrc90 ; a. no .. continue
XSTATE $ST_SNDNAK ; send a NAK
bpgcrc10: mov bx, rcvaddr ; bx -> received char
mov al, [bx] ; al = char received
test bpflg, bpfqut ; q. quoting active?
jz bpgcrc50 ; a. no .. check/save char
and bpflg, not bpfqut ; .. reset quoting flag
cmp al, 60h ; q. 80-9f range?
pushf ; .. save the answer
and al, 1fh ; .. upper bits off
popf ; .. restore answer ..
jb bpgcrc70 ; a. no .. store as is
add al, 80h ; else.. reconstitute 80-9f
jmp short bpgcrc70 ; .. and save it
bpgcrc50: cmp al, $DLE ; q. quoted char?
jne bpgcrc70 ; a. no .. continue
and flgs, not flgrcv ; "receive" the quote
or bpflg, bpfqut ; show quoted char expected
XSTATE ; .. and say in state
bpgcrc70: and flgs, not flgrcv ; show byte processed
mov ckbyte2, al ; save as CRC byte 2
NSTATE $ST_VERCRC ; .. assume CRC checking
bpgcrc90: XSTATE ; .. continue
bpgetcrc endp
; ---------------------------------------------------------------------
; Verify Checksum
; ---------------------------------------------------------------------
bpvercks proc
lea si, packet ; si -> packet
mov cx, rcvaddr ; cx -> next to receive
sub cx, si ; cx = length to check
call bpcksum ; dx = calc'd chksum
cmp dl, chksumval ; q. does it match?
je bpvercks90 ; a. yes .. exit AOK!
XSTATE $ST_SNDNAK ; else .. send a NAK
bpvercks90: NSTATE $ST_VERPKT ; .. continue
bpvercks endp
; ---------------------------------------------------------------------
; Verify CRC
; ---------------------------------------------------------------------
bpvercrc proc
lea si, packet ; si -> packet
mov cx, rcvaddr ; cx -> next to receive
sub cx, si ; cx = length to check
call bpcrc ; dl = calc'd crc
cmp dx, crcval ; q. does it match?
je bpvercrc90 ; a. yes .. exit AOK!
XSTATE $ST_SNDNAK ; else .. send a NAK
bpvercrc90: NSTATE $ST_VERPKT ; .. continue
bpvercrc endp
; ---------------------------------------------------------------------
; Verify packet
; ---------------------------------------------------------------------
bpverpkt proc
mov al, packet ; al = sequence number
mov ah, bpseq ; ah = last good sequence
inc ah ; ah = next seq number
cmp ah, '9' ; q. did seq wrap?
jna bpverpkt05 ; a. no .. continue
mov ah, '0' ; ah = next packet number
bpverpkt05: cmp al, ah ; q. expected number?
jne bpverpkt10 ; a. no .. check it out
mov errors, 0 ; reset error count
or flgs, flgrec ; show record received
XSTATE $ST_SNDACK ; else .. exit the state
bpverpkt10: cmp packet+1, 'F' ; q. Fail packet?
jne bpverpkt20 ; a. no .. continue
or flgs, flgrec ; show record received
XSTATE ; else .. exit the state
bpverpkt20: cmp al, bpseq ; q. previous packet?
jne bpverpkt40 ; a. no .. NAK it
NSTATE $ST_SNDACK ; .. ack the pak
bpverpkt40: NSTATE $ST_SNDNAK ; .. nak that pak!
bpverpkt endp
; ---------------------------------------------------------------------
; Send an ACK
; ---------------------------------------------------------------------
bpsndack proc
mov bl, $DLE ; bl = DLE
mov di, 20 ; di = send one char
call callback ; .. ask BD to do it
mov bl, bpseq ; bl = sequence number
mov di, 20 ; di = send one char
call callback ; .. ask BD to do it
mov bl, '.' ; bl = I'm alive char
mov di, 14 ; di = display one char
call callback ; .. ask BD to do it
mov bx, PKTTMOUT ; set packet timeout
call setimer ; set the timer
and flgs, not flgtmo ; .. reset timeout
mov rcvaddr, offset packet ; .. reset the receive addr
XSTATE $ST_GETDLE ; get a DLE
bpsndack endp
; ---------------------------------------------------------------------
; Send a NAK
; ---------------------------------------------------------------------
bpsndnak proc
cmp errors, MAXERRS ; q. too many errors?
jb bpsndnak10 ; a. no .. continue
lea bx, toomany ; bx -> too many errors msg
jmp proto_err ; .. exit the protocol
bpsndnak10: mov bl, $NAK ; bl = NAK
mov di, 20 ; di = send one char
call callback ; .. ask BD to do it
mov bl, 'X' ; bl = I'm nakking char
mov di, 14 ; di = display one char
call callback ; .. ask BD to do it
mov bx, PKTTMOUT ; set packet timeout
call setimer ; set the timer
and flgs, not flgtmo ; .. reset timeout
mov rcvaddr, offset packet ; .. reset the receive addr
XSTATE $ST_GETDLE ; get a DLE
bpsndnak endp
; ---------------------------------------------------------------------
; Send a panic abort
; ---------------------------------------------------------------------
panic proc
push bx ; save caller's bx
lea bx, panicmsg ; bx -> panic stop msg
mov di, 18 ; di = send one char
call callback ; .. ask BD to do it
pop bx ; restore caller's bx
ret ; return to caller
panic endp
; ---------------------------------------------------------------------
; Check for timeout non-zero = timeout
; ---------------------------------------------------------------------
chktimer proc
test flgs, flgtmo ; set zf if no timeout
pushf ; save flags
jz chktimer10 ; .. jump if no timeout
call setimer ; set 10 second value
chktimer10: popf ; restore test results
ret ; .. return to caller
chktimer endp
; ---------------------------------------------------------------------
; Set timer to 10 second timeout bx = ticks to wait
; ---------------------------------------------------------------------
setimer proc
mov di, 22 ; di = set tick downcounter
call callback ; .. have BD do it
ret ; .. return to caller
setimer endp
; ---------------------------------------------------------------------
; Check errors bx -> error message
; ---------------------------------------------------------------------
chk_err proc
inc errors ; add one to errors
cmp errors, MAXERRS ; q. too many errors?
jb chk_err10 ; a. no .. continue
jmp proto_err ; .. kill the protocol
chk_err10: XSTATE ; .. continue the protocol
chk_err endp
; ---------------------------------------------------------------------
; Show received character processed
; ---------------------------------------------------------------------
charcvd proc
and flgs, not flgrcv ; shut off receive bit
inc rcvaddr ; point to next address
ret ; return to caller
charcvd endp
; ----------------------------------------------------------------------
; Calculate standard B protocol checksum
;
; entry: si -> block to check
; cx = length
; exit: dl = checksum
; ----------------------------------------------------------------------
bpcksum proc
push cx ; save register
xor ah, ah ; ah = zero for additions
xor dx, dx ; dx = zero for accumulator
bpcksum10: shl dx, 1 ; dx = shifted left by 1
cmp dx, 0ffh ; q. over 255?
jle bpcksum20 ; a. no .. continue
and dx, 0ffh ; dx = less than 255
inc dx ; ..plus one
bpcksum20: lodsb ; al = char from buffer
add dx, ax ; dx = acculation of chars
cmp dx, 0ffh ; q. over 255?
jle bpcksum30 ; a. no .. continue
and dx, 0ffh ; dx = less than 255
inc dx ; ..plus one
bpcksum30: loop bpcksum10 ; loop back till done
pop cx ; ..restore register
ret ; ..then return to caller
bpcksum endp
; ----------------------------------------------------------------------
; Calculate CRC entry: si -> message block
; cx = length of message
; exit: dx = returned CRC
; ----------------------------------------------------------------------
bpcrc proc
push bx ; save registers
push cx
push di
mov dx, -1 ; dx = initialize crc
mov bx, 1021h ; bx = working constant
mov di, 8000h ; di = constant, too
bpcrc10: lodsb ; al = char from message
xor ah, ah ; ax = cast char as integer
xchg al, ah ; shift left char by 8
xor dx, ax ; crc ^= (int)*ptr++ << 8
push cx ; save register
mov cx, 8 ; cx = bit loop count
bpcrc20: mov ax, dx ; ax = temp copy of crc
and ax, di ; q. bit on?
jz bpcrc30 ; a. no .. continue
shl dx, 1 ; shift left by one bit
xor dx, bx ; crc = crc << 1 ^ 0x1021
jmp short bpcrc40 ; ..continue with common code
bpcrc30: shl dx, 1 ; crc <<= 1
bpcrc40: loop bpcrc20 ; ..just loop thru this byte
pop cx ; restore register
loop bpcrc10 ; ..and loop thru whole message
pop di ; restore registers
pop cx
pop bx
ret ; return to caller
bpcrc endp
; ----------------------------------------------------------------------
; Setup output field
; - with count
; - DLE + "B"
; - move in data, applying quouting rules
; - call CRC/cksum routines
;
; entry: si -> user data w/terminating ETX
; di -> destination area
; cx = length
; exit: di -> 2byte length + data + CRC/cksum
; ----------------------------------------------------------------------
bd_setout proc
push di ; save registers
xor bx, bx ; bx = extra DLE bytes added
mov ax, cx ; get copy of length
stosw ; store in output record
push si ; save registers
test bpflg, bpfcrc ; q. using CRC?
jz bd_seto20 ; a. no .. calculate chksum
call bpcrc ; dx = CRC of the data
add bx, 2 ; bx = 2 extra bytes
jmp short bd_seto30 ; ..continue w/common code
bd_seto20: call bpcksum ; dl = checksum of data
inc bx ; bx = 1 extra byte
bd_seto30: pop si ; restore register
push dx ; save CRC/checksum
mov ax, 4210h ; ax = DLE + "B"
stosw ; save in output record
add bx, 2 ; bx = 2 more bytes
mov dx, bx ; dx = extra length, now
dec cx ; cx = len - 1, for ETX's sake
bd_seto40: lodsb ; al = source byte
cmp al, 1fh ; q. in the 1st range?
ja bd_seto50 ; a. no .. continue
xor ah, ah ; ax = (int) source byte
mov bx, ax ; bx = index into quote array
cmp bpqc00[bx], 0 ; q. need to quote char?
je bd_seto70 ; a. no .. continue
add al, 40h ; al = quoted character
jmp short bd_seto60 ; ..do rest of quoting stuff
bd_seto50: cmp al, 80h ; q. maybe in 2nd range?
jb bd_seto70 ; a. no .. normal character
cmp al, 9fh ; q. really in 2nd range?
ja bd_seto70 ; a. no .. must be normal
and al, 1fh ; al = 00 - 1fh
xor ah, ah ; ax = (int) source byte
mov bx, ax ; bx = index into quote array
cmp bpqc80[bx], 0 ; q. need to quote char?
je bd_seto70 ; a. no .. continue
add al, 60h ; al = quoted character
bd_seto60: mov ah, 10h ; ah = DLE
xchg al, ah ; swap around for store
stosw ; ..save in output record
inc dx ; dx = one more extra char
jmp short bd_seto80 ; ..continue w/common code
bd_seto70: stosb ; save char in output area
bd_seto80: loop bd_seto40 ; ..then loop till done
movsb ; copy ETX to output area
pop ax ; ax = CRC/checksum
stosw ; save one or the other
pop di ; di -> output count
add [di], dx ; add in "extra" bytes cnt
ret ; ..finally, return to caller
bd_setout endp
; ----------------------------------------------------------------------
; Unroll set quote mode fields
;
; entry: si -> source field of 8 bytes
; di -> destination field of 64 bytes
; ----------------------------------------------------------------------
bd_setq proc
mov cx, 8 ; cx = big loop size
bd_setq10: push cx ; save outer loop size
mov cx, 8 ; cx = bits per byte
lodsb ; al = source byte
mov ah, al ; ah = work value
bd_setq20: test ah, 80h ; q. bit set?
jz bd_setq30 ; a. no .. clear dest byte
mov al, 0ffh ; al = true value
jmp short bd_setq40 ; ..continue w/common code
bd_setq30: xor al, al ; al = false value
bd_setq40: stosb ; save in destination array
shl ah, 1 ; ah = next bit to test
loop bd_setq20 ; ..loop till byte done
pop cx ; restore outer loop count
loop bd_setq10 ; ..loop till array finished
ret ; ..finally, return to caller
bd_setq endp
; ---------------------------------------------------------------------
; Set bpseq to next sequence number
; ---------------------------------------------------------------------
nxtseq proc
inc bpseq ; increment seq number
cmp bpseq, '9' ; q. rollover?
jna nxtseq90 ; a. no .. exit
mov bpseq, '0' ; .. else return to '0'
nxtseq90: ret ; .. return to caller
nxtseq endp
; ---------------------------------------------------------------------
; Setup to "call" backdown di = return code
; ---------------------------------------------------------------------
callback proc
or flgs, flgfnc ; show were in callback mode
jmp short proto_exit ; return to caller
callback endp
; ---------------------------------------------------------------------
; Display error, return done bx -> string
; ---------------------------------------------------------------------
proto_err proc
mov di, 12 ; di = display string
call callback ; .. display it
jmp short proto_done ; tell bd we are done
proto_err endp
; ---------------------------------------------------------------------
; Protocol done - leave forever
; ---------------------------------------------------------------------
proto_done proc
push ds ; save our ds
les di, ubp_area ; es:di -> caller' area
lea si, bparea ; ds:di -> our area
mov cx, 5 ; words to move
cld ; ascending
rep movsw ; .. return 'em to user
pop ds ; restore our DS
mov di, 10 ; di = done code
jmp short proto_exit ; leave the protocol
proto_done endp
; ---------------------------------------------------------------------
; Exit with the OK code
; ---------------------------------------------------------------------
proto_ok proc
and flgs, not flgfnc ; zero out the function flag
mov di, 0 ; di = ok return code
jmp short proto_exit ; restore regs & exit
proto_ok endp
; ---------------------------------------------------------------------
; Protcol exit routine
; ---------------------------------------------------------------------
proto_exit proc
mov stackoffset, sp ; save our stack offset
cli ; no interrupts
mov ss, oldss ; ..restore callers ss
mov sp, oldsp ; ..and sp
sti ; ints ok again
pop ds ; restore caller's regs
pop es
FARRET ; return to caller
proto_exit endp
protocol ends
end start