home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msvp98b1.lzh / MSSRCV.ASM < prev    next >
Assembly Source File  |  1993-05-14  |  40KB  |  1,223 lines

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