home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pcmagazi
/
1992
/
09
/
bxmodm.asm
< prev
next >
Wrap
Assembly Source File
|
1992-03-29
|
35KB
|
851 lines
page 55,132
title "BXMODM - XMODEM Protocol Module"
; ---------------------------------------------------------------------
;
; Maintenance Log
;
; Version Date Description Who
; ------- ------- ---------------------------------- ----------------
; 1.0 08Feb91 Initial version complete Flanders/Holmes
;
; ---------------------------------------------------------------------
protocol segment para public 'code' ; Ascii module
assume cs:protocol
start: jmp bpmxmodem ; jump to start
db "BD" ; backdown protocol ID
db 2 ; number of protcols
db "X" ; invocation letter
db "Xmodem V1.0 ", 0 ; name to display
db 1 ; protocol number
db "C" ; invocation letter
db "Xmodem/CRC V1.0 ", 0 ; name to display
db 2 ; protocol number
; --------------------------------------------------------------------
; Macro(s)
; --------------------------------------------------------------------
FARRET macro ; far return macro
db 0cbh
endm
; ---------------------------------------------------------------------
; Protocol characters equates
; ---------------------------------------------------------------------
$SOH = 1 ; First byte of packets
$EOT = 4 ; Last byte of packet
$ACK = 6 ; Positive acknowledgement
$NAK = 21 ; Negative acknowledgement
$CAN = 24 ; Cancel transmission
; ---------------------------------------------------------------------
; 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_SOH = 0 ; Awaiting - between pkts
$ST_EOP = 2 ; Awaiting end of packet
$ST_CAN = 4 ; Cancelling protocol
$ST_UCAN = 6 ; User cancelled protocol
state dw $ST_INIT ; current state
state_jmp dw xmsoh ; 0: Awaiting SOH
dw xmeop ; 2: Awaiting end of pkt
dw xmcan ; 4: cancel
dw xmucan ; 6: user cancel
; ---------------------------------------------------------------------
; Data areas
; ---------------------------------------------------------------------
flgs db 0 ; various flags
flgfnc = 80h ; 1... .... callback active
flgrcv = 40h ; .1.. .... character rcv'd
flgcan = 20h ; ..1. .... cancel req'd
flgnrm = 10h ; ...1 .... normal XMODEM
flgsoh = 08h ; .... 1... first SOH rcvd
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
ONESEC = 19 ; A solid 1 sec in ticks
CHRTMOUT = 12 ; 12 second char timeout
CANTMOUT = 2 ; 2 seconds for char timout
MAXERRS = 10 ; maximum errors in row
SOHTMOUT = 22 ; Timeout for inter-packet NAK's
; Initial NAK to first SOH
timer db SOHTMOUT ; Timeout after first NAK
nseq db 1 ; next sequence number
nakch db 'C' ; NAK Character
errors db 0 ; error counter
cancnt db 0 ; count of cancels
filename db 65 dup (0) ; space for filename
packet db 133 dup (?) ; XMODEM packet buffer
rcvaddr dw offset packet ; address for next receive
pktsz db 133 ; size of expected packet
PKTSOH equ byte ptr packet ; SOH
PKTSEQ equ byte ptr packet+1 ; sequence number
PKTCSEQ equ byte ptr packet+2 ; complementary seq nbr
PKTDATA equ byte ptr packet+3 ; start of data
PKTCSUM equ byte ptr packet+131 ; checksum
PKTCRC equ word ptr packet+131 ; crc if XMODEM/CRC
PKTBUFLEN equ word ptr packet+1 ; length for write
oldds dw ? ; caller's ds
oldsp dw ? ; caller's sp
oldss dw ? ; caller's ss
init2x db 13, 'XMODEM: Init called twice', 13, 0
badfnc db 13, 'XMODEM: Bad function code', 13, 0
openerr db 13, 'XMODEM: Unable to open that file..'
fileprompt db 13, 'XMODEM: Enter filename, <CR> to exit: ', 0
nofile db 13, 'XMODEM: No filename, protocol aborted', 13, 0
fileopened db 13, 'XMODEM: File opened.. starting download', 13, 0
timedout db 13, 'XMODEM: Timeout, protocol ended', 13, 0
toomany db 13, 'XMODEM: Too many error, download cancelled', 13, 0
notinited db 13, 'XMODEM: BD did not call init first', 13, 0
usercan db 13, 'XMODEM: User cancelled download', 13, 0
sendercan db 13, 'XMODEM: Sender cancelled download', 13,0
success db 13, 'XMODEM: Download completed OK', 13, 0
writerr db 13, 'XMODEM: Write error.. protocol aborted', 13, 0
seqerr db 13, 'XMODEM: Packet sequence error', 13, 0
staterr0 db 13, 'XMODEM: State error 0', 13, 0
staterr1 db 13, 'XMODEM: State error 1', 13, 0
; ---------------------------------------------------------------------
; Protocol Mainline
; ---------------------------------------------------------------------
bpmxmodem proc
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 bpmxmodm90 ; a. no .. return "done"
test di, 1 ; q. is code even?
jnz bpmxmodm90 ; a. no .. return "done"
jmp jmptab[di] ; .. call requested routine
bpmxmodm90: lea bx, badfnc ; bx -> bad function code
jmp proto_err ; issue error and leave
bpmxmodem 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: cmp dl, 1 ; q. normal xmodem?
jne init02 ; a. no .. assume CRC
or flgs, flgnrm ; show normal wanted
mov pktsz, 132 ; .. size of packet
mov nakch, $NAK ; .. reset nak char
init02: mov ds, oldds ; ds -> callers segment
cmp byte ptr [bx], 0 ; q. any filename given?
je init10 ; a. no .. get the file name
lea di, filename ; di -> area for filename
mov si, bx ; si -> input filename
cld ; .. move upward
init05: movsb ; move in a byte
cmp di, offset filename+64 ; q. end of area?
je init20 ; a. yes .. continue
cmp byte ptr [si-1],0 ; q. end of string?
jne init05 ; a. no .. continue
push cs ; save our segment
pop ds ; .. ds -> our segment
jmp short init20 ; .. open the file
init10: lea bx, fileprompt ; bx -> filename prompt
init15: mov di, 12 ; di = display ASCIIZ
call callback ; .. ask bd to display
lea bx, filename ; bx -> filename
mov di, 16 ; di = get a line
call callback ; .. ask bd to get it
cmp filename, ' ' ; q. first char non-blank?
ja init20 ; a. yes .. continue
lea bx, nofile ; bx = no file given
jmp proto_err ; .. kill the protocol
init20: lea bx, filename ; bx -> filename
mov di, 2 ; di = open file
call callback ; q. file open ok?
jnc init25 ; a. yes .. continue
lea bx, openerr ; bx -> open error
jmp init15 ; Tell user .. ask for next
init25: lea bx, fileopened ; bx -> file opened msg
mov di, 12 ; di = display string
call callback ; .. ask bd to do it
mov timer, SOHTMOUT ; set timer to packet timeout
call nak ; send a NAK
mov bx, ONESEC ; bx = ticks for 1 second
mov di, 22 ; di = set tick downcounter
call callback ; .. set the down counter
jmp proto_ok ; .. and init is done
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 state_exec ; run the state machine
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 state_exec ; run the state machine
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 state_exec ; run the state machine
kbdchar endp
; ---------------------------------------------------------------------
; Run the state machine
; ---------------------------------------------------------------------
state_exec proc
cmp ticks, 0 ; q. timeout on ticks?
jg state10 ; a. no .. continue
dec timer ; countdown the timer
mov bx, ONESEC ; bx = ticks for 1 second
mov di, 22 ; di = set tick downcounter
call callback ; reset the down counter
state10: mov bx, state ; bx = state offset
jmp state_jmp[bx] ; .. go to appropriate state
state_exec endp
; ---------------------------------------------------------------------
; Awaiting SOH
; ---------------------------------------------------------------------
xmsoh proc
test flgs, flgrcv ; q. character received?
jz xmsoh50 ; a. no .. check timeouts
test flgs, flgcan ; q. cancel requested?
jz xmsoh05 ; a. no .. continue
mov bl, $CAN ; bl = CAN
mov di, 20 ; di = send one character
call callback ; .. send the cancel
mov state, $ST_UCAN ; set up for user cancel
jmp proto_ok ; .. and say we're ok
xmsoh05: cmp packet, $SOH ; q. packet received?
jne xmsoh10 ; a. no .. check next
call charcvd ; show char processed
or flgs, flgsoh ; show 1st SOH found
mov nakch, $NAK ; reset the NAK char
mov state, $ST_EOP ; show awaiting end pkt
jmp proto_ok ; .. show we are ok
xmsoh10: cmp packet, $CAN ; q. cancel by sender?
jne xmsoh15 ; a. no .. check next
mov state, $ST_CAN ; start the cancel
mov timer, SOHTMOUT ; .. restart the timer
jmp short xmsoh20 ; .. kill the character
xmsoh15: cmp packet, $EOT ; q. end of transmission?
jne xmsoh20 ; a. no .. continue
mov di, 6 ; close the file
call callback ; .. ask bd to do it
call ack ; send an ack
lea bx, success ; bx -> success message
call proto_err ; .. and we are done.
xmsoh20: and flgs, not flgrcv ; shut off receive bit
jmp proto_ok ; .. no change of state
xmsoh50: cmp timer, 0 ; q. timeout?
jng xmsoh55 ; a. yes .. process it
jmp proto_ok ; else .. continue
xmsoh55: inc errors ; add one to errors
cmp errors, 2 ; q. time to chg NAK char?
jb xmsoh90 ; a. no .. get out ok
test flgs, flgsoh ; q. first SOH found?
jnz xmsoh60 ; a. yes .. don't change
mov nakch, $NAK ; reset nak char
mov pktsz, 132 ; .. not CRC
or flgs, flgnrm ; .. normal XMODEM
xmsoh60: cmp errors, MAXERRS ; q. enough errors?
jb xmsoh90 ; a. no .. continue ok
xmsoh80: mov bl, $CAN ; bl = CAN
mov di, 20 ; di = send one character
call callback ; .. send the cancel
lea bx, timedout ; bx -> timeout message
jmp proto_err ; .. and kill the protocol
xmsoh90: call nak ; NAK 'em again
mov timer, SOHTMOUT ; reset timeout
jmp proto_ok ; .. and continue
xmsoh endp
; ---------------------------------------------------------------------
; Awaiting end of packet
; ---------------------------------------------------------------------
xmeop proc
test flgs, flgrcv ; q. character received?
jnz xmeop50 ; a. yes .. process it
cmp timer,0 ; q. Timeout?
jng xmeop05 ; a. yes .. process it
jmp proto_ok ; else .. continue
xmeop05: inc errors ; add one to errors
cmp errors, MAXERRS ; q. enough errors to die?
jb xmeop10 ; a. no .. continue
mov bl, $CAN ; bl = CAN
mov di, 20 ; di = send one character
call callback ; .. send the cancel
lea bx, timedout ; bx -> timeout message
jmp proto_err ; .. and kill the protocol
xmeop10: mov timer, SOHTMOUT ; reset tmout
call nak ; send a nak
jmp proto_ok ; and continue ..
xmeop50: call charcvd ; process the char
mov ax, rcvaddr ; get current rcv address
sub ax, offset packet ; ax = chars received
cmp al, pktsz ; q. packet received?
jb xmeop58 ; a. no .. continue recieve
call chkpkt ; q. packet ok?
jnc xmeop60 ; a. yes .. write & continue
inc errors ; .. add an error
cmp errors, MAXERRS ; q. too many errors?
jb xmeop55 ; a. no .. continue
mov bl, $CAN ; bl = CAN
mov di, 20 ; di = send one character
call callback ; .. send the cancel
lea bx, toomany ; bx -> too many error msg
jmp proto_err ; .. and kill the protocol
xmeop55: call nak ; nak the packet
mov timer, SOHTMOUT ; .. start the timer
xmeop58: jmp proto_ok ; .. and continue
xmeop60: mov al, nseq ; get the next seq nbr
cmp al, PKTSEQ ; q. same sequence?
jne xmeop90 ; a. no .. skip write
inc nseq ; nseq = next sequence nbr
mov PKTBUFLEN, 128 ; set the buffersize
lea bx, PKTBUFLEN ; bx -> buffer to write
mov di, 4 ; di = write to file
call callback ; q. BD write it ok?
jnc xmeop90 ; a. yes .. continue
mov bl, $CAN ; bl = CAN
mov di, 20 ; di = send one character
call callback ; .. send the cancel
lea bx, writerr ; bx -> write error
jmp proto_err ; .. and kill the protocol
xmeop90: call ack ; show it went well
mov timer, SOHTMOUT ; reset the timer
xmeop95: jmp proto_ok ; .. and continue
xmeop endp
; ---------------------------------------------------------------------
; Process cancel request
; ---------------------------------------------------------------------
xmcan proc
test flgs, flgrcv ; q. character received?
jz xmcan90 ; a. no .. continue
and flgs, not flgrcv ; cancel the character
cmp packet, $CAN ; q. cancel?
jne xmcan50 ; a. no .. wait
xmcan40: lea bx, sendercan ; bx -> sender cancelled
call proto_err ; .. cancel the protocol
xmcan50: call nak ; don't accept packet
mov timer, SOHTMOUT ; reset timer
jmp proto_ok ; .. continue
xmcan90: cmp timer, 0 ; q. timeout?
jng xmcan40 ; a. yes .. just die.
jmp proto_ok ; else .. continue
xmcan endp
; ---------------------------------------------------------------------
; Process user cancel request
; ---------------------------------------------------------------------
xmucan proc
test flgs, flgrcv ; q. character received?
jz xmucan50 ; a. no .. test for timeout
mov timer, CANTMOUT ; else .. renew timeout
and flgs, not flgrcv ; .. kill received char
mov rcvaddr, offset packet ; .. and next char addr
jmp proto_ok ; .. continue
xmucan50: cmp timer, 0 ; q. timeout yet?
jng xmucan60 ; a. yes .. die
jmp proto_ok ; else .. let it continue
xmucan60: lea bx, usercan ; bx -> user cancelled
jmp proto_err ; .. and kill the protocol
xmucan endp
; ---------------------------------------------------------------------
; Show received character processed
; ---------------------------------------------------------------------
charcvd proc
and flgs, not flgrcv ; shut off receive bit
inc rcvaddr ; point to next address
mov timer, CHRTMOUT ; set new character timeout
ret ; return to caller
charcvd endp
; ---------------------------------------------------------------------
; Check the received packet carry = error in packet
; ---------------------------------------------------------------------
chkpkt proc
lea si, PKTDATA ; si -> packet data
mov cx, 128 ; cl = len of data
test flgs, flgnrm ; q. CRC?
jnz chkpkt05 ; a. no .. do CHKSUM
call calc_crc ; calculate the CRC
xchg dh, dl ; .. make it little-endian
cmp dx, PKTCRC ; q. same CRC?
jmp short chkpkt10 ; .. test it later
chkpkt05: call calc_cksum ; calculate the chksum
cmp dl, PKTCSUM ; q. same chksum?
chkpkt10: jne chkpkt90 ; a. no .. kill it now
; Check the sequence number
mov al, PKTSEQ ; ah = sequence number
not al ; al = 1's complement
cmp al, PKTCSEQ ; q. same seq/Cseq?
jne chkpkt90 ; a. no .. bad packet
mov al, PKTSEQ ; al = packet's seq
mov ah, nseq ; .. and expected seq
cmp al, ah ; q. sequence ok?
je chkpkt80 ; a. yes .. continue
dec ah ; ah = previous sequence
cmp al, ah ; q. previous packet?
je chkpkt80 ; a. yes .. continue ..
lea bx, seqerr ; bx = sequence error
jmp short proto_err ; .. kill the protocol
chkpkt80: clc ; show it succeeded
ret ; return to caller
chkpkt90: stc ; show it failed
ret ; .. return to caller
chkpkt endp
; ----------------------------------------------------------------------
; Calculate chksum entry: si -> message block
; cx = length of message
; exit: dl = returned checksum
; ----------------------------------------------------------------------
calc_cksum proc
xor dl, dl ; dl = 0
calc_cks10: lodsb ; al = data char
add dl, al ; dl = checksum .. so far
loop calc_cks10 ; .. loop 'til done
ret
calc_cksum endp
; ----------------------------------------------------------------------
; Calculate CRC entry: si -> message block
; cx = length of message
; exit: dx = returned CRC
; ----------------------------------------------------------------------
calc_crc proc
xor dx, dx ; dx = initialize crc
mov bx, 1021h ; bx = working constant
mov di, 8000h ; di = constant, too
calc_c10: 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
calc_c20: mov ax, dx ; ax = temp copy of crc
and ax, di ; q. bit on?
jz calc_c30 ; a. no .. continue
shl dx, 1 ; shift left by one bit
xor dx, bx ; crc = crc << 1 ^ 0x1021
jmp short calc_c40 ; ..continue with common code
calc_c30: shl dx, 1 ; crc <<= 1
calc_c40: loop calc_c20 ; ..just loop thru this byte
pop cx ; restore register
loop calc_c10 ; ..and loop thru whole message
ret
calc_crc endp
; ---------------------------------------------------------------------
; Send a NAK
; ---------------------------------------------------------------------
nak proc
mov bl, nakch ; bl = current NAK char
mov di, 20 ; di = send one char
call callback ; .. ask BD to do it
mov state, $ST_SOH ; Awaiting new pkt
mov rcvaddr, offset packet ; .. reset address
ret ; .. return to caller
nak endp
; ---------------------------------------------------------------------
; Send an ACK
; ---------------------------------------------------------------------
ack proc
mov bl, $ACK ; bl = ACK char
mov di, 20 ; di = send one char
call callback ; .. ask BD to do it
mov bl, '.' ; show we're processing
mov di, 14 ; di = display one char
call callback ; .. ask BD to do it
mov state, $ST_SOH ; Awaiting new pkt
mov rcvaddr, offset packet ; .. reset address
mov errors, 0 ; .. and reset error cnt
ret ; .. return to caller
ack 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
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