home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpm / turbodos / xm-td.lbr / XMODEM.AQM / XMODEM.AÓM
Text File  |  1985-02-09  |  59KB  |  2,524 lines

  1. ; XMODEM-SENECA v2.10
  2. ;
  3.     extrn    freesp,padc,ma3dc
  4.     public    delay,optsav,print
  5.     cseg
  6. ;
  7. VERSION:EQU    2
  8. MODLEV:    EQU    1
  9. ;
  10. NO:    EQU    0
  11. YES:    EQU    0FFH
  12. ;
  13. ; Define ASCII characters used
  14. ;
  15. ACK:    EQU    06H        ;acknowledge
  16. CAN:    EQU    18H        ;control-x for cancel
  17. CR:    EQU    0DH        ;carriage return
  18. CRC:    EQU    'C'             ;crc request character
  19. EOF:    EQU    1AH        ;^Z for end of file
  20. EOT:    EQU    04H        ;end of transmission
  21. LF:    EQU    0AH        ;linefeed
  22. NAK:    EQU    15H        ;neg acknowledge
  23. SOH:    EQU    01H        ;start of header
  24. ;
  25. ; Incidental equates
  26. ;
  27. ;ACL:    EQU    3EH    ;access level storage
  28. ;xmup    equ    35h
  29. ;xmdn    equ    36h
  30. ;maxdn    equ    5    ;max downloads allowed (0=unlimited)
  31. ;MAXS0:    EQU    80    ;max # of sectors for ACL=0
  32. MHZ:    EQU    4    ;clock speed, use integer (2,4,5,8, etc.)
  33. NOCOMR:    EQU    YES    ;yes, change .COM to .OBJ on receive
  34. NOCOMS:    EQU    YES    ;yes, .COM files not sent
  35. NOLBS:    EQU    YES    ;yes, .??# files not sent
  36. NOSYS:    EQU    YES    ;yes, no $SYS files sent or reported
  37. ;
  38. ; Some modems will either go onhook immediately after carrier loss
  39. ; or can be set to lower values. A good value with the Smartmodem
  40. ; is 5 seconds, since it catches all "call forwarding" breaks.
  41. ; Not all is lost after timeout in XMODEM; BYE will still wait
  42. ; some more, but the chance of someone slipping in is less now.
  43. ;
  44. TIMOUT:    EQU    1    ;seconds to abort after carrier loss
  45. ;
  46. ;=======================================================================
  47. ;
  48. ; Type of CP/M - standard starting at 0100H or alterate starting address
  49. ;
  50. STDCPM:    EQU    YES    ;yes, if standard CP/M, no if not
  51. ;
  52. ;=======================================================================
  53. ;
  54. ; Allows drive/user area to be specified for downloading.  If using ZCPR
  55. ; set USEMAX 'YES'.  Then the answers to MAXDRV and MAXUSR are superflu-
  56. ; ous.
  57. ;
  58. USEMAX:    EQU    NO    ;yes if using ZCPR to set DRIVMAX & USRMAX values
  59.             ;no to use MAXDRV and MAXUSR specified below
  60. DRIVMAX:EQU    03DH    ;location of MAXDRIV byte
  61. USRMAX:    EQU    03FH    ;location of MAXUSER byte
  62. ;
  63. ; If USEMAX above is YES for automatic ZCPR setting, the following two
  64. ; are not used. 
  65. ;
  66. MAXDRV:    EQU    2    ;number of disk drives used
  67. MAXUSR:    EQU    30    ;maximum 'SEND' user allowed    
  68. ;
  69. ;=======================================================================
  70. ;
  71. ; Length of external patch program.  If over 128 bytes, get/set size
  72. ;
  73. LARGEIO:EQU    NO    ;yes, if modem patch area over 128 bytes
  74. LARSIZE:EQU    0    ;if 'LARGEIO' set patch area size here
  75. ;
  76. ;=======================================================================
  77. ;
  78. ; Type of modem being used - an external patch file needed in any event.
  79. ;
  80. ALTOS:    EQU    NO    ;yes, if altos
  81. EXTMOD:    EQU    YES    ;yes, if external modem
  82. INTER3:    EQU    NO    ;yes, if compupro interfacer3/4 card
  83. ;
  84. ;=======================================================================
  85. ;
  86. ; Allows uploading to be done on a specified driver and user area so all
  87. ; viewers (indluding the SYSOP) can readily find the latest entries.
  88. ;
  89. SETAREA:EQU    YES    ;yes, if using designated area to receive files
  90. DRV:    EQU    'A'     ;drive to receive file on
  91. USR:    EQU    5    ;user area to receive file in
  92. ;
  93. ;=======================================================================
  94. ;
  95. ; Selects the drive/user area for uploading private files for the SYSOP.
  96. ; This permits experimental files, replacement files and proprietary
  97. ; programs to be sent to the sysop.
  98. ;
  99. PRDRV:    EQU    'A'     ;private drive for SYSOP to receive file
  100. PRUSR:    EQU    1    ;private user area for SYSOP to receive file
  101. ;
  102. ;=======================================================================
  103. ;
  104. ; Selects the drive/user area for downloading private files for SYSOP
  105. ; use.    This permits him to put a special file in this area, then leave
  106. ; a private note to that person mentioning the name of the file and its
  107. ; location.  Although anybody could download that program, they don't
  108. ; know what (if any) files are there.  A high degree of security exists,
  109. ; while the sysop still has the ability to make special files available.
  110. ; Thus any person can be a temporary "privileged user".
  111. ;
  112. SPLDRV: EQU     'A'     ;special drive area for downloading SYSOP files
  113. SPLUSR:    EQU    30    ;special user area for downloading SYSOP files    
  114. ;
  115. ;=======================================================================
  116. ;
  117. ; File transfer logging options
  118. ;
  119. LGCL:    EQU    YES    ;yes, logs XMODEM transfers
  120. LOGUSR:    EQU    0    ;user area to put 'LOG.SYS' file
  121. LOGDRV:    EQU    'A'     ;drive to place 'LOG.SYS' file
  122. LASTUSR:EQU    0    ;user area of 'LASTCALR' file, if 'LGCL' yes
  123. ;
  124. ;=======================================================================
  125. ;
  126. ; The receiving station sends an 'ACK' for each valid sector received.
  127. ; It sends a 'NAK' for each sector incorrectly received.  In poor con-
  128. ; ditions either may be garbled.  Waiting for a valid 'NAK' can slow
  129. ; things down somewhat, giving more time for the interference to quit.
  130. ;
  131. ACKNAK:    EQU    NO    ;yes resends a record after any non-ACK
  132.             ;no requires a valid NAK to resend a record
  133. ;
  134.     IF    STDCPM
  135. BASE:    EQU    0        ;cp/m base address
  136.     ENDIF
  137. ;
  138.     IF    NOT STDCPM
  139. BASE:    EQU    4200H        ;alternate cp/m base address
  140.     ENDIF
  141. ;
  142. ;-----------------------------------------------------------------------
  143. ;
  144. ;            PROGRAM STARTS HERE
  145. ;
  146. ;-----------------------------------------------------------------------
  147. ;
  148. ;
  149.     JMP    BEGIN
  150. ;
  151. ;-----------------------------------------------------------------------
  152. ;
  153. ; This is the I/O patch area.  Assemble the appropriate I/O patch file
  154. ; for your modem, then integrate it into this program via DDT (or SID).
  155. ;
  156. ; Initially, all jumps are to zero, which will cause an unpatched
  157. ; XMODEM to simply execute a warm boot.  All routines must end with RET.
  158. ;
  159. CONOUT:    JMP    0        ;see 'CONOUT' discussion above
  160. MINIT:    JMP    0        ;initialization routine (if needed)
  161. UNINIT:    JMP    0        ;undo whatever minit did (or return)
  162. SENDR:    JMP    0        ;send character (via pop psw)
  163. CAROK:    JMP    0        ;test for carrier
  164. MDIN:    JMP    0        ;receive data byte
  165. GETCHR:    JMP    0        ;get char. from modem
  166. RCVRDY:    JMP    0        ;check receive ready
  167. SNDRDY:    JMP    0        ;check send ready (a=errcde)
  168. SPEED:    JMP    0        ;get speed value for transfer time
  169. EXTRA1:    JMP    0        ;extra for custom routine
  170. EXTRA2:    JMP    0        ;extra for custom routine
  171. EXTRA3:    JMP    0        ;extra for custom routine
  172. ;
  173. ;-----------------------------------------------------------------------
  174. ;
  175.      IF    NOT LARGEIO    ;i/o patch area size up to 128 bytes
  176.     DS    100H
  177.      ENDIF
  178.  
  179.      IF    LARGEIO     ;i/o patch area size if over 128 bytes
  180.     ORG    BASE+100H+IOSIZE
  181.      ENDIF
  182. ;
  183. ; Save CP/M stack, initialize new one for this program
  184. ;
  185. BEGIN:    LXI    H,0
  186.     DAD    SP
  187.     SHLD    STACK
  188.     LXI    SP,STACK    ;initialize new stack
  189.     lxi    d,2000h
  190.     mvi    c,26
  191.     call    5
  192.     mvi    b,0
  193.     mvi    c,41
  194.     mvi    e,81h
  195.     call    50h
  196.     lda    2000h
  197.     sta    aclvl
  198.     mvi    c,26
  199.     lxi    d,80h
  200.     call    5
  201.     mvi    c,12
  202.     call    bdos
  203.     mov    a,e
  204.     lxi    d,lstcft
  205.     call    ma3dc
  206. ;
  207. ; Save the current drive and user area
  208. ;
  209.     MVI    E,0FFH        ;get the current user area
  210.     MVI    C,USER
  211.     CALL    BDOS
  212.     STA    OLDUSR        ;save user number here
  213.     MVI    C,CURDRV    ;get the current drive
  214.     CALL    BDOS
  215.     STA    OLDDRV        ;save drive here
  216.     CALL    print        ;print:
  217.     DB    CR,LF,'XMODEM-TD-SENECA v',VERSION+'0','.',MODLEV+'0',' ',0
  218. ;
  219. ; Get option
  220. ;
  221.     LXI    H,FCB+2     ;first off, check for "p" (private)
  222.     MOV    A,M
  223.     CPI    'P'             ;if not, then normal stuff...
  224.     JNZ    CKOPT
  225.     DCX    H        ;first character in buffer
  226.     MOV    A,M
  227.     CPI    'R'
  228.     JNZ    OPTNERR     ;if not, is an error
  229.     STA    PRVTFL        ;otherwise set 'PRIVATE' flag
  230.     INX    H
  231.     INX    H
  232.     MOV    A,M
  233.     CPI    'C'             ;checksum checking requested?
  234.     JZ    CKOPT1     ;if yes, go set flag
  235.     JMP    CKOPT2
  236. ;
  237. CKOPT:    CPI    'C'             ;checksum checking requested?
  238.     JNZ    CKOPT2     ;no, go check primary
  239.     LDA    FCB+1        ;get primary option
  240.     CPI    'R'             ;checksum only for receive
  241.     JNZ    OPTNERR     ;print error message then abort
  242. ;
  243. CKOPT1:STA    CRCFLG        ;turn on the checksum flag
  244. ;
  245. CKOPT2:LDA    FCB+1        ;get option (l, r or s)
  246.     STA    OPTSAV        ;save option for later use
  247.     PUSH    PSW
  248.     CPI    'R'
  249.     jz    cko21
  250.     JMP    CKOPT4
  251. ;
  252. cko21:    LDA    CRCFLG
  253.     ORA    A
  254.     JZ    CKOPT3
  255.     CALL    print
  256.     DB    'Checksum enabled',0
  257.     JMP    CKOPT4
  258. ;
  259. CKOPT3:CALL    print
  260.     DB    '(CRC is enabled)',0
  261. ;
  262. CKOPT4:CALL    print
  263.     DB    CR,LF,0
  264. ;
  265.     CALL    MINIT
  266. ;
  267. ; Jump to appropriate function
  268. ;
  269.     POP    PSW        ;get option
  270. ;
  271.      IF    LGCL
  272.     PUSH    PSW        ;but save it
  273.      ENDIF            ;LGCL
  274. ;
  275.     CPI    'L'             ;to send a file from a library?
  276.     JNZ    NOL
  277.     JMP    SENDFIL
  278. ;
  279. NOL:    CPI    'R'             ;to receive a file?
  280.     JZ    RCVFIL
  281.     CPI    'S'
  282.     JZ    SENDFIL     ;otherwise go send a file
  283. ;
  284. ; Invalid option
  285. ;
  286. OPTNERR:CALL    print
  287.     DB    CR,LF,'++ Examples of valid options: ++',0
  288. ;
  289.      IF    NOT SETAREA
  290.     CALL    print
  291.     DB    CR,LF,0
  292.      ENDIF            ;NOT SETAREA
  293. ;
  294.      IF    SETAREA
  295.     CALL    print
  296.     DB    '   (Uploads files to ',DRV,0
  297.     LXI    H,USR
  298.     CALL    DECOUT
  299.     CALL    print
  300.     DB    ':)',CR,LF,0
  301.      ENDIF            ;SETAREA
  302. ;
  303.     CALL    ERXIT        ;exit with error
  304.     DB    '   XMODEM L PRINT.LBR PRINT.INF    to send a file '
  305.     DB    'from a library',CR,LF
  306.     DB    '   XMODEM L CATALOG CAT2.OBJ       (.LBR extent may '
  307.     DB    'be omitted)',CR,LF
  308.     DB    '   XMODEM S FILENAME.TYP           to send a file'
  309.     DB    CR,LF
  310.     DB    '   XMODEM R (or RC) FILENAME.TYP   to receive a file'
  311.     DB    CR,LF
  312.     DB    '   XMODEM RP (or RPC) FILENAME.TYP to receive in a '
  313.     DB    'private area',CR,LF,CR,LF
  314.     DB    '   (The "C" in RC or RPC receives via checksum rather '
  315.     DB    'than CRC)',cr,lf,lf
  316.     db    'NOTE: L option for MEMBERS only$'
  317. ;    
  318. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  319. ;
  320. ; ---> SENDFIL    sends a CP/M file
  321. ;
  322. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  323. ;
  324. ; The CP/M file specified in the XMODEM command is transferred over the
  325. ; phone to another computer running modem with the "R" (receive) option.
  326. ; The data is sent one record at a time with headers and checksums, and
  327. ; retransmission on errors.
  328. ;
  329. SENDFIL:CALL    LOGDU        ;check file name or drive/user option
  330.     LDA    OPTSAV
  331.     CPI    'L'             ;if library option skip 'CNREC'
  332.     CNZ    CNREC        ;ignore if in library mode
  333.     CALL    OPENFIL     ;open the file
  334.     MVI    E,100        ;wait 100 sec for initial nak
  335.     CALL    WAITNAK
  336. ;
  337. SENDLP:    CALL    RDRECD        ;read a record
  338.     JC    SENDEOF     ;send 'EOF' if done
  339.     CALL    INCRRNO     ;bump record number
  340.     XRA    A        ;initialize error count to zero
  341.     STA    ERRCT
  342. ;
  343. SENDRPT:CALL    SENDHDR     ;send a header
  344.     CALL    SENDREC     ;send data record
  345.     LDA    CRCFLG        ;get 'CRC' flag
  346.     ORA    A        ;'CRC' in effect?
  347.     CZ    SENDCRC     ;yes, send 'CRC'
  348.     CNZ    SENDCKS     ;no, send checksum
  349.     CALL    GETACK        ;get the 'ACK'
  350.     JC    SENDRPT     ;repeat if no 'ACK'
  351.     LDA    OPTSAV        ;get the command option again
  352.     CPI    'L'
  353.     JNZ    SNRPT1        ;if not library option, exit
  354.     LHLD    RCNT
  355.     MOV    A,H
  356.     ORA    L        ;see if l and h both zero now
  357.     JZ    SENDEOF     ;if finished, exit
  358.     DCX    H        ;if not both zero, more remaining
  359.     SHLD    RCNT        ;one less to go
  360. ;
  361. SNRPT1:    JMP    SENDLP        ;loop until eof
  362. ;
  363. ; File sent, send EOT's
  364. ;
  365. SENDEOF:MVI     A,EOT           ;send an 'EOT'
  366.     CALL    SEND
  367.     CALL    GETACK        ;get the ack
  368.     JC    SENDEOF     ;loop if no ack
  369.     JMP    EXITLG        ;all done
  370. ;
  371. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  372. ;
  373. ; ---> RCVFIL    Receive a CP/M file
  374. ;
  375. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  376. ;
  377. ; Receives a file in block format as sent by another person doing
  378. ; "XMODEM S FM.FT".  Can be invoked by "XMODEM R FN.FT" or by
  379. ; "XMODEM RC FN.FT" if Checksum is to be used.
  380. ;
  381. RCVFIL:    CALL    LOGDU        ;check file name or drive/user option
  382. ;
  383.      IF    SETAREA
  384.     MVI    A,DRV-40H
  385.     STA    FCB
  386.      ENDIF            ;SETAREA
  387. ;
  388.     LDA    PRVTFL        ;receiving to a private area?
  389.     ORA    A
  390.     JZ    RCVFL1     ;if not, exit
  391.     MVI    A,PRDRV-40H    ;private area takes precedence
  392.     STA    FCB        ;store drive to be used
  393. ;
  394. RCVFL1: IF    NOCOMR
  395.     LXI    H,FCB+9     ;point to filetype
  396.     MVI    A,'C'           ;1st letter
  397.     CMP    M        ;is it c ?
  398.     JNZ    CONTNU     ;if not, continue normally
  399.     INX    H        ;get 2nd letter
  400.     MVI    A,'O'           ;2nd letter
  401.     CMP    M        ;is it o ?
  402.     JNZ    CONTNU     ;if not, continue normally
  403.     INX    H        ;get 3rd letter
  404.     MVI    A,'M'           ;3rd letter
  405.     CMP    M        ;is it m ?
  406.     JNZ    CONTNU     ;if not, continue normally
  407.     CALL    print        ;print renaming message
  408.     DB    'Auto-renaming file to ".OBJ"',CR,LF,0
  409.     LXI    H,FCB+9
  410.     MVI    M,'O'
  411.     INX    H
  412.     MVI    M,'B'
  413.     INX    H
  414.     MVI    M,'J'
  415. norest:
  416.      ENDIF            ;NOCMR
  417. ;
  418. CONTNU:CALL    print        ;print the message
  419.     DB    'File will be received on ',0
  420.     LDA    PRVTFL        ;going to store in the private area?
  421.     ORA    A
  422.     LDA    XPRDRV        ;get private drive
  423.     JNZ    CONTN1     ;if yes, it takes priority
  424.     LDA    OLDDRV        ;otherwise get current drive
  425.     ADI    'A'             ;convert to ascii
  426. ;
  427.      IF    SETAREA
  428.     LDA    XDRV        ;setarea uses a specified drive
  429.      ENDIF            ;SETAREA
  430. ;
  431.      IF    NOT SETAREA
  432. NOTDRV:    DB    0,0        ;filled in by 'GETDU' if requested
  433.      ENDIF            ;NOT SETAREA
  434. ;
  435. CONTN1:CALL    CTYPE        ;print the drive to store on
  436.     LDA    PRVTFL        ;going to store in the private area?
  437.     ORA    A
  438.     LDA    XPRUSR        ;get private user area
  439.     JNZ    CONTN2     ;if yes, it takes priority
  440.     LDA    OLDUSR        ;get current drive
  441. ;
  442.      IF    SETAREA
  443.     LDA    XUSR        ;setarea takes next precedence
  444.      ENDIF            ;SETAREA
  445. ;
  446.      IF    NOT SETAREA
  447. NOTUSR:    DB    0,0        ;filled in by 'GETDU' if requested
  448.      ENDIF            ;NOT SETAREA
  449. ;
  450. CONTN2:MVI    H,0
  451.     MOV    L,A
  452.     CALL    DECOUT        ;print the user area
  453.     CALL    print
  454.     DB    ':',CR,LF,0
  455.     CALL    CHEKFIL     ;see if file exists
  456.     CALL    MAKEFIL     ;if not, start a new file
  457.     CALL    print
  458.     DB    'File open - ready to receive',CR,LF,0
  459.     call    freesp
  460. ;
  461. RCVLP:    CALL    RCVRECD     ;get a record
  462.     JC    RCVEOT        ;got 'EOT'
  463.     CALL    WRRECD        ;write the record
  464.     CALL    INCRRNO     ;bump record number
  465.     CALL    SENDACK     ;ack the record
  466.     JMP    RCVLP        ;loop until 'EOF'
  467. ;
  468. ; Got EOT on record so flush buffers then done
  469. ;
  470. RCVEOT:    CALL    WRBLOCK     ;write the last block
  471.     CALL    SENDACK     ;ack the record
  472.     CALL    CLOSFIL     ;close the file
  473.     JMP    EXITLG        ;all done
  474. ;
  475. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  476. ;
  477. ;            SUBROUTINES
  478. ;
  479. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  480. ;
  481. ; ---> LOGDU  Log into drive and user (if specified).  If none mentioned
  482. ;          it falls through to 'TRAP' routine for normal use.
  483. ;
  484. LOGDU:    LXI    H,DEFDMA    ;point to default buffer command line
  485.     MOV    B,M        ;store number of chars. in command
  486.     INR    B        ;add in current location
  487. LOG1:    CALL    CHKSP        ;skip spaces to find 1st command
  488.     JZ    LOG1
  489. LOG2:    CALL    CHKSP        ;skip 1st command (non-spaces)
  490.     JNZ    LOG2
  491.     INX    H
  492.     CALL    CHKFSP        ;skip spaces to find 2nd command
  493.     SHLD    SAVEHL        ;save start address of the 2nd command
  494. ;
  495. ; Now point to the first byte in the argument, i.e., if it was of format
  496. ; similar to:  B6:HELLO.DOC then we point at the drive character 'B'.
  497. ;
  498.     MVI    C,4        ;drive/user is 4 chars. maximum
  499. CPLP:    MOV    A,M
  500.     CPI    ' '+1           ;space or return, finished
  501.     JC    TRAP
  502.     INX    H
  503.     CPI    ':'
  504.     JZ    GETDU        ;if colon, get drive/user and log in
  505.     DCR    B        ;one less position to check
  506.     DCR    C        ;one less to go
  507.     JNZ    CPLP
  508. ;
  509. ; ---> TRAP  Check for no file name or ambiguous name
  510. ;
  511. TRAP:    CALL    MOVEFCB     ;move the filename into the file block
  512.     LXI    H,FCB+1     ;point to file name
  513.     MOV    A,M        ;get first char of file name
  514.     CPI    ' '             ;any there?
  515.     JNZ    ATRAP        ;yes, check for ambigous file name
  516. NFN:    CALL    ERXIT        ;print msg, exit
  517.     DB    '++ No file name requested ++$'
  518. ;
  519. ATRAP:    MVI    B,11        ;11 chars to check
  520. TRLOOP:    MOV    A,M        ;get char from fcb
  521.     CPI    '?'             ;ambiguous?
  522.     JZ    TRERR        ;yes, exit with error msg
  523.     CPI    '*'        ;even more ambiguous??
  524.     JZ    TRERR        ;yes, exit with error msg
  525.     INX    H        ;point to next char
  526.     DCR    B        ;one less to go
  527.     JNZ    TRLOOP        ;not done, check some more
  528.     RET
  529. ;
  530. TRERR:    CALL    ERXIT        ;print msg, exit
  531.     DB    '++ Wild-card options are not valid ++$'
  532. ;
  533. ; ---> GETDU  Get <D>isk and <U>ser from DUSAVE and log in if valid.
  534. ;
  535. GETDU:    CALL    CHKFSP        ;see if a file name is included
  536.     SHLD    SAVEHL        ;save location of the filename
  537.     LDA    PRVTFL        ;uploading to a private area?
  538.     ORA    A
  539.     JNZ    TRAP        ;if yes, going to a specified area
  540.     LXI    H,DUSAVE    ;point to drive/user
  541.     MVI    A,YES        ;reset to provide for current drive
  542.     STA    DUD
  543.     MOV    A,M        ;get 1st char.
  544.     CPI    'A'-1
  545.     JC    NUMERIC     ;satisfied with current drive
  546.     SUI    'A'
  547. ;
  548.      IF    NOT USEMAX
  549.     CPI    MAXDRV
  550.     JNC    ILLDU        ;drive selection not available
  551.      ENDIF            ;NOT USEMAX
  552. ;
  553.      IF    USEMAX
  554.     PUSH    H
  555.     LXI    H,DRIVMAX    ;point to max drive byte
  556.     INR    M
  557.     CMP    M        ;and check it
  558.     PUSH    PSW        ;save flags from the CMP
  559.     DCR    M        ;restore max drive to normal
  560.     POP    PSW        ;restore flags from the CPM
  561.     JNC    ILLDU
  562.     POP    H
  563.      ENDIF            ;USEMAX
  564. ;
  565.     STA    DUD        ;save drive
  566.     INX    H        ;get 2nd character
  567. ;
  568. NUMERIC:MOV    A,M
  569.     CPI    ':'
  570.     JZ    OK4        ;colon for drive only, no user number
  571.     CALL    CKNUM        ;check if numeric
  572.     SUI    '0'             ;convert ascii to binary
  573.     STA    DUU        ;save it
  574.     INX    H        ;get 3rd character if any
  575.     MOV    A,M
  576.     CPI    ':'
  577.     JZ    OK1
  578.     LDA    DUU
  579.     CPI    1        ;is first number a '1'?
  580.     JNZ    ILLDU
  581.     MOV    A,M
  582.     CALL    CKNUM
  583.     SUI    '0'-10
  584.     STA    DUU
  585.     INX    H        ;get 4th (and last character) if any
  586.     MOV    A,M
  587.     CPI    ':'
  588.     JNZ    ILLDU
  589. OK1:    LDA    OPTSAV        ;get the option back
  590.     CPI    'R'             ;receiving a file?
  591.     LDA    DUU        ;get desired user area
  592.     JZ    OK2        ;yes, can not use special download area
  593.     LDA    DUD        ;get desired drive
  594.     CPI    SPLDRV-'A'      ;special download drive requested?
  595.     LDA    DUU        ;get user area requested
  596.     JNZ    OK2        ;if none, exit
  597.     CPI    SPLUSR        ;special download area requested?
  598.     JZ    OK3        ;if yes, process request
  599. ;
  600. OK2:     IF    NOT USEMAX
  601.     CPI    MAXUSR+1    ;check for maximum user download area
  602.     JNC    ILLDU        ;error if more (and not special area)
  603.      ENDIF            ;NOT USEMAX
  604. ;
  605.      IF    USEMAX
  606.     PUSH    H
  607.     LXI    H,USRMAX    ;point at max user byte
  608.     CMP    M        ;and check it
  609.     JNC    ILLDU
  610.     POP    H
  611.      ENDIF            ;USEMAX
  612. ;
  613. OK3:    MOV    E,A
  614. ;
  615.      IF    NOT SETAREA
  616.     STA    NOTUSR+1    ;store requested user area
  617.     MVI    A,3EH        ;'MVI A,--' instruction
  618.     STA    NOTUSR
  619.      ENDIF            ;NOT SETAREA
  620. ;
  621.     MVI    C,USER
  622.     CALL    BDOS        ;set to requested user area
  623. OK4:    LDA    DUD        ;get drive
  624.     MOV    E,A
  625. ;
  626.      IF    NOT SETAREA
  627.     ADI    'A'
  628.     STA    NOTDRV+1    ;store requested drive
  629.     MVI    A,3EH        ;'MVI A,--' instruction
  630.     STA    NOTDRV
  631.      ENDIF            ;NOT SETAREA
  632. ;
  633.     MVI    C,SELDRV
  634.     CALL    BDOS        ;set to requested drive
  635. XIT:    JMP    TRAP        ;now find file selected
  636. ;
  637. CKNUM:    CPI    '0'
  638.     JC    ILLDU        ;error if less than ascii '0'
  639.     CPI    '9'+1
  640.     RC            ;error if more than ascii '9'
  641. ;
  642. ILLDU:    CALL    ERXIT
  643.     DB    '++ Improper drive/user combination ++$'
  644. ;
  645. ; Check next character to see if a space or non-space, file name error
  646. ; if no ASCII character.
  647. ;
  648. CHKFSP:    DCR    B
  649.     JZ    NFN        ;error if end of chars.
  650.     MOV    A,M
  651.     CPI    ' '+1
  652.     RNC            ;ok if valid character so return
  653.     INX    H
  654.     JMP    CHKFSP        ;look at next character
  655. ;
  656. ; Check next character to see if a space or non-space, go to menu if a
  657. ; command error.
  658. ;
  659. CHKSP:    DCR    B
  660.     JZ    OPTNERR
  661.     INX    H
  662.     MOV    A,M        ;get the char. there
  663.     CPI    ' '             ;space character?
  664.     RET            ;jz = space, jnz = non-space
  665. ;
  666. ; ---> RCVRECD    Receive a record
  667. ;
  668. ; Returns with carry bit set if EOT received
  669. ;
  670. RCVRECD:XRA    A        ;initialize error count to zero
  671.     STA    ERRCT
  672. RCVRPT:    XRA    A        ;get 0
  673.     STA    ERRCDE        ;clear receive error code
  674.     MVI    B,15-1        ;15-second timeout
  675.     CALL    RECV        ;get any character received
  676.     JC    RCVSTOT     ;timeout
  677.     CALL    RCVRR        ;error during receive?
  678.     CPI    SOH        ;hoping for a 'SOH'
  679.     JZ    RCVSOH        ;yes
  680.     ORA    A
  681.     JZ    RCVRPT        ;ignore nulls
  682.     CPI    CRC        ;ignore our own 'CRC' if needed
  683.     JZ    RCVRPT
  684.     CPI    NAK        ;ignore our own 'NAK' if needed
  685.     JZ    RCVRPT
  686.     CPI    EOT        ;end of transfer?
  687.     STC            ;return with carry set if 'EOT'
  688.     RZ
  689. ;
  690. ; Didn't get SOH or EOT - or - didn't get valid header - purge the line,
  691. ; then send nak
  692. ;
  693. RCVSRR:MVI    B,1        ;wait for 1 second
  694.     CALL    RECV        ;after last char. received
  695.     JNC    RCVSRR     ;loop until sender done
  696.     LDA    CRCFLG        ;get 'CRC' flag
  697.     ORA    A        ;'CRC' in effect?
  698.     MVI    A,NAK        ;put 'NAK' in accum
  699.     JNZ    RCVSR2     ;no, send the 'NAK'
  700.     LDA    FRSTIM        ;get first time switch
  701.     ORA    A        ;has first 'SOH' been received?
  702.     MVI    A,NAK
  703.     JNZ    RCVSR2     ;yes, then send 'NAK'
  704.     MVI    A,CRC        ;tell sender 'CRC' is in effect
  705. ;
  706. RCVSR2:CALL    SEND        ;   the 'NAK' or 'CRC' request
  707.     LDA    ERRCT        ;abort if
  708.     INR    A        ;   we have reached
  709.     STA    ERRCT        ;the error
  710.     CPI    10        ;   limit?
  711.     jz    rcvsabt
  712.     cpi    5
  713.     JC    RCVRPT        ;   no, try again
  714.     mvi    a,'C'
  715.     sta    crcflg
  716.     jmp    rcvrpt
  717. ;
  718. ; Error limit exceeded, so abort
  719. ;
  720. RCVSABT:CALL    CLOSFIL     ;keep whatever we got
  721.     CALL    print
  722.     DB    CR,LF,CR,LF,'++ RECEIVED FILE CANCELLED ++',0
  723.     CALL    DELFILE     ;delete received file
  724.     CALL    ERXIT        ;print second half of message
  725.     DB    '++ UNFINISHED FILE DELETED ++$'
  726. ;
  727. ; ---> DELFILE    deletes the received file (used if receive aborts)
  728. ;
  729. DELFILE:LXI    D,FCB        ;point to file
  730.     MVI    C,ERASEF    ;get function
  731.     CALL    BDOS        ;delete it
  732.     INR    A        ;delete ok?
  733.     RNZ            ;  yes, return
  734.     CALL    ERXIT        ;  no, abort
  735.     DB    '++ Can''t delete received file ++$'
  736. ;
  737. ; Timed out on receive
  738. ;
  739. RCVSTOT:JMP    RCVSRR     ;bump error count, etc.
  740. ;
  741. ; ---> RCVRR    Checks to see if framing error, overrun, or parity error
  742. ;        occured
  743. ;           1. error code (ERRCDE) was set in receive routine
  744. ;           2. errcde=0 for no errors, ERRCDE<>0 for errors
  745. ;
  746. RCVRR:     IF    ALTOS OR EXTMOD OR INTER3
  747.     RET
  748.      ENDIF
  749. ;
  750.     PUSH    PSW        ;save received character
  751.     LDA    ERRCDE        ;check for any receive error
  752.     ORA    A
  753.     JNZ    RCVRR1     ;if error, exit
  754.     POP    PSW        ;if no error, get received char. back
  755.     RET
  756. ;
  757. RCVRR1:POP    PSW        ;restore char transmitted
  758.     POP    PSW        ;restore 'CALL' on stack
  759.     JMP    RCVSRR     ;purge line, send 'NAK', continue
  760. ;
  761. ; Got SOH - get block number, block number complemented
  762. ;
  763. RCVSOH:    MVI    B,1        ;timeout = 1 sec
  764.     STA    FRSTIM        ;indicate first 'SOH' received
  765.     CALL    RECV        ;get record
  766.     JC    RCVSTOT     ;got timeout
  767.     CALL    RCVRR        ;trans error?
  768.     MOV    D,A        ;d=blk number
  769.     MVI    B,1        ;timeout = 1 sec
  770.     CALL    RECV        ;get complimented record number
  771.     JC    RCVSTOT     ;timeout
  772.     CALL    RCVRR        ;trans error?
  773.     CMA            ;calc complement
  774.     CMP    D        ;good record number?
  775.     JZ    RCVDATA     ;yes, get data
  776. ;
  777. ; Got bad record number
  778. ;
  779.     JMP    RCVSRR     ;bump error count
  780. ;
  781. RCVDATA:MOV    A,D        ;get record number
  782.     STA    RCVRNO        ;save it
  783.     MVI    C,0        ;init cksum
  784.     CALL    CLRCRC        ;clear crc counter
  785.     MVI    D,128        ;init count
  786.     LHLD    RECPTR        ;get buffer address
  787. RCVCHR:    MVI    B,1        ;1 sec timeout
  788.     CALL    RECV        ;get char
  789.     JC    RCVSTOT     ;timeout
  790.     CALL    RCVRR        ;trans error?
  791.     MOV    M,A        ;store char
  792.     INX    H        ;point to next char
  793.     DCR    D        ;done?
  794.     JNZ    RCVCHR        ;no, loop if <= 128
  795.     LDA    CRCFLG        ;get 'CRC' flag
  796.     ORA    A        ;'CRC' in effect?
  797.     JZ    RCRC        ;yes, to receive 'CRC'
  798. ;
  799. ; Verify checksum
  800. ;
  801.     MOV    D,C        ;save checksum
  802.     MVI    B,1        ;timeout len.
  803.     CALL    RECV        ;get checksum
  804.     JC    RCVSTOT     ;timeout
  805.     CALL    RCVRR        ;error during receive?
  806.     CMP    D        ;checksum ok?
  807.     JNZ    RCVSRR     ;no, error
  808. ;
  809. ; Got a record, it's a duplicate if = previous, or OK if = 1 + previous
  810. ; record
  811. ;
  812. CHKSNUM:LDA    RCVRNO        ;get received
  813.     MOV    B,A        ;save it
  814.     LDA    RECDNO        ;get previous
  815.     CMP    B        ;prev repeated?
  816.         JZ      RECVACK         ;'ACK' to catch up
  817.     INR    A        ;calculate next record number
  818.     CMP    B        ;match?
  819.     JNZ    ABORT        ;no match - stop sender, exit
  820.     RET            ;carry off - no errors
  821. ;
  822. ; ---> RCRC    Receive the Cyclic Redundancy Check characters (2 bytes)
  823. ;        and see if the CRC received matches the one calculated.
  824. ;        If they match, get next record, else send a NAK request-
  825. ;        ing the record be sent again.
  826. ;
  827. RCRC:    MVI    E,2        ;number of bytes to receive
  828. ;
  829. RCRC2:MVI    B,1        ;1 sececond timeout
  830.     CALL    RECV        ;get crc byte
  831.     JC    RCVSTOT     ;timeout
  832.     CALL    RCVRR        ;transmission error?
  833.     DCR    E        ;decrement num of bytes
  834.     JNZ    RCRC2     ;get both bytes
  835.     CALL    CHKCRC        ;check rcvd crc against calc'D CRC
  836.     ORA    A        ;is crc okay?
  837.     JZ    CHKSNUM     ;yes, go check record numbers
  838.     JMP    RCVSRR     ;go check error limit and send nak
  839. ;
  840. ; Previous record repeated, due to the last ACK being garbaged.  ACK it
  841. ; so sender will catch up
  842. ;
  843. RECVACK:CALL    SENDACK     ;send the ack,
  844.     JMP    RCVRECD     ;get next block
  845. ;
  846. ; Send an ACK for the record
  847. ;
  848. SENDACK:MVI     A,ACK           ;get 'ACK'
  849.     CALL    SEND        ;  and send it
  850.     RET
  851. ;
  852. ; ---> SENDHDR    Send the record header
  853. ;
  854. ; Send    (SOH) (block number) (complemented block number)
  855. ;
  856. SENDHDR:MVI    A,SOH        ;send
  857.     CALL    SEND        ;  'SOH',
  858.     LDA    RECDNO        ;then send
  859.     CALL    SEND        ;  record number
  860.     LDA    RECDNO        ;then record number
  861.     CMA            ;  complemented
  862.     CALL    SEND        ;  record number
  863.     RET            ;from sendhdr
  864. ;
  865. ; ---> SENDREC    send the data record
  866. ;
  867. SENDREC:MVI    C,0        ;init cksum
  868.     CALL    CLRCRC        ;clear the 'CRC' counter
  869.     MVI    D,128        ;init count
  870.     LHLD    RECPTR        ;get buffer address
  871. SENDC:    MOV    A,M        ;get a char
  872.     CALL    SEND        ;send it
  873.     INX    H        ;point to next char
  874.     DCR    D        ;done?
  875.     JNZ    SENDC        ;loop if <=128
  876.     RET            ;from sendrec
  877. ;
  878. ; ---> SENDCKS    send the checksum
  879. ;
  880. SENDCKS:MOV    A,C        ;send the
  881.     CALL    SEND        ;  checksum
  882.     RET            ;from 'SENDCKS'
  883. ;
  884. ; ---> SENDCRC    Send the two Cyclic Redundancy Check characters.  Call
  885. ;        FINCRC to calculate the CRC which will be in 'DE' upon
  886. ;        return.
  887. ;
  888. SENDCRC:CALL    FINCRC        ;calculate the 'CRC' for this record
  889.     MOV    A,D        ;put first 'CRC' byte in accumulator
  890.     CALL    SEND        ;send it
  891.     MOV    A,E        ;put second 'CRC' byte in accumulator
  892.     CALL    SEND        ;send it
  893.     XRA    A        ;set zero return code
  894.     RET
  895. ;
  896. ; ---> GETACK  Get the ACK on the record
  897. ;
  898. ; Returns with carry clear if ACK received.  If an ACK is not received,
  899. ; the error count is incremented, and if less than 10, carry is set and
  900. ; the record is resent.  if the error count is 10, the program aborts.
  901. ; waits 12 seconds to avoid any collision with the receiving station.
  902. ;
  903. GETACK:    MVI    B,12        ;wait 12 seconds max
  904.     CALL    RECVDG        ;receive with garbage collect
  905.     JC    ACKERR        ;timed out
  906.     CPI    ACK        ;was it an 'ACK' character?
  907.     RZ            ;yes, return
  908. ;
  909.      IF    NOT ACKNAK
  910.     CPI    NAK        ;was it an authentic 'NAK'?
  911.     JNZ    GETACK        ;ignore if neither 'ACK' nor 'NAK'
  912.      ENDIF            ;NOT ACKNAK
  913. ;
  914. ; Timeout or error on ACK - bump error count then resend the record if
  915. ; error limit is not exceeded
  916. ;
  917. ACKERR:    LDA    ERRCT        ;get count
  918.     INR    A        ;bump it
  919.     STA    ERRCT        ;save back
  920.     CPI    10        ;at limit?
  921.     RC            ;if not, go resend the record
  922. ;
  923. ; Reached error limit
  924. ;
  925. CSABORT:CALL    ERXIT
  926.     DB    '++ SEND FILE CANCELLED ++$'
  927. ;
  928. ABORT:    LXI    SP,STACK
  929. ABORTL:    MVI    B,1        ;one second without characters
  930.     CALL    RECV
  931.     JNC    ABORTL        ;loop until sender done
  932.     MVI    A,CAN        ;ctl- x
  933.     CALL    SEND        ;stop sending end
  934. ABORTW:    MVI    B,1        ;one second without chracters
  935.     CALL    RECV
  936.     JNC    ABORTW        ;loop until sender done
  937.     MVI    A,CR        ;get a space...
  938.     CALL    SEND        ;to clear out ctl-x
  939.     CALL    ERXIT        ;exit with abort message
  940.     DB    '++ XMODEM',VERSION+'0',MODLEV+'0',' ABORTED ++$'  
  941. ;
  942. ; ---> INCRRNO    increment record number
  943. ;
  944. INCRRNO:PUSH    H
  945.     LHLD    RECDNO        ;increment record number
  946.     INX    H
  947.     SHLD    RECDNO
  948.     LHLD    CONOUT+1    ;check to see if showing count on crt
  949.     MOV    A,H        ;if both zero, user did not fill out
  950.     ORA    L        ;   "conout:  jmp 0000h" in patch area
  951.     JZ    INCRN5        ;   with his own console output address
  952. ;
  953. ; Display the record count on the local CRT if "CONOUT" was filled in by
  954. ; the implementor
  955.  
  956.     MVI    A,1
  957.     STA    CONONL        ;set local only
  958.     CALL    print
  959.     DB    CR,'Record # ',0
  960.     LHLD    RECDNO
  961.     CALL    DHXOUT
  962.     CALL    print
  963.     DB    'H',0
  964.     XRA    A        ;reset the flag for local only
  965.     STA    CONONL
  966. ;
  967. INCRN5:    POP    H        ;here from above if no conout
  968.     RET
  969. ;
  970. ; ---> CHEKFIL    See if file exists
  971. ;
  972. ; If it exists, say use a different name.
  973. ;
  974. CHEKFIL: IF    NOT SETAREA
  975.     LDA    PRVTFL        ;receiving in private area?
  976.     ORA    A
  977.     CNZ    RCAREA     ;if yes, set drive and user area
  978.      ENDIF            ;NOT SETAREA
  979. ;
  980.      IF    SETAREA
  981.     CALL    RCAREA     ;set the designated area up
  982.      ENDIF            ;SETAREA
  983. ;
  984. CHEKFIL1:
  985.     LXI    D,FCB        ;point to control block
  986.     MVI    C,SRCHF     ;see if it
  987.     CALL    BDOS        ;   exists
  988.     INR    A        ;found?
  989.     RZ            ;   no, return
  990.     CALL    ERXIT        ;exit, print error message
  991.     DB    '++ File exists, use a different name ++$'
  992. ;
  993. ; ---> MAKEFIL    Makes the file to be received
  994. ;
  995. MAKEFIL:XRA    A        ;set extent and record number to 0
  996.     STA    FCBEXT
  997.     STA    FCBRNO
  998.     LXI    D,FCB        ;point to fcb
  999.     MVI    C,MAKE        ;get bdos fnc
  1000.     CALL    BDOS        ;to the make
  1001.     INR    A        ;ff=bad?
  1002.     RNZ            ;open ok
  1003. ;
  1004. ; Directory full - can't make file
  1005. ;
  1006.     CALL    ERXIT
  1007.         DB      '++ Error: can''t make file -'
  1008.     DB    ' directory may be full? ++$'
  1009. ;
  1010. ; ---> CNREC  Computes record count, and saves it until a successful
  1011. ;           file-open.
  1012. ;
  1013. ; Look up the FCB in the directory
  1014. ;
  1015. CNREC:    MVI    C,CFSIZE    ;computes file size
  1016.     LXI    D,FCB
  1017.     CALL    BDOS        ;read first
  1018.     LHLD    RANDOM        ;get the file size
  1019.     SHLD    RCNT        ;save total record count
  1020.     MOV    A,H
  1021.     ORA    L
  1022.     RNZ            ;return if not zero length
  1023. NONAME:    CALL    ERXIT
  1024.     DB    '++ No file with that name ++$'
  1025. ;
  1026. ; ---> OPENFIL    Opens the file to be sent
  1027. ;
  1028. OPENFIL:XRA    A        ;set extent and rec number to 0
  1029.     STA    FCBEXT        ;  for proper open
  1030.     STA    FCBRNO
  1031.     LXI    D,FCB        ;point to file
  1032.     MVI    C,OPEN        ;get function
  1033.     CALL    BDOS        ;open it
  1034.     INR    A        ;open ok?
  1035.     JNZ    OPNOK        ;if yes, exit
  1036.     LDA    OPTSAV        ;get command line option
  1037.     CPI    'L'             ;want to send a library file?
  1038.     JNZ    NONAME        ;exit, if not
  1039.     CALL    print
  1040.     DB    CR,LF,'++ No library file with that name ++',CR,LF,0
  1041.     JMP    OPTNERR
  1042. ;
  1043. ; Check for distribution-protected file
  1044. ;
  1045. OPNOK:    LDA    FCB+1        ;first char of file name
  1046.     ANI    80H        ;check bit 7
  1047.     JNZ    OPENOT        ;if on, file can not be sent
  1048.     LDA    FCB+2        ;also check "f2" for tab
  1049.     ANI    80H        ;is is set?
  1050. ;
  1051.      IF    NOSYS
  1052.     JNZ    OPENOT
  1053.     LDA    FCB+10
  1054.     ANI    80H
  1055.     JNZ    NONAME        ;if $sys then fake a "file not found"
  1056.      ENDIF
  1057. ;
  1058.     JZ    OPNOK2     ;if not, ok to send file
  1059. OPENOT:    CALL    ERXIT        ;exit with message
  1060.     DB    '++ File is not for distribution, sorry ++$'
  1061. ;
  1062. OPNOK2:LDA    OPTSAV
  1063.     CPI    'L'
  1064.     JNZ    OPN2
  1065.     LXI    D,DEFDMA
  1066.     MVI    C,SETDMA
  1067.     CALL    BDOS
  1068.     MVI    C,READ
  1069.     LXI    D,FCB
  1070.     CALL    BDOS
  1071.     LHLD    8EH
  1072.     SHLD    DIRSZ
  1073.     LXI    H,DEFDMA
  1074.     MOV    A,M
  1075.     ORA    A
  1076.     JZ    CKDIR        ;check directory present?
  1077. NOTLBR:    CALL    ERXIT
  1078.     DB    '++ Library directory invalid? ++$'
  1079. ;
  1080. ; --> CKDIR  Check to see if there is a .LBR file directory with that
  1081. ;         name and complain if not.
  1082. ;
  1083. CKDIR:    MVI    B,11        ;maximum length of file name
  1084.     MVI    A,' '        ;first entry must be all blanks
  1085.     INX    H
  1086. CKDLP:    CMP    M
  1087.     JNZ    NOTLBR
  1088.     DCR    B
  1089.     INX    H
  1090.     JNZ    CKDLP
  1091. ;
  1092. ; The first entry in the .LBR directory is indeed blank.  Now see if the
  1093. ; directory size is more than 0.
  1094. ;
  1095.     MOV    D,M        ;get directory starting location
  1096.     INX    H        ;...which must be 0000H...
  1097.     MOV    A,M
  1098.     ORA    D
  1099.     JNZ    NOTLBR        ;directory does not start in record 0
  1100.     INX    H
  1101.     MOV    A,M        ;get size of directory
  1102.     INX    H
  1103.     ORA    M
  1104.     JZ    NOTLBR        ;directory must be >0 sectors!
  1105.     LXI    H,DEFDMA    ;point to directory
  1106. ;
  1107. ; The next routine checks the .LBR directory for the specified member.
  1108. ; Name one sector at a time.
  1109. ;
  1110. CMLP:    MOV    A,M        ;get member active flag
  1111.     ORA    A        ;00=active, anything else can be...
  1112.     MVI    B,11        ;...regarded as invalid (erased or blank)
  1113.     INX    H        ;point to member name
  1114.     JNZ    NOMTCH        ;no match if inactive entry
  1115. CKLP:    LDAX    D        ;now compare the file name specified...
  1116.     CMP    M        ;...against the member file name
  1117.     JNZ    NOMTCH        ;exit loop if no match found
  1118.     INX    H
  1119.     INX    D
  1120.     DCR    B
  1121.     JNZ    CKLP        ;check all 11 chars
  1122.     MOV    E,M        ;got the file - get file address
  1123.     INX    H
  1124.     MOV    D,M
  1125.     XCHG
  1126.     SHLD    INDEX        ;save file addr in LBR
  1127.     XCHG
  1128.     INX    H
  1129.     MOV    E,M        ;get the file size
  1130.     INX    H
  1131.     MOV    D,M
  1132.     XCHG
  1133.     DCX    H
  1134.     SHLD    RCNT        ;save size as # of records
  1135.     LHLD    INDEX        ;get file address
  1136.     SHLD    RANDOM        ;place it into random field
  1137.     XRA    A
  1138.     STA    RANDOM+2    ;must zero the 3rd byte
  1139.     STA    FCBRNO        ;also zero FCB record #
  1140.     LXI    D,FCB        ;point to FCB of LBR file
  1141.     MVI    C,RRDM        ;read random
  1142.     CALL    BDOS
  1143.     JMP    OPNOK3        ;no need to error check
  1144. ;
  1145. ; Come here if no file name match and another sector is needed
  1146. ;
  1147. NOMTCH:    INX    H        ;skip past the end of the file entry
  1148.     DCR    B
  1149.     JNZ    NOMTCH
  1150.     LXI    B,20        ;point to next file entry
  1151.     DAD    B
  1152.     LXI    D,MEMFCB    ;point to member name again
  1153.     MOV    A,H        ;see if we checked all 4 entries
  1154.     ORA    A
  1155.     JZ    CMLP        ;no, check next
  1156.     LHLD    DIRSZ        ;get directory size
  1157.     MOV    A,H
  1158.     ORA    L
  1159.     JNZ    INLBR        ;continue if still more to check
  1160.     CALL    ERXIT
  1161.     DB    '++ File not found in library ++$'
  1162. ;
  1163. INLBR:    DCX    H        ;decrement directory size
  1164.     SHLD    DIRSZ
  1165.     MVI    C,READ        ;read next sector of directory
  1166.     LXI    D,FCB
  1167.     CALL    BDOS
  1168.     LXI    H,DEFDMA    ;set our pointers for compare
  1169.     LXI    D,MEMFCB
  1170.     JMP    CMLP        ;check next sector
  1171. ;
  1172. OPN2:     IF    NOLBS OR NOCOMS ;check for send restrictions
  1173.     LXI    H,FCB+11
  1174.     MOV    A,M        ;check for protect attr
  1175.     ANI    7FH        ;remove cp/m 2.x attrs
  1176.      ENDIF            ;NOLBS OR NOCOMS
  1177. ;
  1178.      IF    NOLBS        ;do not allow '#' to be sent
  1179.     CPI    '#'             ;chk for '#' as last first
  1180.     JZ    OPENOT        ;if '#', can not send, show why
  1181.      ENDIF            ;NOLBS
  1182. ;
  1183.      IF    NOCOMS        ;do not allow '.COM' to be sent
  1184.     CPI    'M'             ;if not, check for '.COM'
  1185.     JNZ    OPNOK3     ;if not, ok to send
  1186.     DCX    H
  1187.     MOV    A,M        ;check next character
  1188.     ANI    7FH        ;strip attributes
  1189.     CPI    'O'             ;'O'?
  1190.     JNZ    OPNOK3     ;if not, ok to send
  1191.     DCX    H
  1192.     MOV    A,M        ;now check 1st character
  1193.     ANI    7FH        ;strip attributes
  1194.     CPI    'C'             ;'C' as in '.COM'?
  1195.     JNZ    OPNOK3     ;if not, continue
  1196.     CALL    ERXIT        ;exit with message
  1197.     DB    '++ Can''t Send a .COM File ++$'
  1198.      ENDIF            ;NOCOMS
  1199. ;
  1200. OPNOK3:    lda    aclvl
  1201.     ora    a
  1202.     jnz    norst
  1203.     lhld    rcnt
  1204.     mov    a,h
  1205.     ora    a
  1206.     jnz    nonono
  1207.     mov    a,l
  1208.     cpi    81
  1209.     jnc    nonono
  1210. norst:    CALL    print        ;print:
  1211.     DB    'File open: ',0
  1212.     LHLD    RCNT        ;get record count
  1213.     LDA    OPTSAV
  1214.     CPI    'L'
  1215.     JNZ    OPNOK4     ;if send from library add 1 to
  1216.     INX    H        ;show correct record count
  1217. OPNOK4:call    decout
  1218.     call    print
  1219.     db    ' (',0
  1220.     CALL    DHXOUT        ;print hex number of records
  1221.     CALL    print
  1222. ;
  1223.     DB    'H) records',CR,LF
  1224.     DB    'Send time: ',0
  1225.     CALL    SPEED        ;get speed indicator
  1226.     LXI    D,0
  1227.     MOV    E,A        ;set up for table access
  1228.     LXI    H,BTABLE    ;point to baud factor table
  1229.     DAD    D        ;index to proper factor
  1230.     MOV    A,M        ;factor in 'A'
  1231.     LHLD    RCNT        ;get number of records
  1232.     CALL    DIVHLA        ;divide hl by value in a (records/min)
  1233.     PUSH    H
  1234. ;
  1235.      IF    LGCL
  1236.     SHLD    PGSIZE
  1237.      ENDIF            ;LGCL
  1238. ;    
  1239.     MVI    H,0
  1240.     CALL    DECOUT        ;print decimal number of minutes
  1241.     CALL    print
  1242.     DB    ' mins, ',0
  1243.     LXI    H,RECTBL    ;point to divisors for seconds calc.
  1244.     LXI    D,0
  1245.     CALL    SPEED        ;get speed indicator
  1246.     MOV    E,A
  1247.     DAD    D        ;index into table
  1248.     MOV    A,M        ;get multiplier
  1249.     POP    H        ;get remainder
  1250.     CALL    MULHA        ;multiply 'H' by 'A'
  1251.     CALL    SHFTHL
  1252.     CALL    SHFTHL
  1253.     CALL    SHFTHL
  1254.     CALL    SHFTHL
  1255.     MVI    H,0
  1256.     CALL    DECOUT        ;print the seconds portion
  1257.     CALL    print
  1258.     DB    ' secs at ',0
  1259.     LXI    H,SPTBL     ;start of baud rate speeds
  1260.     MVI    D,0        ;zero the 'D' register
  1261.     CALL    SPEED        ;get speed indicator
  1262.     ADD    A        ;index into the baud rate table
  1263.     ADD    A
  1264.     MOV    E,A        ;now have the index factor in 'DE'
  1265.     DAD    D        ;add to 'HL'
  1266.     XCHG            ;put address in 'DE' regs.
  1267.     MVI    C,PRINTF     ;show the baud
  1268.     CALL    BDOS
  1269.     CALL    print
  1270.     DB    ' bps',CR,LF
  1271.     DB    'To cancel: use CTL-X',CR,LF,0
  1272.     RET
  1273. ;
  1274. BTABLE:    DB    5,13,20,24,30,48,0
  1275. RECTBL:    DB    192,74,48,40,32,20,0
  1276. SPTBL:    DB    '110$','300$','450$','600$','710$','1200$'
  1277. ;
  1278. nonono:    call    erxit
  1279.     db    cr,lf,lf
  1280.     db    '+++ File is too large for nonmembers +++',cr,lf,'$'
  1281. ;
  1282. ; ---> DIVHLA  Divides 'HL' by value in 'A'
  1283. ;           upon exit: L=quotient, H=remainder
  1284. ;
  1285. DIVHLA:    PUSH    B
  1286.     MVI    B,8        ;shift factor to 'B'
  1287.     MOV    C,A        ;divisor to 'C'
  1288. DIV2:    XRA    A        ;clear carry flag and accumulator
  1289.     DAD    H
  1290.     MOV    A,H
  1291.     SUB    C
  1292.     JM    DIV3        ;dont borrow on neg results
  1293.     MOV    H,A
  1294.     MOV    A,L
  1295.     ORI    1        ;borrow 1
  1296.     MOV    L,A
  1297. DIV3:    DCR    B
  1298.     JNZ    DIV2
  1299.     POP    B
  1300.     RET
  1301. ;
  1302. ; ---> MULHA  Multiply the value in 'H' by the value in 'A'
  1303. ;           Return with answer in 'HL'.
  1304. ;
  1305. MULHA:    MOV    B,A        ;put loop count in 'B'
  1306.     MVI    D,0
  1307.     MOV    E,H
  1308.     MOV    L,H
  1309.     MVI    H,0
  1310. MULLP:    DCR    B
  1311.     RZ
  1312.     DAD    D
  1313.     JMP    MULLP
  1314.     RET
  1315. ;
  1316. ; Shift the 'HL' pair one bit to the right
  1317. ;
  1318. SHFTHL:    MOV    A,L
  1319.     RAR
  1320.     MOV    L,A
  1321.     ORA    A        ;clear the carry bit
  1322.     MOV    A,H
  1323.     RAR
  1324.     MOV    H,A
  1325.     RNC
  1326.     MVI    A,80H
  1327.     ORA    L
  1328.     MOV    L,A
  1329.     RET
  1330. ;
  1331. ; ---> CLOSFIL    Closes the received file
  1332. ;
  1333. CLOSFIL:LXI    D,FCB        ;point to file
  1334.     MVI    C,CLOSE     ;get function
  1335.     CALL    BDOS        ;close it
  1336.     INR    A        ;close ok?
  1337.     RNZ            ;  yes, return
  1338.     CALL    ERXIT        ;  no, abort
  1339.     DB    '++ Can''t close file ++$'
  1340. ;
  1341. ; ---> DECOUT  Decimal output routine
  1342. ;
  1343. DECOUT:    PUSH    B
  1344.     PUSH    D
  1345.     PUSH    H
  1346.     LXI    B,-10
  1347.     LXI    D,-1
  1348. DECOU2:    DAD    B
  1349.     INX    D
  1350.     JC    DECOU2
  1351.     LXI    B,10
  1352.     DAD    B
  1353.     XCHG
  1354.     MOV    A,H
  1355.     ORA    L
  1356.     CNZ    DECOUT
  1357.     MOV    A,E
  1358.     ADI    '0'
  1359.     CALL    CTYPE
  1360.     POP    H
  1361.     POP    D
  1362.     POP    B
  1363.     RET
  1364. ;
  1365. ; ---> DHXOUT  Double precision hex output routine.  Call with hex
  1366. ;           value in 'HL'.
  1367. ;
  1368. DHXOUT:    PUSH    H        ;save h,l
  1369.     PUSH    PSW        ;save a
  1370.     MOV    A,H        ;get ms byte
  1371.     CALL    HEXO        ;output high order byte
  1372.     MOV    A,L        ;get ls byte
  1373.     CALL    HEXO        ;output low order byte
  1374.     POP    PSW        ;restore a
  1375.     POP    H        ;restore h,l
  1376.     RET            ;return to caller
  1377. ;
  1378. ; ---> RDRECD  Reads a record
  1379. ;
  1380. ; For speed, this routine buffers up 16 records at a time.
  1381. ;
  1382. RDRECD:    LDA    RECNBF        ;get number of records in buffer
  1383.     DCR    A        ;decrement it
  1384.     STA    RECNBF
  1385.     CPI    0FFH
  1386.     JZ    RDBLOCK     ;exhausted?  need more
  1387.     LHLD    RECPTR        ;get buffer address
  1388.     LXI    D,128        ;add length of one record
  1389.     DAD    D        ;  to next buffer
  1390.     SHLD    RECPTR        ;save buffer address
  1391.     RET            ;from "readred"
  1392. ;
  1393. ; Buffer is empty - read in another block of 16
  1394. ;
  1395. RDBLOCK:LDA    EOFLG        ;get 'EOF' flag
  1396.     CPI    1        ;is it set?
  1397.     STC            ;to show 'EOF'
  1398.     RZ            ;got 'EOF'
  1399.     MVI    C,0        ;records in block
  1400.     LXI    D,DBUF        ;to disk buffer
  1401. RDRECLP:PUSH    B
  1402.     PUSH    D
  1403.     MVI    C,SETDMA    ;set dma address
  1404.     CALL    BDOS
  1405.     LXI    D,FCB
  1406.     MVI    C,READ
  1407.     CALL    BDOS
  1408.     POP    D
  1409.     POP    B
  1410.     ORA    A        ;read ok?
  1411.     JZ    RDRECOK     ;yes
  1412.     DCR    A        ;'EOF'?
  1413.     JZ    REOF        ;got 'EOF'
  1414. ;
  1415. ; Read error
  1416. ;
  1417.     CALL    ERXIT
  1418.     DB    '++ File read error ++$'
  1419. ;
  1420. RDRECOK:LXI    H,128        ;add length of one record
  1421.     DAD    D        ;  to next buffer
  1422.     XCHG            ;buff to de
  1423.     INR    C        ;more records?
  1424.     MOV    A,C        ;get count
  1425.     CPI    200        ;done?
  1426.     JZ    RDBFULL     ;  yes, buffer is full
  1427.     JMP    RDRECLP     ;read more
  1428. ;
  1429. REOF:    MVI    A,1
  1430.     STA    EOFLG        ;set eof flag
  1431.     MOV    A,C
  1432. ;
  1433. ; Buffer is full, or got eof
  1434. ;
  1435. RDBFULL:STA    RECNBF        ;store record count
  1436.     LXI    H,DBUF-128    ;init buffer pointear
  1437.     SHLD    RECPTR        ;save buffer address
  1438.     LXI    D,DEFDMA    ;reset dma address
  1439.     MVI    C,SETDMA
  1440.     CALL    BDOS
  1441.     JMP    RDRECD        ;pass record to caller
  1442. ;
  1443. ; ---> WRRECD  Write a record
  1444. ;
  1445. ; Writes the record into a buffer.  When 16 have been written, writes
  1446. ; the block to disk.
  1447. ;
  1448. ; Entry point "WRBLOCK" flushes the buffer at EOF
  1449. ;
  1450. WRRECD:    LHLD    RECPTR        ;get buffer address
  1451.     LXI    D,128        ;add length of one record
  1452.     DAD    D        ;  to next buffer
  1453.     SHLD    RECPTR        ;save buffer address
  1454.     LDA    RECNBF        ;bump the
  1455.     INR    A        ;  record number
  1456.     STA    RECNBF        ;  in the buffer
  1457.     CPI    200        ;have we 16?
  1458.     RNZ            ;no, return
  1459. ;
  1460. ; ---> WRBLOCK    Writes a block to disk
  1461. ;
  1462. WRBLOCK:LDA    RECNBF        ;number of records in the buffer
  1463.     ORA    A        ;0 means end of file
  1464.     RZ            ;none to write
  1465.     MOV    C,A        ;save count
  1466.     LXI    D,DBUF        ;point to disk buff
  1467. DKWRLP:    PUSH    H
  1468.     PUSH    D
  1469.     PUSH    B
  1470.     MVI    C,SETDMA    ;set dma
  1471.     CALL    BDOS        ;to buffer
  1472.     LXI    D,FCB        ;then write the block
  1473.     MVI    C,WRITE
  1474.     CALL    BDOS
  1475.     POP    B
  1476.     POP    D
  1477.     POP    H
  1478.     ORA    A
  1479.     JNZ    WRERR        ;oops, error
  1480.     LXI    H,128        ;length of 1 record
  1481.     DAD    D        ;'HL'= next buff
  1482.     XCHG            ;to 'DE' for setdma
  1483.     DCR    C        ;more records?
  1484.     JNZ    DKWRLP        ;  yes, loop
  1485.     XRA    A        ;get a zero
  1486.     STA    RECNBF        ;reset number of records
  1487.     LXI    H,DBUF        ;reset buffer buffer
  1488.     SHLD    RECPTR        ;save buffer address
  1489. RSDMA:    LXI    D,DEFDMA    ;reset dma address
  1490.     MVI    C,SETDMA
  1491.     CALL    BDOS
  1492.     RET
  1493. ;
  1494. WRERR:    CALL    RSDMA        ;reset dma to normal
  1495.     MVI    C,CAN        ;cancel
  1496.     CALL    SEND        ;  sender
  1497.     CALL    RCVSABT     ;kill receive file
  1498.     CALL    ERXIT        ;exit with msg:
  1499.     DB    '++ Error writing file ++$'
  1500. ;
  1501. ;----> RECV  Receive a character
  1502. ;
  1503. ; Timeout time is in 'B' in seconds.  Entry via 'RECVDG' deletes garbage
  1504. ; characters on the line.  For example, having just sent a record,
  1505. ; calling 'RECVDG' will delete any line-noise-induced characters "long"
  1506. ; before the ACK/NAK would be received.
  1507. ;
  1508. RECVDG:
  1509. RECV:    PUSH    D        ;save 'DE' regs.
  1510.     MVI    E,MHZ        ;get the clock speed
  1511.     XRA    A        ;clear the 'A' reg.
  1512. MSLOOP:    ADD    B        ;number of seconds
  1513.     DCR    E        ;one less mhz. to go
  1514.     JNZ    MSLOOP        ;if not zero, continue
  1515.     MOV    B,A        ;put total value back into 'B'
  1516. MSEC:    LXI    D,0205h        ;1 second dcr count
  1517. MWTI:    CALL    RCVRDY        ;input from modem ready
  1518. ;
  1519.      IF    (NOT INTER3) AND (NOT ALTOS) AND (NOT EXTMOD)
  1520.     STA    ERRCDE
  1521.      ENDIF
  1522. ;
  1523.     JZ    MCHAR        ;got char
  1524.     DCR    E        ;count down for timeout
  1525.     JNZ    MWTI
  1526.     DCR    D
  1527.     JNZ    MWTI
  1528.     DCR    B        ;more seconds?
  1529.     JNZ    MSEC        ;yes, wait
  1530. ;
  1531. ; Test for the presence of carrier - if none, go to 'CARCK' and continue
  1532. ; testing for specified time.  If carrier returns, continue.  If is doesn't
  1533. ; return, exit.
  1534. ;
  1535.     CALL    CAROK        ;is carrier still on?
  1536.     CNZ    CARCK        ;if not, test for 15 seconds
  1537. ;
  1538. ; Modem timed out receiving - but carrier is still on.
  1539. ;
  1540.     POP    D        ;restore d,e
  1541.     STC            ;carry shows timeout
  1542.     RET
  1543. ;
  1544. ; Get character from modem.
  1545. ;
  1546. MCHAR:    CALL    MDIN        ;get data byte from modem
  1547.         POP     D               ;restore 'DE'
  1548. ;
  1549. ; Calculate Checksum and CRC
  1550. ;
  1551.     PUSH    PSW        ;save the character
  1552.     CALL    UPDCRC        ;calculate crc
  1553.     ADD    C        ;add to checksum
  1554.     MOV    C,A        ;save checksum
  1555.     POP    PSW        ;restore char
  1556.     ORA    A        ;carry off: no error
  1557.     RET            ;from "recv"
  1558. ;
  1559. ; CARCK - common carrier test for recv and send.  If carrier returns
  1560. ; within TIMOUT seconds, normal program execution continues.  Else,
  1561. ; it will abort to CP/M via EXIT.
  1562. ;
  1563. CARCK:    MVI    E,TIMOUT*10    ;value for 15 second delay
  1564. CARCK1:    CALL    DELAY        ;kill .1 seconds
  1565.     CALL    CAROK        ;is carrier still on?
  1566.     RZ            ;return if carrier on
  1567.     DCR    E        ;has 15 seconds expired?
  1568.     JNZ    CARCK1        ;if not, continue testing
  1569. ;
  1570. ; See if got a local console, and report if so.
  1571. ;
  1572.     LHLD    CONOUT+1    ;get conout address
  1573.     MOV    A,H        ;zero if no local console
  1574.     ORA    L
  1575.     JZ    CARCK2
  1576. ;
  1577.     MVI    A,1        ;print local only
  1578.     STA    CONONL
  1579.     CALL    print        ;report loss of carrier
  1580.     DB    CR,LF,'++ Carrier lost in XMODEM ++',CR,LF,0
  1581. CARCK2:    LDA    OPTSAV        ;get option
  1582.     CPI    'R'             ;if not receive
  1583.     JNZ    EXIT        ;then abort now, else
  1584.     CALL    DELFILE     ;get rid of the junk first
  1585.     JMP    EXIT        ;else, abort to cp/m.
  1586. ;
  1587. ; Delay - 100 millisecond delay.
  1588. ;
  1589. DELAY:    PUSH    B        ;save 'BC'
  1590.     LXI    B,MHZ*4167    ;value for 100 ms. delay
  1591. DELAY2:    DCX    B        ;update count
  1592.     MOV    A,B        ;get ms byte
  1593.     ORA    C        ;count = zero?
  1594.     JNZ    DELAY2        ;if not, continue
  1595.     POP    B        ;restore 'BC'
  1596.     RET            ;return to carck1.
  1597. ;
  1598. ; ---> SEND  Send a character to the modem
  1599. ;
  1600. SEND:    PUSH    PSW        ;save the character
  1601.     CALL    UPDCRC        ;calc the crc
  1602.     ADD    C        ;calc cksum
  1603.     MOV    C,A        ;save cksum
  1604. SENDW:    CALL    SNDRDY        ;is transmit ready
  1605.     JZ    SENDR        ;  yes, go send
  1606. ;
  1607. ; Xmit status not ready, so test for carrier before looping - if lost,
  1608. ; go to CARCK and give it up to 15 seconds to return.  If it doesn't,
  1609. ; return abort via EXIT.
  1610. ;
  1611.         PUSH    D               ;save 'DE'
  1612.     CALL    CAROK        ;is carrier still on?
  1613.     CNZ    CARCK        ;if not, continue testing it
  1614.     POP    D        ;restore 'DE'
  1615.     JMP    SENDW        ;else, wait for xmit ready.
  1616. ;
  1617. ; ---> WAITNAK    Waits for initial NAK
  1618. ;
  1619. ; To ensure no data is sent until the receiving program is ready, this
  1620. ; routine waits for the first timeout-nak or the letter 'C' for CRC
  1621. ; from the receiver.  If CRC is in effect, then Cyclic Redundancy Checks
  1622. ; are used instead of checksums.  'E' contains the number of seconds to
  1623. ; wait.
  1624. ;
  1625. ; If the first character received is a CAN (CTL-X) then the send will be
  1626. ; aborted as though it had timed out.
  1627. ;
  1628. WAITNAK:MVI    B,1        ;timeout delay
  1629.     CALL    RECV        ;did we get
  1630.     CPI    CRC        ;'CRC' indicated?
  1631.     RZ            ;yes, send block
  1632.     CPI    NAK        ;a 'NAK' indicating checksum?
  1633.     JZ    SETNAK        ;yes go put checksum in effect
  1634.     CPI    CAN        ;was it a cancel (ctl-x)?
  1635.     JZ    ABORT        ;yes, abort
  1636.     DCR    E        ;finished yet?
  1637.     JZ    ABORT        ;yes, abort
  1638.     JMP    WAITNAK     ;no, loop
  1639. ;
  1640. ; ---> WAITCRC    Turn on CRC flag
  1641. ;
  1642. SETNAK:    MVI    A,'C'           ;make sure in checksum
  1643.     STA    CRCFLG
  1644.     RET
  1645. ;
  1646. ; ---> MOVEFCB    Moves the filename to the FCB
  1647. ;
  1648. ; This routine moves the filename from the default command line buffer
  1649. ; to the file control block (FCB).
  1650. ;
  1651. MOVEFCB:LHLD    SAVEHL        ;get position on command line
  1652.     CALL    GETB        ;get numeric position
  1653.     LXI    D,FCB+1
  1654.     CALL    MOVENAM     ;move name to fcb
  1655.     XRA    A
  1656.     STA    FCBRNO        ;zero record number
  1657.     STA    FCBEXT        ;zero extent
  1658.     LDA    OPTSAV        ;this going to be a library file?
  1659.     CPI    'L'
  1660.     RNZ            ;if not, finished
  1661. ;
  1662. ; Handles library entries, first checks for proper .LBR extent.  If no
  1663. ; extent was included, it adds one itself.
  1664. ;
  1665.     SHLD    SAVEHL
  1666.     LXI    H,FCB+9     ;1st extent char.
  1667.     MOV    A,M
  1668.     CPI    ' '
  1669.     JZ    NOEXT        ;no extent, make one
  1670.     CPI    'L'             ;check 1st char. in extent
  1671.     JNZ    LBRERR
  1672.     INX    H        
  1673.     MOV    A,M
  1674.     CPI    'B'             ;check 2nd char. in extent
  1675.     JNZ    LBRERR
  1676.     INX    H
  1677.     MOV    A,M
  1678.     CPI    'R'             ;check 3rd char. in extent
  1679.     JNZ    LBRERR
  1680. ;
  1681. ; Get the name of the desired file in the library
  1682. ;
  1683. MOVEF1:    LHLD    SAVEHL        ;get current position on command line
  1684.     CALL    CHKMSP        ;see if valid library member file name
  1685.     INR    B        ;increment for move name
  1686.     LXI    D,MEMFCB    ;store member name in special buffer
  1687.     JMP    MOVENAM     ;move from command line to buffer, done
  1688. ;
  1689. ; Check for any spaces prior to library member file name, if none (or
  1690. ; only spaces remaining), no name.
  1691. ;
  1692. CHKMSP:    DCR    B
  1693.     JZ    MEMERR
  1694.     MOV    A,M
  1695.     CPI    ' '+1
  1696.     RNC
  1697.     INX    H
  1698.     JMP    CHKMSP
  1699. ;
  1700. ; Gets the count of characters remaining on the command line
  1701. ;
  1702. GETB:    MOV    A,L
  1703.     SUI    DEFDMA+2    ;start location of 1st command
  1704.     MOV    B,A        ;store for now
  1705.     LDA    DEFDMA        ;find length of command line
  1706.     SUB    B        ;subtract those already used
  1707.     MOV    B,A        ;now have number of bytes remaining
  1708.     RET
  1709. ;
  1710. LBRERR:    CALL    ERXIT
  1711.     DB    '++ Invalid library name ++$'
  1712. ;
  1713. MEMERR:    CALL    print
  1714.     DB    CR,LF,'++ No library member file requested ++',CR,LF,0
  1715.     JMP    OPTNERR
  1716. ;
  1717. ; Add .LBR extent to the library file name
  1718. ;
  1719. NOEXT:    LXI    H,FCB+9     ;location of extent
  1720.     MVI    M,'L'
  1721.     INX    H
  1722.     MVI    M,'B'
  1723.     INX    H
  1724.     MVI    M,'R'
  1725.     JMP    MOVEF1        ;now get the library member name
  1726. ;
  1727. ; Move a file name from the 'DEFDMA' command line buffer into FCB
  1728. ;
  1729. MOVENAM:MVI    C,1
  1730. MOVEN1:    MOV    A,M
  1731.     CPI    ' '+1           ;name ends with space or return
  1732.     RC            ;end of name
  1733.     CPI    '.'
  1734.     JZ    CHKFIL        ;file name might be less than 8 chars.
  1735.     STAX    D        ;store
  1736.     INX    D        ;next position to store char.
  1737.     INR    C        ;one less to go
  1738.     MOV    A,C
  1739.     CPI    12+1
  1740.     JNC    NONAME        ;11 chars. maximum filename plus extent
  1741. MOVEN2:    INX    H        ;next char. in file name
  1742.     DCR    B
  1743.     JZ    OPTNERR     ;end of name, see if done yet
  1744.     JMP    MOVEN1
  1745. ;
  1746. ; See if any spaces needed between file name and .ext
  1747. ;
  1748. CHKFIL:    MOV    A,C
  1749.     CPI    9
  1750.     JNC    MOVEN2        ;up to 1st character in .ext now
  1751.     MVI    A,' '           ;be sure there is a blank there now
  1752.     STAX    D
  1753.     INR    C
  1754.     INX    D
  1755.     JMP    CHKFIL        ;go do another
  1756. ;
  1757. CTYPE:    PUSH    B        ;save all registers
  1758.     PUSH    D
  1759.     PUSH    H
  1760.     MOV    E,A        ;char to 'E' in case bdos (normal)
  1761.     LDA    CONONL        ;want to bypass 'BYE' output to modem?
  1762.     ORA    A
  1763.     JNZ    CTYPEL        ;yes, go directly to crt, then
  1764.     MVI    C,WRCON     ;bdos console output, to crt and modem
  1765.     CALL    BDOS        ;   since "bye" intercepts the char.
  1766.     POP    H        ;restore all registers
  1767.     POP    D
  1768.     POP    B
  1769.     RET
  1770. ;
  1771. CTYPEL:    MOV    C,E        ;bios needs it in 'C'
  1772.     CALL    CONOUT        ;bios console output routine, not bdos
  1773.     POP    H        ;restore all registers saved by 'CTYPE'
  1774.     POP    D
  1775.     POP    B
  1776.     RET
  1777. ;
  1778. HEXO:    PUSH    PSW        ;save for right digit
  1779.     RAR            ;right justify the left digit
  1780.     RAR
  1781.     RAR
  1782.     RAR
  1783.     CALL    NIBBL        ;print left digit
  1784.     POP    PSW        ;restore right
  1785. ;
  1786. ; Slick new nybble hex maker. If this catches on, hex digits
  1787. ; will never be the same... Lifted from BYE.ASM.
  1788. ;
  1789. NIBBL:    ANI    0FH        ;isolate digit
  1790.     ADI    90H
  1791.     DAA
  1792.     ACI    40H
  1793.     DAA
  1794.     JMP    CTYPE        ;type it
  1795. ;
  1796. EXITLG:
  1797.      IF    LGCL        ;special log caller exit
  1798.     JMP    LGCLL
  1799.      ENDIF            ;LGCL
  1800. ;
  1801.     JMP    EXIT
  1802. ;
  1803. ;
  1804. ; ---> ERXIT  Exit printing message following call
  1805. ;
  1806. ERXIT:    CALL    print
  1807.     DB    CR,LF,0
  1808.     POP    D        ;get message
  1809.     MVI    C,PRINTF     ;get bdos fnc
  1810.     CALL    BDOS        ;print message
  1811.     CALL    print
  1812.     DB    CR,LF,0
  1813. EXIT:    CALL    UNINIT        ;reset vectors (if needed)
  1814.     LDA    OLDDRV        ;restore the original drive
  1815.     MOV    E,A
  1816.     CALL    RECDRX
  1817.     LDA    OLDUSR        ;restore the original number
  1818.     MOV    E,A
  1819.     CALL    RCARE
  1820.     XRA    A
  1821.     LHLD    STACK
  1822.     SPHL
  1823.     RET
  1824. ;
  1825. ; ---> ILPRT  Inline print of message
  1826. ;
  1827. ; The call to ILPRT is followed by a message, binary 0 for its end.
  1828. ;
  1829. PRINT:    XTHL            ;save HL, get HL=message
  1830. ILPLP:    MOV    A,M        ;get the character
  1831.     INX    H        ;to next character
  1832.     ORA    A        ;end of message?
  1833.     JZ    ILPRET        ;  yes, return
  1834.     CALL    CTYPE        ;type the message
  1835.     JMP    ILPLP        ;loop
  1836. ;
  1837. ILPRET:    XTHL            ;restore HL
  1838.     RET            ;past message
  1839. ;
  1840. ; ---> Restore the old user area and drive from a received file
  1841. ;
  1842. ; ---> Set user area to receive file
  1843. ;
  1844. RCAREA:CALL    RECDRV        ;ok set the drive to its place
  1845.     LDA    PRVTFL        ;private area wanted?
  1846.     ORA    A
  1847.     MVI    E,PRUSR     ;yes, set to private area
  1848.     JNZ    RCARE
  1849.     MVI    E,USR        ;ok now set the user area
  1850. RCARE:    MVI    C,USER        ;tell bdos what we want to do
  1851.     CALL    BDOS        ;do it
  1852.     RET
  1853. ;
  1854. RECDRV:    LDA    PRVTFL
  1855.     ORA    A
  1856.     MVI    E,PRDRV-'A'     ;make drive cp/m number
  1857.     JNZ    RECDRX
  1858.     MVI    E,DRV-'A'       ;make drive cp/m number
  1859. RECDRX:    MVI    C,SELDRV    ;tell bdos
  1860.     CALL    BDOS        ;do it
  1861.     RET            ;back
  1862. ;
  1863. ; Move 128 characters from 'HL' to 'DE' length in 'B'
  1864. ;
  1865. MOVE128: MVI    B,128        ;set move count
  1866. MOVE:    MOV    A,M        ;get a char
  1867.     STAX    D        ;store it
  1868.     INX    H        ;to next "from"
  1869.     INX    D        ;to next "to"
  1870.     DCR    B        ;more?
  1871.     JNZ    MOVE        ;  yes, loop
  1872.     RET            ;  no, return
  1873. ;
  1874. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1875. ;
  1876.      IF    LGCL
  1877. ;
  1878. BSIZE:    EQU    80H
  1879. SECT:    EQU    80H
  1880. ;
  1881. ; The following allocations are used by the 'FILE' macros
  1882. ;
  1883. DFLT$USER:    DB    LASTUSR
  1884. CUR$USER:    DB    0FFH
  1885. DFLT$DISK:    DB    LOGDRV-'A'
  1886. CUR$DISK:    DB    0FFH
  1887. PGSIZE:        DB    0,0
  1888. ;
  1889. LGCLL:    JMP    M010
  1890. ;
  1891. FCBCLLR:    DB    0,'LASTCALR'
  1892. lstcft:        db    '000',0 ;the file name has ALWAYS been
  1893.         DS    23          ;LASTCALR, not LASTCALR.DAT!
  1894.         DB    0FFH
  1895. ;
  1896. CLLRADR:    DW    DBUF
  1897. CLLRSIZ:    EQU    BSIZE
  1898. CLLRLEN:    DW    BSIZE
  1899. CLLRPTR:    DS    2
  1900. M010:        JMP    M001
  1901. ;
  1902. GETCLLR:
  1903.     LHLD    CLLRLEN
  1904.     XCHG
  1905.     LHLD    CLLRPTR
  1906.     MOV    A,L
  1907.     SUB    E
  1908.     MOV    A,H
  1909.     SBB    D
  1910.     JC    M007
  1911.     LXI    H,0
  1912.     SHLD    CLLRPTR
  1913. M004:    XCHG
  1914.     LHLD    CLLRLEN
  1915.     MOV    A,E
  1916.     SUB    L
  1917.     MOV    A,D
  1918.     SBB    H
  1919.     JNC    M006
  1920.     LHLD    CLLRADR
  1921.     DAD    D
  1922.     XCHG
  1923.     MVI    C,SETDMA
  1924.     CALL    BDOS
  1925.     LDA    FCBCLLR+36
  1926.     CPI    0FFH
  1927.     JZ    M009
  1928.     MVI    C,USER
  1929.     MOV    E,A
  1930.     CALL    BDOS
  1931. M009:    LXI    D,FCBCLLR
  1932.     MVI    C,RRDM
  1933.     CALL    BDOS
  1934.     CALL    RST$SYSTEM
  1935.     ORA    A
  1936.     JNZ    M005
  1937.     LHLD    FCBCLLR+33
  1938.     INX    H
  1939.     SHLD    FCBCLLR+33
  1940.     LXI    D,SECT
  1941.     LHLD    CLLRPTR
  1942.     DAD    D
  1943.     SHLD    CLLRPTR
  1944.     JMP    M004
  1945. ;
  1946. M005:    LHLD    CLLRPTR
  1947.     SHLD    CLLRLEN
  1948. M006:    LXI    D,DEFDMA
  1949.     MVI    C,SETDMA
  1950.     CALL    BDOS
  1951.     LXI    H,0
  1952.     SHLD    CLLRPTR
  1953. M007:    XCHG
  1954.     LHLD    CLLRADR
  1955.     DAD    D
  1956.     XCHG
  1957.     LHLD    CLLRLEN
  1958.     MOV    A,L
  1959.     ORA    H
  1960.     MVI    A,EOF
  1961.     RZ
  1962.     LDAX    D
  1963.     LHLD    CLLRPTR
  1964.     INX    H
  1965.     SHLD    CLLRPTR
  1966.     RET
  1967. ;
  1968. M001:    XRA    A
  1969.     STA    FCBCLLR+12
  1970.     STA    FCBCLLR+32
  1971.     LXI    H,CLLRSIZ
  1972.     SHLD    CLLRLEN
  1973.     SHLD    CLLRPTR
  1974.     LXI    D,FCBCLLR
  1975.     JMP    M011
  1976. ;
  1977. OPENF:    PUSH    D    
  1978.     MVI    A,0FFH        ;declare current user area on file
  1979.     STA    FILEUA
  1980.     MVI    C,VERNO        ;get version number
  1981.     CALL    BDOS
  1982.     MOV    A,H        ;cp/m 1.x?
  1983.     ORA    L
  1984.     JZ    START2$DISK    ;check for default disk if so
  1985.     MVI    E,0FFH        ;get current user number
  1986.     MVI    C,USER        ;get user code
  1987.     CALL    BDOS
  1988.     MOV    C,A
  1989.     LDA    DFLT$USER    ;check if at default user
  1990.     CMP    C
  1991.     JZ    START2$DISK    ;do not try if at default user area
  1992.     STA    FILEUA        ;where the file is if anywhere
  1993.     MOV    E,A
  1994.     MOV    A,C
  1995.     STA    CUR$USER    ;where we are (save for later)
  1996.     MVI    C,USER        ;set user code to default$user
  1997.     CALL    BDOS
  1998. START2$DISK:
  1999.     MVI    C,CURDRV    ;see if current disk is default drive
  2000.     CALL    BDOS
  2001.     MOV    C,A
  2002.     LDA    DFLT$DISK    ;check if at default disk
  2003.     CMP    C
  2004.     POP    H        ;fcb into hl
  2005.     PUSH    H        ;preserve stack
  2006.     JZ    START3$DISK
  2007.     INR    A        ;add one to disk number
  2008.     MOV    M,A        ;put into fcb
  2009. START3$DISK:
  2010.     XCHG            ;fcb into de
  2011.     MVI    C,OPEN        ;open file
  2012.     CALL    BDOS
  2013.     CPI    255        ;not present?
  2014. M012:    POP    D        ;get the fcb again(and clean up stack)
  2015.     PUSH    PSW        ;save open status on file
  2016.     LXI    H,36
  2017.     DAD    D
  2018.     LDA    FILEUA        ;get the user area for the file
  2019.     MOV    M,A        ;put user area into fcb
  2020.     POP    PSW
  2021.     RET
  2022. ;
  2023. RST$SYSTEM:
  2024.     PUSH    PSW
  2025.     LDA    CUR$USER    ;check user
  2026.     CPI    0FFH        ;0ffh=no change
  2027.     JZ    RST$RET
  2028.     MOV    E,A        ;user in e
  2029.     MVI    C,USER        ;get/set user code
  2030.     CALL    BDOS
  2031. RST$RET:
  2032.     POP    PSW
  2033.     RET
  2034. ;
  2035. FILEUA:    DS    1
  2036. ;
  2037. M011:    CALL    OPENF
  2038.     JNZ    M003
  2039.     CALL    ERXIT
  2040.     DB    CR,LF
  2041.     DB    'NO CLLR FILE$'
  2042. ;
  2043. M003:    MVI    C,SETRRD    ;get random record #
  2044.     LXI    D,FCBCLLR
  2045.     CALL    BDOS
  2046.     CALL    RST$SYSTEM
  2047.     MVI    A,LOGUSR
  2048.     STA    DFLT$USER
  2049.     JMP    M022
  2050. ;
  2051. FCBLOG:    DB    0,'LOG     SYS',0
  2052.     DS    23
  2053.     DB    0FFH
  2054. ;
  2055. LOGADR:    DW    LOGBUF
  2056. LOGSIZ:    EQU    BSIZE
  2057. LOGLEN:    DW    BSIZE
  2058. LOGPTR:    DS    2
  2059. ;
  2060. M022:    JMP    M013
  2061. ;
  2062. GETLOG:    LHLD    LOGLEN
  2063.     XCHG
  2064.     LHLD    LOGPTR
  2065.     MOV    A,L
  2066.     SUB    E
  2067.     MOV    A,H
  2068.     SBB    D
  2069.     JC    M019
  2070.     LXI    H,0
  2071.     SHLD    LOGPTR
  2072. M016:    XCHG
  2073.     LHLD    LOGLEN
  2074.     MOV    A,E
  2075.     SUB    L
  2076.     MOV    A,D
  2077.     SBB    H
  2078.     JNC    M018
  2079.     LHLD    LOGADR
  2080.     DAD    D
  2081.     XCHG
  2082.     MVI    C,SETDMA
  2083.     CALL    BDOS
  2084.     LDA    FCBLOG+36
  2085.     CPI    0FFH
  2086.     JZ    M021
  2087.     MVI    C,USER
  2088.     MOV    E,A
  2089.     CALL    BDOS
  2090. M021:    LXI    D,FCBLOG
  2091.     MVI    C,RRDM
  2092.     CALL    BDOS
  2093.     CALL    RST$SYSTEM
  2094.     ORA    A
  2095.     JNZ    M017
  2096.     LHLD    FCBLOG+33
  2097.     INX    H
  2098.     SHLD    FCBLOG+33
  2099.     LXI    D,SECT
  2100.     LHLD    LOGPTR
  2101.     DAD    D
  2102.     SHLD    LOGPTR
  2103.     JMP    M016
  2104. ;
  2105. M017:    LHLD    LOGPTR
  2106.     SHLD    LOGLEN
  2107. M018:    LXI    D,DEFDMA
  2108.     MVI    C,SETDMA
  2109.     CALL    BDOS
  2110.     LXI    H,0
  2111.     SHLD    LOGPTR
  2112. M019:    XCHG
  2113.     LHLD    LOGADR
  2114.     DAD    D
  2115.     XCHG
  2116.     LHLD    LOGLEN
  2117.     MOV    A,L
  2118.     ORA    H
  2119.     MVI    A,EOF
  2120.     RZ
  2121.     LDAX    D
  2122.     LHLD    LOGPTR
  2123.     INX    H
  2124.     SHLD    LOGPTR
  2125.     RET
  2126. ;
  2127. M013:    XRA    A
  2128.     STA    FCBLOG+12
  2129.     STA    FCBLOG+32
  2130.     LXI    H,LOGSIZ
  2131.     SHLD    LOGLEN
  2132.     SHLD    LOGPTR
  2133.     LXI    D,FCBLOG
  2134.     CALL    OPENF
  2135.     JNZ    M015
  2136.     MVI    A,EOF
  2137.     STA    LOGBUF
  2138.     LXI    H,0
  2139.     SHLD    LOGPTR
  2140.     LXI    D,FCBLOG
  2141.     MVI    C,MAKE
  2142.     CALL    BDOS
  2143.     INR    A
  2144.     JNZ    M015
  2145.     CALL    ERXIT
  2146.     DB    CR,LF
  2147.     DB    'NO DIR SPACE: LOG$'
  2148. ;
  2149. BACKLOG:LXI    H,LOGSIZ
  2150.     SHLD    LOGLEN
  2151.     LHLD    LOGPTR
  2152.     MOV    A,L
  2153.     ORA    H
  2154.     RZ
  2155.     DCX    H
  2156.     SHLD    LOGPTR
  2157. LLOG:    LHLD    FCBLOG+33
  2158.     MOV    A,L
  2159.     ORA    H
  2160.     RZ
  2161.     DCX    H
  2162.     SHLD    FCBLOG+33
  2163.     RET
  2164. ;
  2165. M015:    JMP    M023
  2166. ;
  2167. PUTLOG:    PUSH    PSW
  2168.     LHLD    LOGLEN
  2169.     XCHG
  2170.     LHLD    LOGPTR
  2171.     MOV    A,L
  2172.     SUB    E
  2173.     MOV    A,H
  2174.     SBB    D
  2175.     JC    M029
  2176.     LXI    H,0
  2177.     SHLD    LOGPTR
  2178. M026:    XCHG
  2179.     LHLD    LOGLEN
  2180.     MOV    A,E
  2181.     SUB    L
  2182.     MOV    A,D
  2183.     SBB    H
  2184.     JNC    M028
  2185.     LHLD    LOGADR
  2186.     DAD    D
  2187.     XCHG
  2188.     MVI    C,SETDMA
  2189.     CALL    BDOS
  2190.     LDA    FCBLOG+36
  2191.     CPI    0FFH
  2192.     JZ    M031
  2193.     MVI    C,USER
  2194.     MOV    E,A
  2195.     CALL    BDOS
  2196. M031:    LXI    D,FCBLOG
  2197.     MVI    C,WRDM
  2198.     CALL    BDOS
  2199.     CALL    RST$SYSTEM
  2200.     ORA    A
  2201.     JNZ    M027
  2202.     LHLD    FCBLOG+33
  2203.     INX    H
  2204.     SHLD    FCBLOG+33
  2205.     LXI    D,SECT
  2206.     LHLD    LOGPTR
  2207.     DAD    D
  2208.     SHLD    LOGPTR
  2209.     JMP    M026
  2210. ;
  2211. M027:    CALL    ERXIT
  2212.     DB    CR,LF
  2213.     DB    'DISK FULL: LOG$'
  2214. ;
  2215. M028:    LXI    D,DEFDMA
  2216.     MVI    C,SETDMA
  2217.     CALL    BDOS
  2218.     LXI    H,0
  2219.     SHLD    LOGPTR
  2220. M029:    XCHG
  2221.     LHLD    LOGADR
  2222.     DAD    D
  2223.     XCHG
  2224.     POP    PSW
  2225.     STAX    D
  2226.     LHLD    LOGPTR
  2227.     INX    H
  2228.     SHLD    LOGPTR
  2229.     RET
  2230. ;
  2231. M023:    MVI    C,CFSIZE    ;get file length
  2232.     LXI    D,FCBLOG
  2233.     CALL    BDOS
  2234.     CALL    LLOG
  2235. M030:    CALL    GETLOG
  2236.     CPI    EOF
  2237.     JNZ    M030
  2238.     CALL    BACKLOG
  2239.     CALL    RST$SYSTEM
  2240.     POP    PSW        ;get option back
  2241.     CALL    PUTLOG
  2242.     CALL    SPEED        ;get speed factor
  2243.     ADI    30H
  2244.     CALL    PUTLOG
  2245.     LDA    PGSIZE        ;now the program size in minuntes..
  2246.     CALL    PNDEC        ;..of transfer time
  2247.     MVI    A,' '           ;blank
  2248.     CALL    PUTLOG
  2249. ;
  2250. ; log the drive and user area as a prompt
  2251. ;
  2252.     LDA    FCB
  2253.     ORA    A
  2254.     JNZ    WDRV
  2255.     MVI    C,CURDRV
  2256.     CALL    BDOS
  2257.     INR    A
  2258. WDRV:    ADI    'A'-1
  2259.     CALL    PUTLOG
  2260.     MVI    C,USER        ;now the user area (as decimal number)
  2261.     MVI    E,0FFH
  2262.     CALL    BDOS
  2263.     CALL    PNDEC
  2264.     MVI    A,'>'           ;make it look like a prompt
  2265.     CALL    PUTLOG
  2266.     LDA    OPTSAV
  2267.     CPI    'L'
  2268.     JNZ    WDRV1
  2269.     LXI    H,MEMFCB    ;name of file in lib
  2270.     MVI    B,11
  2271.     CALL    PUTSTR
  2272.     MVI    A,' '
  2273.     CALL    PUTLOG
  2274. WDRV1:    LXI    H,FCB+1     ;now the name of the file
  2275.     MVI    B,11
  2276.     CALL    PUTSTR
  2277.     LDA    OPTSAV
  2278.     CPI    'L'
  2279.     JNZ    WDRV2
  2280.     MVI    C,1
  2281.     JMP    SPLOOP
  2282. ;
  2283. WDRV2:    MVI    C,13
  2284. SPLOOP:    PUSH    B
  2285.     MVI    A,' '
  2286.     CALL    PUTLOG
  2287.     POP    B
  2288.     DCR    C
  2289.     JNZ    SPLOOP
  2290. CLOOP:    CALL    GETCLLR    ;and the caller
  2291.     CPI    EOF
  2292.     JZ    QUIT
  2293.     CPI    CR        ;do not print 2nd line of 'lastcalr'
  2294.     JNZ    CLOP1
  2295.     CALL    PUTLOG
  2296.     MVI    A,LF
  2297.     CALL    PUTLOG        ;and add a lf
  2298.     JMP    QUIT
  2299. ;
  2300. CLOP1:    CPI    ','             ;do not print the ',' between names
  2301.     JNZ    CLOP2
  2302.     MVI    A,' '           ;instead send a ' '
  2303. CLOP2:    CALL    PUTLOG
  2304.     JMP    CLOOP
  2305. ;
  2306. PNDEC:    CPI    10        ;two column decimal format routine
  2307.     JC    ONE        ;one or two digits to area number?
  2308.     JMP    TWO
  2309. ;
  2310. ONE:    PUSH    PSW
  2311.     MVI    A,'0'
  2312.     CALL    PUTLOG
  2313.     POP    PSW
  2314. TWO:    MVI    H,0
  2315.     MOV    L,A
  2316. DECOT:    PUSH    B
  2317.     PUSH    D
  2318.     PUSH    H
  2319.     LXI    B,-10
  2320.     LXI    D,-1
  2321. DECOT2:    DAD    B
  2322.     INX    D
  2323.     JC    DECOT2
  2324.     LXI    B,10
  2325.     DAD    B
  2326.     XCHG
  2327.     MOV    A,H
  2328.     ORA    L
  2329.     CNZ    DECOT
  2330.     MOV    A,E
  2331.     ADI    '0'
  2332.     CALL    PUTLOG
  2333.     POP    H
  2334.     POP    D
  2335.     POP    B
  2336.     RET
  2337. ;
  2338. PUTSTR:    MOV    A,M
  2339.     PUSH    H
  2340.     PUSH    B
  2341.     CALL    PUTLOG
  2342.     POP    B
  2343.     POP    H
  2344.     INX    H
  2345.     DCR    B
  2346.     JNZ    PUTSTR
  2347.     RET
  2348. ;
  2349. QUIT:
  2350. M033:    LHLD    LOGPTR
  2351.     MOV    A,L
  2352.     ANI    (SECT-1) AND 0FFH
  2353.     JNZ    M034
  2354.     SHLD    LOGLEN
  2355. M034:    MVI    A,EOF
  2356.     PUSH    PSW
  2357.     CALL    PUTLOG
  2358.     POP    PSW
  2359.     JNZ    M033
  2360.     LDA    FCBLOG+36
  2361.     CPI    0FFH
  2362.     JZ    M037
  2363.     MVI    C,USER
  2364.     MOV    E,A
  2365.     CALL    BDOS
  2366. ;
  2367. M037:    LXI    D,FCBLOG
  2368.     MVI    C,CLOSE
  2369.     CALL    BDOS
  2370.     CALL    RST$SYSTEM
  2371.     INR    A
  2372.     JNZ    EXIT
  2373.     CALL    ERXIT
  2374.     DB    CR,LF
  2375.     DB    'CANNOT CLOSE LOG$'
  2376.      ENDIF            ;LGCL
  2377. ;
  2378. ;            end of LGCL routine
  2379. ;***********************************************************************
  2380. ;
  2381. ;            CRC SUBROUTINES
  2382. ;
  2383. ;***********************************************************************
  2384. ;
  2385. CLRCRC:    PUSH    H        ;reset 'CRC' store for a new message
  2386.     LXI    H,0
  2387.     SHLD    CRCVAL
  2388.     POP    H
  2389.     RET
  2390. ;
  2391. UPDCRC:    PUSH    PSW        ;update 'CRC' store  with byte in 'A'
  2392.     PUSH    B
  2393.     PUSH    H
  2394.     MVI    B,8
  2395.     MOV    C,A
  2396.     LHLD    CRCVAL
  2397. UPDLOOP:MOV    A,C
  2398.     RLC
  2399.     MOV    C,A
  2400.     MOV    A,L
  2401.     RAL
  2402.     MOV    L,A
  2403.     MOV    A,H
  2404.     RAL
  2405.     MOV    H,A
  2406.     JNC    SKIPIT
  2407.     MOV    A,H        ;the generator is x^16 + x^12 + x^5 + 1
  2408.     XRI    10H
  2409.     MOV    H,A
  2410.     MOV    A,L
  2411.     XRI    21H
  2412.     MOV    L,A
  2413. SKIPIT:    DCR    B
  2414.     JNZ    UPDLOOP
  2415.     SHLD    CRCVAL
  2416.     POP    H
  2417.     POP    B
  2418.     POP    PSW
  2419.     RET
  2420. ;
  2421. FINCRC:    PUSH    PSW        ;finish 'CRC' calculation for final xmsn
  2422.     XRA    A
  2423.     CALL    UPDCRC
  2424.     CALL    UPDCRC
  2425.     PUSH    H
  2426.     LHLD    CRCVAL
  2427.     MOV    D,H
  2428.     MOV    E,L
  2429.     POP    H
  2430.     POP    PSW
  2431.     RET
  2432. ;
  2433. CHKCRC:    PUSH    H        ;check 'CRC' bytes of received message
  2434.     LHLD    CRCVAL
  2435.     MOV    A,H
  2436.     ORA    L
  2437.     POP    H
  2438.     RZ
  2439.     MVI    A,0FFH
  2440.     RET
  2441. ;
  2442.     dseg
  2443. ;
  2444. ;***********************************************************************
  2445. ;
  2446. ; Temporary storage area
  2447. ;
  2448. aclvl:    db    0
  2449. MEMFCB:    DB    '                '  ;library name (16 bytes required)
  2450. CONONL:    DB    0        ;ctype console-only flag
  2451. CRCFLG:    DB    0        ;sets to 'C' if checksum requested
  2452. CRCVAL:    DB    0,0        ;current crc value
  2453. DIRSZ:    DB    0,0        ;directory size
  2454. DUD:    DB    0        ;specified disk
  2455. DUSAVE:    DB    0,0,0,0     ;buffer for drive/user
  2456. DUU:    DB    0        ;specified user
  2457. ERRCDE:    DB    0        ;receive error code
  2458. ERRCT:    DB    0        ;error count
  2459. FRSTIM:    DB    0        ;turned on after first 'SOH' received
  2460. INDEX:    DB    0,0        ;index into directory
  2461. MAXEXT:    DB    0        ;highest ext. # seen in file size calc.
  2462. RCNT:    DB    0,0        ;record count
  2463. RCVRNO:    DB    0        ;record number received
  2464. RECDNO:    DB    0,0        ;current record number
  2465. OLDDRV:    DB    0        ;save the original drive number
  2466. OLDUSR:    DB    0        ;save the original user number
  2467. OPTSAV:    DB    0        ;save option here for carrier loss
  2468. PRVTFL:    DB    0        ;private user area option flag
  2469. SAVEHL:    DB    0,0        ;saves defdma command line address
  2470. XDRV:    DB    DRV
  2471. XPRDRV:    DB    PRDRV
  2472. XUSR:    DB    USR
  2473. XPRUSR:    DB    PRUSR
  2474. ;
  2475. ; Following 3 used by disk buffering routines
  2476. ;
  2477. EOFLG:    DB    0        ;'EOF' flag (1=yes)
  2478. RECPTR:    DW    DBUF
  2479. RECNBF:    DW    0        ;number of records in the buffer
  2480.     DS    60        ;stack area
  2481. STACK:    DS    2        ;save original stack address
  2482. ;
  2483. ; 16 record disk buffer
  2484. ;
  2485. DBUF:    DS    0        ;16 record disk buffer
  2486. LOGBUF:    EQU    DBUF+128    ;for use with LGCL
  2487. ;
  2488. ; BDOS equates
  2489. ;
  2490. WRCON:    EQU    2
  2491. PRINTF:    EQU    9
  2492. VERNO:    EQU    12        ;get CP/M version number
  2493. SELDRV:    EQU    14        ;select drive
  2494. OPEN:    EQU    15        ;0ffh = not found
  2495. CLOSE:    EQU    16        ;    "    "
  2496. SRCHF:    EQU    17        ;    "    "
  2497. SRCHN:    EQU    18        ;    "    "
  2498. ERASEF:    EQU    19        ;no ret code
  2499. READ:    EQU    20        ;0=ok, 1=eof
  2500. WRITE:    EQU    21        ;0=ok, 1=err, 2=?, 0ffh=no dir spc
  2501. MAKE:    EQU    22        ;0ffh=bad
  2502. CURDRV:    EQU    25        ;get current drive
  2503. SETDMA:    EQU    26        ;set dma
  2504. USER:    EQU    32        ;set user area to receive file
  2505. RRDM:    EQU    33        ;read random
  2506. WRDM:    EQU    34        ;write random
  2507. CFSIZE:    EQU    35        ;compute file size
  2508. SETRRD:    EQU    36        ;set random record
  2509. BDOS:    EQU    BASE+05H
  2510. DEFDMA:    EQU    BASE+80H    ;default dma address
  2511. FCB:    EQU    BASE+5CH    ;system fcb
  2512. FCB1:    EQU    BASE+6CH    ;second fcb
  2513. FCBEXT:    EQU    FCB+12        ;file extent
  2514. FCBRNO:    EQU    FCB+32        ;record number
  2515. RANDOM:    EQU    FCB+33        ;random record field
  2516. ;
  2517.     END
  2518. he buffer
  2519.     DS    60        ;stack area
  2520. STACK:    DS    2        ;save original stack address
  2521. ;
  2522. ; 16 record disk buffer
  2523. ;
  2524. DBUF:    DS    0        ;16 re