home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / extra / nyenhuis3.arc / MSSRCV.ASM < prev    next >
Assembly Source File  |  1990-01-16  |  38KB  |  1,162 lines

  1.     NAME    mssrcv
  2. ; File MSSRCV.ASM
  3.     include mssdef.h
  4. ; Edit history
  5. ; Last edit 16 Jan 1990
  6. ; Sliding Windows
  7.  
  8.     public    read2, read, rrinit, ackpak, nakpak, rstate
  9.  
  10. setattr equ    57h            ; DOS get/set file's date and time
  11.  
  12. data     segment    public 'data'
  13.     extrn    encbuf:byte, decbuf:byte, fmtdsp:byte, flags:byte, trans:byte
  14.     extrn    dtrans:byte, sstate:byte, diskio:byte, auxfile:byte
  15.     extrn    maxtry:byte, fsta:word, errlev:byte, kstatus:word
  16.     extrn    rpacket:byte, wrpmsg:byte, numpkt:word, windlow:byte
  17.     extrn    charids:word, windflag:byte
  18.  
  19. cemsg    db    'User intervention',0
  20. ermes6    db    'Unknown packet type',0
  21. erms11    db    'Not enough disk space for file',0
  22. erms13    db    'Unable to send reply',0
  23. erms14  db    'No response from the host',0
  24. erms15    db    'Error. No buffers in receive routine',0
  25. erms29    db    'Rejecting file: ',0
  26. erms30    db    'File size',0
  27. erms31    db    'Date/time',0
  28. erms32    db    'Mailer request',0
  29. erms33    db    'File Type',0
  30. erms34    db    'Transfer Char-set',0
  31. erms36    db    'Unknown reason',0
  32. infms1  db    cr,'           Receiving: In progress',cr,lf,'$'
  33. infms3  db      'Completed',cr,lf,'$'
  34. infms4  db      'Failed',cr,lf,'$'
  35. infms6  db      'Interrupted',cr,lf,'$'
  36. infms7    db    'Discarding $'
  37. donemsg    db    '100%$'
  38. filhlp2 db      ' Local path or filename or carriage return$'
  39. ender    db    bell,bell,'$'
  40. crlf    db    cr,lf,'$'
  41. badrcv    db    0        ; local retry counter
  42. filopn    db    0        ; non-zero if disk file is open
  43. ftime    db    0,0        ; file time (defaults to 00:00:00)
  44. fdate    db    0,0        ; file date (defaults to 1 Jan 1980)
  45. attrib    db    0        ; attribute code causing file rejection
  46. rstate    db    0        ; state of automata
  47. temp    dw    0
  48. data    ends
  49.  
  50. code    segment    public 'code'
  51.     extrn    gofil:near, comnd:near, cntretry:near, chkwind:near
  52.     extrn    spack:near, rpack:near, serini:near, spar:near, rpar:near
  53.     extrn    init:near, cxmsg:near, cxerr:near, perpos:near
  54.     extrn    ptchr:near, ermsg:near, winpr:near, dskspace:near
  55.     extrn    stpos:near, rprpos:near, pakdup:near, packlen:near
  56.     extrn    dodec:near, doenc:near, errpack:near, intmsg:near
  57.     extrn    ihostr:near, getbuf:near, prtasz:near, begtim:near
  58.     extrn    endtim:near, pktsize:near,strlen:near,strcpy:near
  59.     extrn    bufclr:near, bufrel:near, pakptr:near, msgmsg:near
  60.     extrn    makebuf:near, clrbuf:near, pcwait:near, firstfree:near
  61.  
  62.     assume  cs:code, ds:data, es:nothing
  63.  
  64. ; Data structures comments.
  65. ; Received packet material is placed in buffers pointed at by [si].bufadr;
  66. ; SI is typically used as a pointer to a pktinfo packet structure.
  67. ; Sent packet material (typically ACK/NAKs) is placed in a standard packet
  68. ; structure named rpacket.
  69. ; Rpack and Spack expect a pointer in SI to the pktinfo structure for the
  70. ; packet.
  71.  
  72. ; RECEIVE command
  73.  
  74. READ    PROC    NEAR        
  75.     mov    bx,offset filhlp2    ; help message
  76.     mov    dx,offset auxfile    ; local file name string
  77.     mov    byte ptr auxfile,0    ; clear it first
  78.     mov    ah,cmword        ; local override filename/path
  79.     call    comnd        
  80.     jc    read1a            ; c = failure
  81.     mov    ah,cmeol        ; get a confirm
  82.     call    comnd
  83.     jc    read1a            ; c = failure
  84.     mov    rstate,'R'        ; set state to receive initiate
  85.     mov    flags.xflg,0
  86.     call    serini            ; initialize serial port
  87.     jnc    read1b            ; nc = success
  88.     or    errlev,2        ; set DOS error level
  89.     or    fsta.xstatus,2        ; set status, failed
  90.     or    kstatus,2        ; global status
  91.     test    flags.remflg,dquiet    ; quiet display mode?
  92.     jnz    read1a            ; nz = yes. Don't write to screen
  93.     mov    ah,prstr
  94.     mov    dx,offset infms4    ; Failed message
  95.     int    dos
  96.     stc
  97. read1a:    ret                ; return failure
  98.  
  99. read1b:    call    rrinit            ; init variables for read
  100.     call    clrbuf            ; clear serial port buffer
  101.     call    ihostr            ; initialize the host
  102.     cmp    flags.destflg,2        ; destination is screen?
  103.     je    read2            ; e = yes
  104.     call    init            ; setup display form
  105.     
  106.                     ; Called by GET & SRVSND, display ok
  107. READ2:    mov    kstatus,0        ; global status, success
  108.     mov    windflag,0        ; init windows in use display flag
  109.     mov    numpkt,0        ; set the number of packets to zero
  110.     mov    fsta.pretry,0        ; clear total retry counter
  111.     call    cxerr            ; clear Last Error line
  112.     call    cxmsg            ; clear Last Message line
  113.     call    begtim            ; start next statistics group
  114.     mov    flags.cxzflg,0        ; reset ^X/^Z flag
  115.     cmp    fmtdsp,0        ; formatted display?
  116.     je    read2a            ; e = no
  117.     call    stpos
  118.     mov    ah,prstr        ; Receiving in progress msg
  119.     mov    dx,offset infms1
  120.     int    dos
  121. read2a:    jmp    dispatch
  122. READ    ENDP
  123.  
  124. ; Call the appropriate action routines for each state of the protocol machine.
  125. ; State is held in byte rstate. Enter at label dispatch.
  126.  
  127. dispatch proc    near            ; dispatch on state variable rstate
  128.     mov    ah,rstate        ; get current state
  129.     cmp    ah,'R'            ; Receive initiate state?
  130.     jne    dispat2            ; ne = no
  131.     call    rinit
  132.     jmp    dispatch
  133.  
  134. dispat2:cmp    ah,'F'            ; File header receive state?
  135.     jne    dispat3
  136.     call    rfile            ; receive file header
  137.     jmp    dispatch
  138.  
  139. dispat3:cmp    ah,'D'            ; Data receive state?
  140.     jne    dispat4
  141.     call    rdata            ; get data packets
  142.     jmp    dispatch
  143.  
  144. dispat4:cmp    ah,'Z'            ; EOF?
  145.     jne    dispat5
  146.     call    reof            ; do EOF wrapup
  147.     jmp    dispatch
  148.  
  149. dispat5:cmp    ah,'E'            ; ^C or ^E abort?
  150.     jne    dispat6            ; ne = no
  151.     call    bufclr
  152.     mov    bx,offset cemsg        ; user intervention message
  153.     call    errpack            ; send error message
  154.     call    intmsg            ; show interrupt msg for Control-C-E
  155.  
  156.                     ; Receive Complete state processor
  157. dispat6:cmp    rstate,'C'        ; completed normally?
  158.     jne    dispat6a        ; ne = no
  159.     cmp    flags.cxzflg,0        ; interrupted?
  160.     je    dispat7            ; e = no, ended normally
  161. dispat6a:or    errlev,2        ; set DOS error level
  162.     or    fsta.xstatus,2+80h    ; set status, failed + intervention
  163.     or    kstatus,2+80h        ; global status
  164. dispat7:xor    ax,ax        ; tell statistics this is a receive operation
  165.     call    endtim            ; stop file statistics accumulator
  166.     call    bufclr            ; release all buffers
  167.     mov    windlow,0
  168.     cmp    rstate,'C'        ; receive complete state?
  169.     je    dispat8            ; e = yes
  170.     or    errlev,2        ; Failed, set DOS error level
  171.     or    fsta.xstatus,2        ; set status, failed
  172.     or    kstatus,2        ; global status
  173.     call    fileclose        ; close output file
  174.     call    filedel            ; delete incomplete file
  175.  
  176. dispat8:cmp    flags.destflg,2        ; receiving to screen?
  177.     je    dispa11            ; e = yes, nothing to clean up
  178.     test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  179.     jnz    dispa11            ; nz = yes, keep going
  180.     cmp    flags.xflg,0        ; writing to the screen?
  181.     jne    dispa11            ; ne = yes
  182.     call    stpos            ; position cursor to status line
  183.     mov    dx,offset infms3    ; completed message
  184.     cmp    rstate,'C'        ; receive complete state?
  185.     je    dispa10            ; e = yes
  186.     mov    dx,offset infms4    ; failed message
  187.     cmp    flags.cxzflg,0        ; interrupted?
  188.     je    dispa10            ; e = no, ended normally
  189.     mov    dx,offset infms6    ; interrupted message
  190. dispa10:mov    ah,prstr
  191.     int    dos
  192.     cmp    flags.belflg,0        ; bell desired?
  193.     je    dispa11            ; e = no
  194.     mov    ah,prstr
  195.     mov    dx,offset ender        ; ring the bell
  196.     int    dos
  197. dispa11:call    rprpos            ; put cursor at reprompt position
  198.     mov    flags.cxzflg,0        ; clear flag for next command
  199.     mov    auxfile,0        ; clear receive-as filename buffer
  200.     mov    flags.xflg,0        ; clear to-screen flag
  201.     mov    diskio.string,0        ; clear active filename buffer
  202.     mov    fsta.xname,0        ; clear statistics external name
  203.     clc                ; return to ultimate caller, success
  204.     ret
  205. dispatch endp
  206.  
  207. ;    Receive routines
  208.  
  209. ; Receive initiate packet (tolerates E F M S X Y types)
  210. RINIT    PROC    NEAR
  211.     mov    windlow,0        ; lowest acceptable packet number
  212.     mov    trans.chklen,1        ; Use 1 char for init packet
  213.     call    rcvpak            ; get a packet
  214.     jnc    rinit2            ; nc = success
  215.     ret
  216.  
  217. rinit2:    mov    ah,[si].pktype        ; examine packet type
  218.     cmp    ah,'S'            ; Send initiate packet?
  219.     je    rinit6            ; e = yes, process 'S' packet
  220.     cmp    ah,'M'            ; Message packet?
  221.     jne    rinit4            ; ne = no
  222.     call    msgmsg            ; display message
  223.     mov    trans.chklen,1        ; send Init checksum is always 1 char
  224.     call    ackpak0            ; ack and release packet
  225.     ret
  226.  
  227. rinit4:    cmp    ah,'F'            ; File receive?
  228.     je    rinit5            ; e = yes
  229.     cmp    ah,'X'            ; File receive to screen?
  230.     je    rinit5            ; e = yes
  231.     cmp    ah,'Y'            ; ACK to a REMOTE command?
  232.     jne    rinit4a            ; ne = no
  233.     call    msgmsg            ; show any message in the ACK
  234.     mov    rstate,'C'        ; Completed state
  235.     ret
  236.  
  237. rinit4a:cmp    ah,'N'            ; old NAK from a server?
  238.     jne    rinit4b            ; ne = no
  239.     call    bufrel            ; yes, release this packet buffer
  240.     ret                ;  and ignore it
  241.  
  242. rinit4b:mov    dx,offset ermes6    ; say unknown packet type
  243.     jmp    giveup            ; tell the other side
  244.  
  245. rinit5:    mov    rstate,'F'        ; File header receive state
  246.     ret
  247.  
  248.                     ; 'S' packet received
  249. rinit6:    call    spar            ; negotiate parameters
  250.     push    si
  251.     mov    si,offset rpacket    ; build response in this packet
  252.     call    rpar            ; report negotiated parameters
  253.     pop    si
  254.     mov    ah,trans.chklen        ; negotiated checksum length
  255.     push    ax            ; save it
  256.     mov    trans.chklen,1        ; use 1 char for init packet reply
  257.     mov    rstate,'F'        ; set state to file header
  258.     call    ackpak            ; ack with negotiated data
  259.     pop    ax            ; recover working checksum
  260.     mov    trans.chklen,ah
  261.     call    makebuf            ; remake buffering for new windowing
  262.     call    packlen            ; compute packet length
  263.     ret
  264. RINIT    ENDP
  265.  
  266.  
  267. ; Receive file header (tolerates E F M X Z types)
  268.  
  269. RFILE    PROC    NEAR
  270.     call    rcvpak            ; receive next packet
  271.     jnc    rfile1            ; nc = success
  272.     ret
  273.  
  274. rfile1: cmp    [si].pktype,'Z'        ; EOF?
  275.     jne    rfile2            ; ne = no, try next type
  276.     mov    rstate,'Z'        ; change to EOF state, SI is valid pkt
  277.     ret
  278.  
  279. rfile2: cmp    [si].pktype,'F'        ; file header (F or X packet)?
  280.     je    rfil3a            ; e = yes, 'F' pkt
  281.     cmp    [si].pktype,'X'        ; visual display header?
  282.     je    rfile3            ; e = yes, 'X' pkt
  283.     jmp    rfile5            ; neither one
  284.  
  285. rfile3:    mov    flags.xflg,1        ; 'X', say receiving to the screen
  286. rfil3a:    mov    filopn,0        ; assume not writing to a disk file
  287.     call    dodec            ; decode packet
  288.     call    cxerr            ; clear Last Error line
  289.     call    cxmsg            ; clear Last Message line
  290.     call    begtim            ; start statistics gathering
  291.     mov    al,dtrans.xchset    ; reset Transmission char set
  292.     mov    trans.xchset,al        ;  to the current user default
  293.     mov    al,dtrans.xtype        ; ditto for File Type
  294.     mov    trans.xtype,al
  295.     call    gofil            ; open the output file
  296.     jnc    rfile4            ; nc = success
  297.     jmp    giveup            ; failure, dx has message pointer
  298.  
  299. rfile4:    mov    filopn,2        ; say file is open for writing
  300.     push    si
  301.     push    di
  302.     mov    si,offset decbuf    ; local filename is here
  303.     mov    di,offset encbuf    ; destination is encoding buffer
  304.     mov    byte ptr [di],' '    ; leave space for protocol char
  305.     inc    di            ;  so other Kermits do not react
  306.     call    strcpy            ; copy it, to echo local name to host
  307.     dec    di
  308.     mov    dx,di
  309.     call    strlen            ; get length to cx for doenc
  310.     mov    si,offset rpacket    ; use this packet buffer
  311.     call    doenc            ; encode buffer, cx gets length
  312.     pop    di
  313.     pop    si
  314.     mov    rstate,'D'        ; set the state to data receive
  315.     jmp    ackpak            ; ack the packet, with filename
  316.  
  317. rfile5:    mov    ah,[si].pktype        ; get reponse packet type
  318.     cmp    ah,'B'            ; 'B' End Of Transmission?
  319.     jne    rfile6            ; ne = no
  320.     mov    rstate,'C'        ; set state to Complete
  321.     jmp    ackpak0            ; ack the packet
  322.  
  323. rfile6:    cmp    ah,'M'            ; Message packet?
  324.     jne    rfile7            ; ne = no
  325.     call    msgmsg            ; display message
  326.     jmp    ackpak0            ; ack packet, stay in this state
  327.  
  328. rfile7:    mov    dx,offset ermes6    ; unknown packet type
  329.     jmp    giveup            ; tell both sides and quit
  330. RFILE    ENDP
  331.  
  332. ; Get file attributes from packet
  333. ; Recognize file size in bytes and kilobytes (used if bytes missing),
  334. ; file time and date. Reject Mail commands. Return carry clear for success,
  335. ; carry set for failure. If rejecting place reason code in byte attrib.
  336.  
  337. GETATT    PROC    NEAR
  338.     mov    bx,[si].datadr        ; pointer to data field
  339. getat0:    push    bx
  340.     sub    bx,[si].datadr        ; bx = length to examine
  341.     cmp    bx,[si].datlen        ; are we beyond end of data?
  342.     pop    bx
  343.     jl    getat1            ; l = not yet
  344.     clc
  345.     ret                ; has carry clear for success
  346.  
  347. getat1:    cmp    byte ptr [bx],'1'    ; Byte length field?
  348.     jne    getat2            ; ne = no
  349.     mov    al,[bx]            ; remember attribute
  350.     mov    attrib,al
  351.     inc    bx            ; pointer
  352.     test    flags.attflg,attlen    ; allowed to examine file length?
  353.     jnz    getat1a            ; nz = yes
  354.     jmp    getatunk        ; z = no, ignore
  355. getat1a:push    si
  356.     call    getas            ; get file size
  357.     call    spchk            ; check available disk space
  358.     pop    si
  359.     jnc    getat0            ; nc = have enough space for file
  360.     ret                ; return failure
  361.  
  362. getat2:    cmp    byte ptr [bx],'!'    ; Kilobyte length field?
  363.     jne    getat3            ; ne = no
  364.     test    flags.attflg,attlen    ; allowed to examine file length?
  365.     jnz    getat2b            ; nz = yes
  366. getat2a:jmp    getatunk        ; z = no, ignore
  367. getat2b:mov    al,[bx]            ; remember attribute
  368.     mov    attrib,al
  369.     inc    bx            ; pointer
  370.     call    getak            ; get file size
  371.     jc    getat2a            ; carry means decode rejected
  372.     call    spchk            ; check available disk space
  373.     jnc    short getat0
  374.     ret                ; return failure
  375.  
  376. getat3:    cmp    byte ptr [bx],'#'    ; date field?
  377.     jne    getat4            ; ne = no
  378.     mov    word ptr ftime,0    ; clear time and date fields
  379.     mov    word ptr fdate,0
  380.     test    flags.attflg,attdate    ; allowed to update file date/time?
  381.     jnz    getat3a            ; nz = yes
  382.     jmp    getatunk        ; z = no, ignore
  383. getat3a:mov    al,[bx]            ; remember attribute
  384.     mov    attrib,al
  385.     inc    bx
  386.     call    getatd            ; get file date
  387.     jmp    short getat0
  388.  
  389. getat4:    cmp    byte ptr [bx],'+'    ; Disposition?
  390.     jne    getat5            ; ne = no
  391.     mov    al,[bx]            ; remember attribute
  392.     mov    attrib,al
  393.     cmp    byte ptr [bx+2],'M'    ; Mail indicator?
  394.     je    getat4a            ; e = yes, fail
  395.     jmp    getatunk        ; ne = no, ignore field
  396. getat4a:stc                ; set carry for failure
  397.     ret
  398.  
  399. getat5:    cmp    byte ptr [bx],'"'    ; File Type?
  400.     jne    getat6            ; ne = no
  401.            test    flags.attflg,atttype    ; allowed to examine file type?
  402.     jnz    getat5a            ; nz = yes
  403.     jmp    getatunk        ; z = no, ignore
  404. getat5a:mov    attrib,'"'        ; remember attribute
  405.     inc    bx            ; length field
  406.     xor    ch,ch
  407.     mov    cl,[bx]            ; get length
  408.     inc    bx
  409.     sub    cl,20h            ; remove ascii bias
  410.     jc    getat5d            ; c = error in length, fail
  411.     cmp    byte ptr [bx],'A'    ; Type letter (A, B, I), Ascii?
  412.     jne    getat5b            ; ne = no
  413.     mov    trans.xtype,0        ; say Ascii/Text file type
  414.     add    bx,cx            ; step to next field
  415.     jmp    getat0            ; next item please
  416. getat5b:cmp    byte ptr [bx],'B'    ; "B" Binary?
  417.     jne    getat5d            ; ne = no, fail
  418.     cmp    cl,2            ; full "B8"?
  419.     jb    getat5c            ; b = no, just "B"
  420.     cmp    byte ptr [bx+1],'8'    ; proper length?
  421.     jne    getat5d            ; ne = no
  422. getat5c:mov    trans.xtype,1        ; say Binary
  423.     add    bx,cx            ; step to next field
  424.     jmp    getat0            ; next item please
  425. getat5d:stc                ; set carry for rejection
  426.     ret
  427.  
  428. getat6:    cmp    byte ptr [bx],'*'    ; character set usage?
  429.     jne    getat6d            ; ne = no
  430.     test    flags.attflg,attchr    ; allowed to examine char-set?
  431.     jnz    getat6a            ; nz = yes
  432. getat6d:jmp    getatunk        ; z = no, ignore
  433. getat6a:mov    attrib,'*'        ; remember attribute
  434.     inc    bx            ; length field
  435.     xor    ch,ch
  436.     mov    cl,[bx]            ; get length
  437.     inc    bx
  438.     sub    cl,20h            ; remove ascii bias
  439.     js    getat6c            ; c = length error, fail
  440.     mov    trans.xchset,0        ; assume Transparent Transfer char-set
  441.     cmp    byte ptr [bx],'A'    ; Normal Transparent?
  442.     jne    getat6b            ; be = not Transparent
  443.     add    bx,cx            ; point at next field
  444.     clc
  445.     jmp    getat0
  446. getat6b:cmp    byte ptr [bx],'C'    ; character set?
  447.     je    getat7            ; e = yes
  448. getat6c:stc                ; set carry for rejection
  449.     ret
  450. getat7:    push    si            ; examine transfer character set
  451.     mov    si,bx            ; point at first data character
  452.     add    bx,cx            ; point bx beyond the text
  453.     dec    cx            ; deduct leading 'C' char from count
  454.     push    bx            ; save bx
  455.     mov    bx,offset charids    ; point to array of char set info
  456.     mov    ax,[bx]            ; number of members
  457.     mov    temp,ax            ; loop counter
  458.     mov    trans.xchset,0        ; assume xfer char-set 0 (Transparent)
  459. getat7a:add    bx,2            ; point to a member's address
  460.     mov    di,[bx]            ; point at member [length, string]
  461.     cmp    cl,[di]            ; string lengths the same?
  462.     jne    getat7b            ; ne = no, try the next member
  463.     inc    si            ; skip the 'C'
  464.     inc    di            ; point at ident string
  465.     cld
  466.     push    es
  467.     push    ds
  468.     pop    es            ; set es:di to data segment
  469.     push    cx            ; save incoming count
  470.     push    si            ; save incoming string pointer
  471.     repe    cmpsb            ; compare cx characters
  472.     pop    si
  473.     pop    cx
  474.     pop    es
  475.     jne    getat7b            ; ne = idents do not match
  476.     pop    bx            ; a match, use current trans.xchset
  477.     pop    si
  478.     clc
  479.     jmp    getat0            ; success
  480. getat7b:inc    trans.xchset        ; try next set
  481.     dec    temp            ; one less member to consider
  482.     jnz    getat7a            ; nz = more members to try
  483.     pop    bx            ; failure to find a match
  484.     pop    si
  485.     mov    trans.xchset,0        ; use Transparent for unknown char set
  486.     cmp    flags.unkchs,0        ; keep the file?
  487.     je    getat7c            ; e = yes, regardless of unk char set
  488.     stc                ; set carry for rejection
  489.     ret
  490. getat7c:jmp    getat0            ; report success anyway
  491.  
  492.                     ; workers for above
  493. getatunk:inc    bx            ; Unknown. Look at length field
  494.     mov    al,[bx]
  495.     sub    al,' '            ; remove ascii bias
  496.     xor    ah,ah
  497.     inc    ax            ; include length field byte
  498.     add    bx,ax            ; skip to next attribute
  499.     jmp    getat0
  500.                     ; Decode File length (Byte) field
  501. getas:    mov    cl,[bx]            ; length of file size field
  502.     inc    bx            ; point at file size data
  503.     sub    cl,' '            ; remove ascii bias
  504.     mov    ch,0
  505.     mov    ax,0            ; current length, bytes
  506.     mov    dx,0
  507. getas2:    push    cx
  508.     shl    dx,1            ; high word of size, times two
  509.     mov    di,dx            ; save
  510.     shl    dx,1
  511.     shl    dx,1            ; times 8
  512.     add    dx,di            ; yields dx * 10
  513.     mov    di,dx            ; save dx
  514.     mov    dx,0
  515.     mov    cx,10            ; also clears ch
  516.     mul    cx            ; scale up previous result in ax
  517.     mov    cl,[bx]            ; get a digit
  518.     inc    bx
  519.     sub    cl,'0'            ; remove ascii bias
  520.     add    ax,cx            ; add to current length
  521.     adc    dx,0            ; extend result to dx
  522.     add    dx,di            ; plus old high part
  523.     pop    cx
  524.     loop    getas2
  525.     mov    diskio.sizelo,ax    ; low order word
  526.     mov    diskio.sizehi,dx    ; high order word
  527.     ret
  528.                     ; Decode Kilobyte attribute
  529. getak:    mov    ax,diskio.sizelo    ; current filesize, low word
  530.     add    ax,diskio.sizehi
  531.     cmp    ax,0            ; zero if not used yet
  532.     je    getak1            ; e = not used before
  533.     dec    bx            ; backup pointer
  534.     stc                ; set carry to ignore this field
  535.     ret
  536.  
  537. getak1:    call    getas            ; parse as if Byte field
  538.     mov    ax,diskio.sizelo    ; get low word of size
  539.     mov    dx,diskio.sizehi    ; high word
  540.     mov    dh,dl            ; times 256
  541.     mov    dl,ah
  542.     mov    ah,al
  543.     mov    al,0
  544.     shl    dx,1            ; times four to make times 1024
  545.     shl    dx,1
  546.     rol    ax,1            ; two high bits of ah to al
  547.     rol    ax,1
  548.     and    al,3            ; keep them
  549.     or    dl,al            ; insert into high word
  550.     mov    al,0
  551.     mov    diskio.sizehi,dx    ; store high word
  552.     mov    diskio.sizelo,ax    ; store low word
  553.     clc                ; clear carry
  554.     ret
  555.                     ; File date and time
  556. getatd:    mov    word ptr ftime,1    ; two seconds past midnight
  557.     mov    word ptr fdate,0
  558.     mov    dl,[bx]            ; field length
  559.     mov    dh,0
  560.     sub    dl,' '            ; remove ascii bias
  561.     inc    bx            ; next field
  562.     add    dx,bx            ; where next field begins
  563.     mov    temp,dx            ; save in temp
  564.     cmp    byte ptr[bx+6],' '    ; short form date (yymmdd)?
  565.     je    getad2            ; e = yes
  566.     add    bx,2            ; skip century digits (19)
  567. getad2:    mov    ax,10
  568.     mov    dx,[bx]            ; get year tens and units digits
  569.     add    bx,2            ; dl has tens, dh has units
  570.     sub    dx,'00'            ; remove ascii bias
  571.     mul    dl            ; ax = high digit times ten
  572.     add    al,dh            ; units digit
  573.     sub    ax,80            ; remove rest of 1980 bias
  574.     jns    getad2a            ; ns = no sign = non-negative result
  575.     mov    ax,0            ; don't store less than 1980
  576. getad2a:shl    al,1            ; adjust for DOS bit format
  577.     mov    fdate+1,al
  578.     mov    ax,[bx]            ; get month digits
  579.     add    bx,2
  580.     sub    ax,'00'            ; remove ascii bias
  581.     cmp    al,0            ; tens digit set?
  582.     je    getad2b            ; e = no
  583.     add    ah,10            ; add to units digit
  584. getad2b:cmp    ah,8            ; high bit of month set?
  585.     jb    getad3            ; b = no
  586.     or    fdate+1,1
  587.     sub    ah,8            ; and deduct it here
  588. getad3:    mov    cl,5
  589.     shl    ah,cl            ; normalize months bits
  590.     mov    fdate,ah
  591.     mov    dx,[bx]            ; do day of the month
  592.     add    bx,2            ; dh has units, dl has tens digit
  593.     sub    dx,'00'            ; remove ascii bias
  594.     mov    ax,10
  595.     mul    dl            ; ax = ten times tens digit
  596.     add    al,dh            ; plus units digit
  597.     or    fdate,al
  598.     cmp    bx,temp            ; are we at the end of this field?
  599.     jae    getad5            ; ae = yes, prematurely
  600.     inc    bx            ; skip space separator
  601.     mov    ax,10            ; prepare for hours
  602.     mov    dx,[bx]            ; hh digits
  603.     add    bx,2
  604.     sub    dx,'00'            ; remove ascii bias
  605.     mul    dl            ; 10*high digit of hours
  606.     add    al,dh            ; plus low digit of hours
  607.     mov    cl,3            ; normalize bits
  608.     shl    al,cl
  609.     mov    ftime+1,al        ; store hours
  610.     inc    bx            ; skip colon
  611.     mov    ax,10            ; prepare for minutes
  612.     mov    dx,[bx]            ; mm digits
  613.     add    bx,2
  614.     sub    dx,'00'            ; remove ascii bias
  615.     mul    dl            ; 10*high digit of minutes
  616.     add    al,dh            ; plus low digit of minutes
  617.     mov    ah,0
  618.     mov    cl,5            ; normalize bits
  619.     shl    ax,cl
  620.     or    ftime+1,ah        ; high part of minutes
  621.     mov    ftime,al        ; low part of minutes
  622.     cmp    bx,temp            ; are we at the end of this field
  623.     jae    getad5            ; ae = yes, quit here
  624.     inc    bx            ; skip colon
  625.     mov    ax,10            ; prepare for seconds
  626.     mov    dx,[bx]            ; ss digits
  627.     add    bx,2
  628.     sub    dx,'00'            ; remove ascii bias
  629.     mul    dl            ; 10*high digit of seconds
  630.     add    al,dh            ; plus low digit of seconds
  631.     shr    al,1            ; store as double-seconds for DOS
  632.     or    ftime,al        ; store seconds
  633. getad5:    ret
  634. GETATT    ENDP
  635.  
  636. ; Receive data (tolerates A D E M Z types)
  637.  
  638. RDATA    PROC    NEAR
  639.     call    rcvpak            ; get next packet
  640.     jnc    rdata1            ; nc = success
  641.     ret                ; else return to do new state
  642.  
  643. rdata1:    mov    ah,[si].pktype        ; check packet type
  644.     cmp    ah,'D'            ; Data packet?
  645.     je    rdata3            ; e = yes
  646.     cmp    ah,'A'            ; Attributes packet?
  647.     je    rdata4            ; e = yes
  648.     cmp    ah,'M'            ; Message packet?
  649.     jne    rdat2            ; ne = no
  650.     call    msgmsg            ; display message
  651.     jmp    ackpak0            ; ack the packet, stay in this state
  652.  
  653. rdat2:    cmp    ah,'Z'            ; EOF packet?
  654.     jne    rdat2a            ; ne = no
  655.     mov    rstate,'Z'        ; next state is EOF, do not ack yet
  656.     ret
  657.  
  658. rdat2a:    mov    dx,offset ermes6    ; Unknown packet type
  659.     jmp    giveup
  660.  
  661. rdata3:    call    ptchr            ; decode 'D' packet, output to file
  662.     jc    rdat3a            ; c = failure to write output
  663.     jmp    ackpak0            ; ack the packet, stay in this state
  664.  
  665. rdat3a:    mov    dx,offset erms11    ; cannot store all the data
  666.     jmp    giveup            ; tell the other side
  667.  
  668.                          ; 'A' packet, analyze        
  669. rdata4:    call    getatt            ; get file attributes from packet
  670.     mov    cx,0            ; reply length, assume 0/nothing
  671.     jnc    rdat4b            ; nc = success, attributes accepted
  672.     mov    cx,2            ; 2 bytes, declining the file
  673.     mov    encbuf,'N'        ; decline the transfer
  674.     mov    al,attrib        ; get attribute causing rejection
  675.     mov    encbuf+1,al        ; report rejection reason to sender
  676.     or    fsta.xstatus,2        ; set status, failed
  677.     mov    kstatus,2        ; global status, failed
  678.     test    flags.remflg,dquiet    ; quiet display?
  679.     jnz    rdat4b            ; nz = yes
  680.     push    si
  681.     push    cx
  682.     push    ax
  683.     mov    dx,offset erms29    ; say rejecting the file
  684.     call    ermsg            ; show rejecting file, then reason
  685.     pop    ax
  686.     mov    dx,offset erms30
  687.     cmp    al,'1'            ; Byte count?
  688.     je    rdat4a            ; e = yes
  689.     cmp    al,'!'            ; Kilobyte count?
  690.     je    rdat4a            ; e = yes
  691.     mov    dx,offset erms31
  692.     cmp    al,'#'            ; Date and Time?
  693.     je    rdat4a            ; e = yes
  694.     mov    dx,offset erms32
  695.     cmp    al,'+'            ; Mail?
  696.     je    rdat4a            ; e = yes
  697.     mov    dx,offset erms33
  698.     cmp    al,'"'            ; File Type?
  699.     je    rdat4a
  700.     mov    dx,offset erms34
  701.     cmp    al,'*'            ; Transfer Char-set?
  702.     je    rdat4a
  703.     mov    dx,offset erms36    ; unknown reason
  704. rdat4a:    call    prtasz            ; display reason
  705.     pop    cx
  706.     pop    si
  707. rdat4b:    push    si
  708.     mov    si,offset rpacket    ; encode to this packet
  709.     call    doenc            ; do encoding
  710.     pop    si
  711.     jmp    ackpak            ; ACK the attributes packet
  712. rdata endp
  713.  
  714. ; End of File processor (expects Z type to have been received elsewhere)
  715. ; Enter with packet pointer in SI to a 'Z' packet.
  716. reof    proc    near            ; 'Z' End of File packet
  717.     cmp    flags.cxzflg,0        ; interrupted?
  718.     jne    reof3            ; ne = yes, no 100% done indicator
  719.     cmp    fmtdsp,0        ; formatted screen?
  720.     je    reof5            ; e = no, no message
  721.     cmp    wrpmsg,0        ; written Percentage done yet?
  722.     je    reof5            ; e = no
  723.     call    perpos            ; position cursor to percent done
  724.     mov    dx,offset donemsg    ; say 100%
  725.     mov    ah,prstr
  726.     int    dos
  727.     jmp    short reof5        ; file close common code
  728.  
  729. reof3:    call    intmsg            ; show interrupt msg on local screen
  730.     or    errlev,2        ; set DOS error level
  731.     or    fsta.xstatus,2+80h    ; set status, failed + intervention
  732.     mov    kstatus,2+80h        ; global status
  733.     cmp    flags.cxzflg,'X'    ; kill one file?
  734.     jne    reof5            ; ne = no
  735.     mov    flags.cxzflg,0        ; clear ^X so next file survives
  736.                     ; common code for file closing
  737. reof5:    call    fileclose        ; close the file
  738.     call    dodec            ; decode incoming packet to decbuf
  739.     cmp    decbuf,'D'        ; is the data "D" for discard?
  740.     jne    reof7            ; ne = no, write out file
  741.     call    filedel            ; delete file incomplete file
  742.     or    errlev,2        ; set DOS error level
  743.     or    fsta.xstatus,2+80h    ; set status, failed + intervention
  744.     mov    kstatus,2+80h        ; global status
  745.  
  746. reof7:    mov    rstate,'F'
  747.     call    ackpak0            ; acknowledge the packet
  748.     mov    ax,0            ; tell statistics this was a receive
  749.     call    endtim
  750.     mov    diskio.string,0        ; clear file name
  751.     ret
  752. reof    endp
  753.  
  754. ; init variables for read
  755. rrinit    proc    near
  756.     call    makebuf            ; construct & clear all buffer slots
  757.     call    packlen            ; compute packet length
  758.     mov    numpkt,0        ; set the number of packets to zero
  759.     mov    windlow,0        ; starting sequence number of zero
  760.     mov    fsta.pretry,0        ; set the number of retries to zero
  761.     mov    filopn,0        ; say no file opened yet
  762.     mov    windflag,0        ; windows in use init flag
  763.     mov    fmtdsp,0        ; no formatted display yet
  764.     ret
  765. rrinit    endp
  766.  
  767. ; Deliver packets organized by sequence number.
  768. ; Delivers a packet pointer in SI whose sequence number matches windlow.
  769. ; If necessary a new packet is requested from the packet recognizer. Failures
  770. ; to receive are managed here and may generate NAKs. Updates formatted screen.
  771. ; Store packets which do not match windlow, process duplicates and strays.
  772. ; Error packet and ^C/^E interrupts are detected and managed here.
  773. ; Return success with carry clear and SI holding the packet structure address.
  774. ; Return failure with carry set, maybe with a new rstate.
  775.  
  776. rcvpak    proc    near
  777.     mov    al,windlow        ; sequence number we want
  778.     call    pakptr            ; find pkt pointer with this seqnum
  779.     mov    si,bx            ; the packet pointer
  780.     jnc    rcvpa1a            ; nc = got one, else read fresh pkt
  781.     call    getbuf            ; get a new buffer address into si
  782.     jnc    rcvpa1            ; nc = success
  783.     mov    bx,offset erms15    ; insufficient buffers
  784.     jmp    giveup
  785.  
  786. rcvpa1:    call    winpr            ; show window slots in use
  787.     call    rpack            ; receive a packet, si has buffer ptr
  788.     jc    rcvpa2            ; c = failure to receive, analyze
  789.     inc    numpkt            ; increment the number of packets
  790.     cmp    flags.xflg,0        ; receiving to screen?
  791.     jne    rcvpa1a            ; ne = yes, skip displaying
  792.     cmp    flags.destflg,2        ; destination is screen?
  793.     je    rcvpa1a            ; e = yes
  794.     call    pktsize            ; report packet qty and size
  795. rcvpa1a:jmp    rcvpa6            ; success, validate
  796. ; ------------------- failure to receive any packet -------------------------
  797.                     ; Reception failed. What to do?
  798. rcvpa2:    call    cntretry        ; update retries, detect ^C, ^E
  799.     jc    rcvpa2a            ; c = exit now from ^C, ^E
  800.     call    bufrel            ; discard unused buffer
  801.     inc    badrcv            ; count receive retries
  802.     mov    al,badrcv        ; count # bad receptions in a row
  803.     cmp    al,maxtry        ; too many?
  804.     jb    rcvpa4            ; b = not yet, NAK intelligently
  805.     mov    dx,offset erms14    ; no response from host
  806.     jmp    giveup            ; tell the other side
  807.  
  808. rcvpa2a:call    bufrel            ; discard unwanted buffer
  809.     stc                ; set carry for failure
  810.     ret                ; move to Error state
  811.  
  812.                     ; do NAKing
  813. rcvpa4:    mov    al,windlow        ; Timeout or Crunched packet
  814.     add    al,trans.windo        ; find next slot after last good
  815.     dec    al
  816.     and     al,3fh            ; start at window high
  817.     mov    ah,-1            ; set a not-found marker
  818.     mov    cl,trans.windo        ; cx = number of slots to examine
  819.     xor    ch,ch
  820. rcvpa4a:call    pakptr            ; sequence number (in AL) in use?
  821.     jnc    rcvpa4b            ; nc = yes, stop here
  822.     mov    ah,al            ; remember seqnum of highest vacancy
  823.     dec    al            ; work backward in sequence numbers
  824.     and    al,3fh
  825.     loop    rcvpa4a
  826.  
  827. rcvpa4b:mov    al,ah            ; last-found empty slot (-1 = none)
  828.     cmp    ah,-1            ; found a vacant slot?
  829.     jne    rcvpa4c            ; ne = no, else use first free seqnum
  830.     call    firstfree        ; set AL to first open slot
  831.     jc    rcvpa4d            ; c = no free slots, an error
  832. rcvpa4c:mov    rpacket.seqnum,al    ; NAK this unused sequence number
  833.     call    nakpak            ; NAK using rpacket
  834.     jc    rcvpa4d            ; c = failure on sending operation
  835.     stc                ; rcv failure, stay in current state
  836.     ret
  837.  
  838. rcvpa4d:mov    dx,offset erms13    ; failure, cannot send reply
  839.     jmp    giveup            ; show msg, change states
  840. ; ------------------------- received a packet ------------------------------
  841.             ; remove duplicates, validate sequence number
  842. rcvpa6:    cmp    [si].pktype,'E'        ; Error packet? Accept w/any seqnum
  843.     jne    rcvpa6a            ; ne = no
  844.     jmp    error            ; display message, change states
  845.  
  846. rcvpa6a:mov    al,[si].seqnum        ; this packet's sequence number
  847.     mov    rpacket.seqnum,al    ; save here for reply
  848.     call    pakdup            ; set ah to number of copies
  849.     cmp    ah,1            ; more than one copy?
  850.     jbe    rcvpa7            ; be = no, just one
  851.     call    bufrel            ; discard duplicate
  852.     mov    al,rpacket.seqnum    ; recover current sequence number
  853.     call    pakptr            ; get packet pointer for original
  854.     mov    si,bx            ; should not fail if pakdup works ok
  855.     jnc    rcvpa7            ; nc = ok, work on the original again
  856.     ret                ; say failure, stay in current state
  857.  
  858. rcvpa7:    call    chkwind            ; validate sequence number (cx=status)
  859.     jc    rcvpa7b            ; c = outside current window
  860.     mov    al,[si].seqnum        ; get sequence number again
  861.     cmp    al,windlow        ; is it the desired sequence number?
  862.     jne    rcvpa7a            ; ne = no, do not change states yet
  863.     mov    badrcv,0        ; clear retry counter
  864.     clc
  865.     ret                ; return success, SI has packet ptr
  866.  
  867. rcvpa7a:stc                ; not desired pkt, stay in this state
  868.     ret                ; do not increment retry counter here
  869.  
  870. rcvpa7b:cmp    cx,0            ; inside previous window?
  871.     jg    rcvpa7c            ; g = outside any window, ignore it
  872.     cmp    [si].pktype,'I'        ; let 'I' and 'S' pkts be reported
  873.     je    rcvpa7d            ; even if in previous window, to
  874.     cmp    [si].pktype,'S'        ; accomodate lost ack w/data
  875.     je    rcvpa7d
  876.     call    ackpak0            ; previous window, ack and ignore it
  877.     stc                ; rcv failure, stay in current state
  878.     ret
  879.  
  880. rcvpa7d:mov    rstate,'R'        ; redo initialization when 'I'/'S'
  881.     stc                ;  are observed, keep current pkt
  882.     ret
  883.  
  884. rcvpa7c:call    bufrel            ; ignore packet outside of any window
  885.     stc                ; rcv failure, stay in current state
  886.     ret
  887. rcvpak    endp
  888.  
  889. ; Send ACK packet. Enter with rpacket data field set up.
  890. ; ACKPAK sends ack with data, ACKPAK0 sends ack without data.
  891. ackpak    proc    near            ; send an ACK packet
  892.     cmp    rpacket.datlen,0    ; really just no data?
  893.     jne    ackpa2            ; ne = no, send prepared ACK packet
  894. ackpak0:mov    rpacket.datlen,0    ; no data
  895.     cmp    flags.cxzflg,0        ; user interruption?
  896.     je    ackpa2            ; e = no
  897.     push    cx            ; yes, send the interrupt character
  898.     push    si
  899.     mov    si,offset rpacket
  900.     mov    cl,flags.cxzflg        ; send this so host knows about ^X/^Z
  901.     mov    encbuf,cl        ; put datum into the encode buffer
  902.     mov    cx,1            ; data size of 1 byte
  903.     call    doenc            ; encode, char count is in cx
  904.     pop    si
  905.     pop    cx
  906. ackpa2:    mov    rpacket.pktype,'Y'    ; ack packet
  907.     mov    rpacket.numtry,0
  908. ackpa3:    push    si
  909.     mov    si,offset rpacket
  910.     call    spack            ; send the packet
  911.     pop    si
  912.     jnc    ackpa4            ; nc = success
  913.     cmp    flags.cxzflg,'C'    ; Control-C abort?
  914.     je    ackpa3a            ; e = yes, quit now
  915.     cmp    flags.cxzflg,'E'    ; Control-E abort?
  916.     je    ackpa3a            ; e = yes, quit now
  917.     push    ax            ; send failure, retry
  918.     mov    ax,100            ; 0.1 sec
  919.     call    pcwait            ; small wait between retries
  920.     inc    rpacket.numtry
  921.     mov    al,rpacket.numtry
  922.     cmp    al,maxtry        ; exceeded retry limit?
  923.     pop    ax
  924.     jbe    ackpa3            ; be = ok to try again
  925.     mov    sstate,'A'        ; set states to abort
  926.     mov    rstate,'A'
  927.     mov    rpacket.numtry,0
  928.     mov    dx,offset erms13    ; unable to send reply
  929.     jmp    giveup
  930. ackpa3a:stc                ; set carry for failure
  931.     ret
  932.  
  933. ackpa4:    mov    al,rpacket.seqnum    ; success
  934.     mov    rpacket.datlen,0    ; clear old contents
  935.     call    pakptr            ; acking an active buffer?
  936.     jc    ackpa5            ; c = no such seqnum, stray ack
  937.     push    si
  938.     mov    si,bx            ; packet pointer from pakptr
  939.     call    bufrel            ; release ack'ed packet
  940.     pop    si
  941.     mov    rpacket.numtry,0
  942.     cmp    al,windlow        ; acking window low?
  943.     jne    ackpa5            ; ne = no
  944.     mov    al,windlow        ; yes, rotate the window
  945.     inc    al
  946.     and    al,3fh
  947.     mov    windlow,al
  948. ackpa5:    clc
  949.     ret
  950. ackpak    endp
  951.  
  952. ; Send a NAK. Uses rpacket structure.
  953. NAKPAK    proc    near
  954.     mov    rpacket.numtry,0
  955. nakpa2:    push    si
  956.     mov    si,offset rpacket
  957.     mov    [si].datlen,0        ; no data
  958.     inc    fsta.nakscnt        ; count NAKs sent
  959.         mov    [si].pktype,'N'        ; NAK that packet
  960.     call    spack
  961.     pop    si
  962.     jc    nakpa3            ; c = failure
  963.     mov    rpacket.numtry,0
  964.     clc
  965.     ret                ; return success
  966.  
  967. nakpa3:    cmp    flags.cxzflg,'C'    ; Control-C abort?
  968.     je    nakpa3a            ; e = yes, quit now
  969.     cmp    flags.cxzflg,'E'    ; Control-E abort?
  970.     je    nakpa3a            ; e = yes, quit now
  971.     push    ax            ; send failure, retry
  972.     mov    ax,100            ; wait 0.1 second
  973.     call    pcwait
  974.     inc    rpacket.numtry        ; count attempts to respond
  975.     mov    al,rpacket.numtry
  976.     cmp    al,maxtry        ; tried enough times?
  977.     pop    ax
  978.     jbe    nakpa2            ; be = ok to try again
  979.     mov    sstate,'A'        ; set states to abort
  980.     mov    rstate,'A'
  981.     mov    rpacket.numtry,0
  982.     mov    dx,offset erms13    ; unable to send reply
  983.     jmp    giveup
  984. nakpa3a:stc
  985.     ret                ; return failure
  986. NAKPAK    ENDP
  987.  
  988. ; Close, but do not delete, output file. Update file attributes,
  989. ; add Control-Z or Control-L, if needed.
  990. fileclose proc    near
  991.     cmp    filopn,0        ; is a file open?
  992.     jne    filec0            ; ne = yes
  993.     ret
  994. filec0:    cmp    flags.xflg,0        ; receiving to screen?
  995.     jne    filec2            ; ne = yes
  996.     cmp    flags.destflg,1        ; destination is disk?
  997.     jne    filec1            ; ne = no
  998.     cmp    flags.eofcz,0        ; should we write a ^Z?
  999.     je    filec1            ; e = no, keep going
  1000.     cmp    trans.xtype,0        ; test mode tranfer?
  1001.     jne    filec2            ; ne = no, binary, no ^Z
  1002.     push    si
  1003.     mov    rpacket.datlen,1    ; one byte to decode and write
  1004.     mov    si,rpacket.datadr    ; source buffer address
  1005.     mov    byte ptr[si],'Z'-40h    ; put Control-Z in buffer
  1006.     mov    si,offset rpacket    ; address for decoder
  1007.     call    ptchr            ; decode and write to output
  1008.     pop    si
  1009. filec1:    cmp    flags.destflg,0        ; file destination is printer?
  1010.     jne    filec2            ; ne = no, skip next part
  1011.     push    si
  1012.     mov    rpacket.datlen,1    ; one byte to decode and write
  1013.     mov    si,rpacket.datadr    ; source buffer address
  1014.     mov    byte ptr [si],'L'-40h    ; put Control-L (FF) in buffer
  1015.     mov    si,offset rpacket    ; address for decoder
  1016.     call    ptchr            ; decode and write to output
  1017.     pop    si
  1018. filec2:    mov    ah,write2        ; write to file
  1019.     xor    cx,cx            ; write 0 bytes to truncate length
  1020.     mov    bx,diskio.handle    ; file handle
  1021.     cmp    bx,0            ; valid handle?
  1022.     jl    filec5            ; l = no
  1023.     int    dos
  1024.     xor    al,al            ; get device info
  1025.     mov    ah,ioctl
  1026.     int    dos
  1027.     test    dl,80h            ; bit set if handle is for a device
  1028.     jnz    filec4            ; nz = non-disk, no file attributes
  1029.                     ; do file attributes and close
  1030.     mov    cx,word ptr ftime    ; new time
  1031.     mov    dx,word ptr fdate    ; new date
  1032.     mov    word ptr fdate,0
  1033.     mov    word ptr ftime,0    ; clear current time/date attributes
  1034.     mov    ax,cx
  1035.     or    ax,dx
  1036.     jz    filec4            ; z = no attributes to set
  1037.     or    cx,cx            ; time set as null?
  1038.     jnz    filec3            ; nz = no
  1039.     inc    cl            ; two seconds past midnight
  1040. filec3:    mov    ah,setattr        ; set file date/time attributes
  1041.     mov    al,1            ; set, not get
  1042.     mov    bx,diskio.handle    ; file handle
  1043.     int    dos            ; end of file attributes
  1044. filec4:    mov    bx,diskio.handle    ; file handle
  1045.     push    dx            ; save dx
  1046.     mov    ah,close2        ; close file
  1047.     int    dos
  1048.     pop    dx
  1049.     mov    filopn,0        ; say file is closed
  1050. filec5:    ret
  1051. fileclose endp
  1052.  
  1053. ; Delete file whose asciiz name is in diskio.string
  1054. filedel    proc    near
  1055.     mov    dx,offset diskio.string    ; file name, asciiz
  1056.     cmp    diskio.string,0        ; filename present?
  1057.     je    filede2            ; e = no
  1058.     cmp    flags.abfflg,0        ; keep incomplete file?
  1059.     je    filede2            ; e = yes
  1060.     test    flags.remflg,dquiet    ; quiet display?
  1061.     jnz    filede1            ; nz = yes
  1062.     cmp    flags.xflg,0        ; receiving to screen?
  1063.     jne    filede1            ; ne = yes, no message
  1064.     push    dx
  1065.     call    cxmsg            ; clear Last message line
  1066.     mov    dx,offset infms7    ; saying Discarding file
  1067.     mov    ah,prstr
  1068.     int    dos
  1069.     pop    dx
  1070.     call    prtasz            ; show filename
  1071. filede1:mov    ah,del2            ; delete the file
  1072.     int    dos
  1073. filede2:ret
  1074. filedel    endp
  1075.  
  1076. ; Error exit. Enter with dx pointing to asciiz error message.
  1077. ; Sends 'E' Error packet and shows message on screen. Changes state to 'A'.
  1078. ; Always returns with carry set.
  1079. giveup    proc    near
  1080.     cmp    flags.destflg,2        ; receiving to the screen?
  1081.     je    giveu1            ; e = yes, no formatted display
  1082.     call    ermsg            ; show msg on error line
  1083. giveu1:    mov    bx,dx            ; set bx to error message
  1084.     call    errpack            ; send error packet just in case
  1085.     mov    rstate,'A'        ; change the state to abort
  1086.     stc                ; set carry
  1087.     ret
  1088. giveup    endp
  1089.  
  1090. ; ERROR sets abort state, positions the cursor and displays the Error message.
  1091.  
  1092. ERROR    PROC    NEAR
  1093.     mov    rstate,'A'        ; set state to abort
  1094.     call    dodec            ; decode to decbuf
  1095.     mov    dx,offset decbuf    ; where msg got decoded, asciiz
  1096.     call    ermsg            ; show string
  1097.     stc                ; set carry for failure state
  1098.     ret
  1099. ERROR    ENDP
  1100.  
  1101. ; Called by GETATT in receiver code to verify sufficient disk space.
  1102. ; Gets file path from diskio.string setup in mssfil, remote size in diskio
  1103. ; from getatt, and whether a disk file or not via ioctl on the file handle.
  1104. ; Returns carry clear if enough space.
  1105. spchk    proc    near            ; check for enough disk space
  1106.     push    ax
  1107.     push    bx
  1108.     push    cx
  1109.     push    dx
  1110.     mov    ah,ioctl        ; ask DOS about this file handle
  1111.     mov    al,0            ; get info
  1112.     mov    bx,diskio.handle
  1113.     int    dos
  1114.      test    dl,80h            ; handle is a disk file?
  1115.     jnz    spchk5b            ; nz = no, always enough space
  1116.     and    dl,01fh            ; get current drive from bits 5-0
  1117.     add    dl,'A'            ; convert to a letter
  1118.     mov    cl,dl            ; cl holds drive letter
  1119.     call    dskspace        ; calculate space into dx:ax
  1120.     jc    spchk6            ; c = error
  1121.     push    ax            ; save low word of bytes
  1122.     push    dx            ; save high word, dx:ax
  1123.     mov    dx,diskio.sizehi    ; high word of file size dx:ax
  1124.     mov    ax,diskio.sizelo    ; low word
  1125.     mov    cx,dx            ; copy size long word to cx:bx
  1126.     mov    bx,ax
  1127.     shr    bx,1            ; divide long word by two
  1128.     shr    cx,1
  1129.     jnc    spchk2            ; nc = no carry down
  1130.     or    bx,8000h        ; get carry down
  1131. spchk2:    shr    bx,1            ; divide by two again
  1132.     shr    cx,1
  1133.     jnc    spchk3
  1134.     or    bx,8000h        ; get carry down
  1135. spchk3:    shr    bx,1            ; divide long word by two
  1136.     shr    cx,1
  1137.     jnc    spchk4            ; nc = no carry down
  1138.     or    bx,8000h        ; get carry down
  1139. spchk4:    shr    bx,1            ; divide long word by two
  1140.     shr    cx,1
  1141.     jnc    spchk5            ; nc = no carry down
  1142.     or    bx,8000h        ; get carry down
  1143. spchk5:    add    ax,bx            ; form dx:ax = (17/16) * dx:ax
  1144.     adc    dx,cx
  1145.     pop    cx            ; high word of disk space
  1146.     pop    bx            ; low word
  1147.     sub    bx,ax            ; minus inflated file size, low word
  1148.     sbb    cx,dx            ;  and high word
  1149.     js    spchk6            ; s = not enough space for file
  1150. spchk5b:clc
  1151.     jmp    short spchk7        ; enough space
  1152. spchk6:    stc
  1153. spchk7:    pop    dx
  1154.     pop    cx
  1155.     pop    bx
  1156.     pop    ax
  1157.     ret
  1158. spchk    endp
  1159.  
  1160. code    ends 
  1161.     end
  1162.