home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / lambda / soundpot / p / xmdm91kp.lbr / XMODEM91.AZM / XMODEM91.ASM
Encoding:
Assembly Source File  |  1993-10-25  |  75.0 KB  |  3,232 lines

  1.  
  2. ; 03/24/84 XMODEM91.ASM - REMOTE CP/M FILE TRANSFER PROGRAM
  3. ;
  4. ; Originally written by Keith Petersen W8SDZ, this program allows a re-
  5. ; mote user to transfer files (to or from) a RCPM facility.  Files may
  6. ; be loaded to a specific user area, keeping all new programs in that
  7. ; one area.  This aids the SYSOP as well as the remote user.  If upload-
  8. ; ing a .COM file, an option changes it automatically to an .OBJ file.
  9. ; This security feature prevents intentional uploading of programs that
  10. ; could be used to alter (or erase, etc.) those already present.
  11. ;
  12. ; Many people have contributed greatly to the present program.
  13. ;
  14. ; SYSOPs may use MAXTIM to set the time limit for downloading files.  It
  15. ; prevents users from tying up the system for excessive lengths of time
  16. ; with slow modems.  For instance, 30 minutes limits 300 baud users to
  17. ; about 48k.  MAXTIM does not kick them off, it stops them from starting
  18. ; to download a lengthy file.  (A user could still download a number of
  19. ; files (each less than the limit) which would take several hours total,
  20. ; but it is an excellent start.)
  21. ;
  22. ; NOTE:     Setting the SHOWHEX option to NO will give slightly faster file
  23. ;     transfers - will save time converting and displaying the hex
  24. ;     count, which is superfluous since the decimal count is already
  25. ;     shown.
  26. ;                    - Notes by Irv Hoff W6FFC
  27. ;
  28. ; *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *
  29. ;
  30. ; 03/24/84  Added a bit of code to allow counting the number of uploads
  31. ;        or downloads since a user logged on. Set LOGLDS to YES to
  32. ;        enable this. You must modify your BBS program to clear the
  33. ;        locations UPLDS and DNLDS (54h and 55h) when a user logs in
  34. ;        and check these locations when he logs out to determine how
  35. ;        many up/down loads took place. I modified my BBS program to
  36. ;        make this check on log in and update the last users file
  37. ;        (the last user is still in LASTCALR.DAT), then clear locations
  38. ;        UPLDS and DNLDS. This works fine. Its not the most elegant
  39. ;        solution but a start. (The number of uploads on my RCPM
  40. ;        increased dramaticly since people see a message like:
  41. ;        "No of Downloads: 109, No of Uploads: 1" ....
  42. ;                     - Alex Soya, Melbourne RCPM-
  43. ;
  44. ; 02/26/84  Added code to check for zero length uploads and delete them.
  45. ;   v9.0    The problem occurred with ZPRO, a non-public modem program
  46. ;        that times out before xmodem and sent EOT causing xmodem to
  47. ;        save zero-length filename.    Changed console messages so that
  48. ;        it says "Sent #" on downloads and "Received #" on uploads.
  49. ;                    -wayne masters-
  50. ;
  51. ; 02/22/84  Combined versions 8.7 and 8.8 so Sigi Kluger's mod is again
  52. ;   v8.9    included.  Note added that MAXMIN is limited to 99 minutes.
  53. ;        Several modest changes requested by Wayne Masters for his
  54. ;        time routine.        - Irv Hoff
  55. ;
  56. ; 02/20/84  Added TIMEON equate and TIME routine to calculate the time a
  57. ;   v8.8    user has been on and if STATUS is non-zero that delta time
  58. ;        is added to the file transfer time.     This sum is then com-
  59. ;        pared to MAXMIN to see if user will exceed his total time
  60. ;        limit.  If he does, the transfer is aborted and the user is
  61. ;        informed BEFORE the transfer is started.  TIME is called at
  62. ;        entry and exit and the message "Time on system is xx minutes"
  63. ;        is printed.     If xx is => MAXTIME the user is told not to
  64. ;        call back for 24 hours automatically logged off.  If STATUS
  65. ;        is non-zero, the "Time on system" message is displayed but
  66. ;        the delta is not added to the transfer time nor is he logged
  67. ;        off after MAXMIN is exceeded.
  68. ;
  69. ;        The SYSOP must add code to the beginning of TIME which will
  70. ;        read his realtime clock and store BINARY values of the cur-
  71. ;        rent hour (0-23) and the current minute (0-59) in locations
  72. ;        pointed at by CHOUR and CMIN.  The SYSOP must also code his
  73. ;        BBS entry program (or BYE) to store the BINARY hour and min-
  74. ;        utes of the user's logon time in LHOUR and LMIN.  The BBS
  75. ;        (or BYE) software should also determine if the user has
  76. ;        special privledges (such as SYSOPs, frequent uploaders etc.)
  77. ;        and store FF (or non-zero) in the byte pointed at by STATUS.
  78. ;        Store a 0 in STATUS for normal users.
  79. ;
  80. ;        If TIMEON and LOGCAL are both set YES, then SAVE 22 instead
  81. ;        of 21 with DDT.  If TIMEON is YES, MAXMIN should be set to
  82. ;        60.     TIMEON uses 5 bytes in page one memory not used by CP/M
  83. ;        (similiar to WHEEL and MAXDRIV).  They are:
  84. ;
  85. ;        CHOUR    EQU    043H    ;for basic
  86. ;        CMIN    EQU    044H    ;programmers the decimal to poke
  87. ;        LHOUR    EQU    050H    ;is 80
  88. ;        LMIN    EQU    051H    ;81
  89. ;        STATUS    EQU    053H    ;and 83
  90. ;
  91. ;        Be sure to store binary (not ASCII) in these locations.
  92. ;                    - Wayne Masters
  93. ;
  94. ; 02/05/84  If using RP or RPC with a filename shorter than the option
  95. ;   v8.7    (RPC X for example), part of the option stayed in the file-
  96. ;        name (above saved as "XPC").  Fixed now.
  97. ;                    - Sigi Kluger
  98. ;    
  99. ; 01/20/84  Combined XMODM84A with XMODEM85.  Time limit set by SYSOP
  100. ;   v8.6    now displayed when a file exceeding the limit is requested.
  101. ;                    - Irv Hoff
  102. ;
  103. ; 01/04/84  Added buffer size equate.    - Sigi Kluger
  104. ;   v8.5
  105. ;
  106. ; 01/04/84  Put back the MAXTIM routines to limit the transfer time of
  107. ;   V8.4a   files in minutes.  This keeps a 300 bps caller from tying up
  108. ;        your system with a 2 hour transfer of a LBR file.  WHY DO
  109. ;        PEOPLE KEEP TAKING THIS ROUTINE OUT...  Also renamed LOGCAL
  110. ;        file to XMODEM.LOG, it makes more sense and also cannot be
  111. ;        typed due to the .LOG extent.
  112. ;                    - Steve Sanders
  113. ;
  114. ; 03/17/83  SYSOPS can now designate a special user area for downloading
  115. ;   v7.2    private files.  (See SPDRV, SPUSR below).  This can make any
  116. ;        user a temporary privileged person.     Nobody but the SYSOP
  117. ;        and the person to whom he left a private note would know the
  118. ;        name of the file, so a high degree of security is possible.
  119. ;        The menu also now shows the drive/area for uploading normal
  120. ;        programs.  This version standardizes receiving files.  "R"
  121. ;        or "PR" now receive via CRC and "RC" or "PRC" now receive
  122. ;        via Checksum.  This matches the "R" protocol used for some
  123. ;        time with the COMM7, MODEM7 and MDM7 programs.  When exiting
  124. ;        the program, it always reverts to the initial drive and user
  125. ;        area.  Several other changes to fix bugs reported by various
  126. ;        SYSOP's.                    - Irv Hoff
  127. ;
  128. ;
  129. ; *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *
  130. ;
  131. ; XMODEM allows programs to be uploaded to a non-public area for private
  132. ; use of the SYSOP.  (Use "RP" or "RPC" for the private area.)    It also
  133. ; allows private programs to be downloaded from a special area, giving
  134. ; the SYSOP the ability to make any person a temporary privileged user.
  135. ; (A private note tells the person the name of the file.  Others would
  136. ; be unaware such a file existed, insuring excellent security.)
  137. ;
  138. ; Individual files from a library group may be downloaded.  The library
  139. ; extent (.LBR) need not be included, in which case it is automatically
  140. ; added.  Using library groups permits greater utilization of the avail-
  141. ; able disk space, plus puts all associated files into the same program.
  142. ; An example is shown in the menu.
  143. ;
  144. ; Since there are so many different computer/modem combinations, you are
  145. ; expected to select one of the external overlays available to match the
  146. ; equipment being used (or make your own).  First, select the general
  147. ; options desired on this program and assemble it.  ASM.COM is suitable.
  148. ; Then use LOAD to get a .COM file.  Edit the appropriate external over-
  149. ; lay and assemble it to get a .HEX file.  DDT (or SID, etc.) would be
  150. ; used to merge the two into your final working .COM file.  (The infor-
  151. ; mation on how to do this is contained in the external patch file.  It
  152. ; is easier and quicker to do than it may appear.  Follow instructions
  153. ; included in each overlay.)
  154. ;                    - Notes by Irv Hoff
  155. ;
  156. ; *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *      *   *
  157. ;
  158. VERSION:EQU    9
  159. MODLEV: EQU    1
  160. ;
  161. NO:    EQU    0
  162. YES:    EQU    0FFH
  163. ;
  164. ;
  165. ; Define ASCII characters used
  166. ;
  167. ACK:    EQU    06H    ;acknowledge
  168. BELL:    EQU    7    ;bell
  169. CAN:    EQU    18H    ;CTL-X for cancel
  170. CR:    EQU    0DH    ;carriage return
  171. CRC:    EQU    'C'    ;CRC request character
  172. EOF:    EQU    1AH    ;^Z for end of file
  173. EOT:    EQU    04H    ;end of transmission
  174. LF:    EQU    0AH    ;linefeed
  175. NAK:    EQU    15H    ;negative acknowledge
  176. SOH:    EQU    01H    ;start of header
  177. ;.....
  178. ;
  179. ;
  180. ; Incidental equates
  181. ;
  182. MHZ:    EQU    4    ;clock speed, use integer (2,4,5,8, etc.)
  183. NOCOMR: EQU    YES    ;yes, change .COM to .OBJ on receive
  184. NOCOMS: EQU    YES    ;yes, .COM files not sent
  185. NOLBS:    EQU    YES    ;yes, .??# files not sent
  186. NOSYS:    EQU    YES    ;yes, no $SYS files sent or reported
  187. ;
  188. ;=======================================================================
  189. ;
  190. ; Type of CP/M - standard starting at 100H or alterate starting address.
  191. ;
  192. STDCPM: EQU    YES    ;yes, if standard "zero ORG" CP/M, no if not
  193. ;
  194. ;=======================================================================
  195. ;
  196. ; Allows drive/user area to be specified for downloading.  If using ZCPR
  197. ; set USEMAX 'YES'.  Then the answers to MAXDRV and MAXUSR are superflu-
  198. ; ous.
  199. ;
  200. USEMAX: EQU    YES    ;yes if using ZCPR for DRIVMAX & USRMAX values
  201.             ;no to use MAXDRV and MAXUSR specified next
  202. DRIVMAX:EQU    03DH    ;location of MAXDRIV byte
  203. USRMAX: EQU    03FH    ;location of MAXUSER byte
  204. ;
  205. ;
  206. ; If USEMAX above is YES for automatic ZCPR setting, the following two
  207. ; are not used. 
  208. ;
  209. MAXDRV: EQU    2    ;number of disk drives used
  210. MAXUSR: EQU    7    ;maximum 'SEND' user allowed    
  211. ;
  212. TIMEON: EQU    YES    ;If YES, add your clock reader code at the start
  213.             ;of label TIME: and store binary values in CHOUR
  214.             ; and CMIN
  215. ;
  216.      IF    TIMEON    ;use these bytes in low memory
  217. CHOUR:    EQU    043H    ;some BIOS clocks keep time
  218. CMIN:    EQU    044H    ;here anyway.
  219. LHOUR:    EQU    050H    ;set by BBS (or BYE) in binary
  220. LMIN:    EQU    051H    ;when user logs on
  221. STATUS: EQU    053H    ;and his status
  222.      ENDIF
  223. ;
  224. LOGLDS    EQU    YES    ;count no of Up/down loads since login.
  225.             ;your BBS program can check UPLDS and DNLDS
  226.             ;when user logs out and update either the users
  227.             ;file or a file for this purpose. You can either
  228.             ;modify your BBS entry program to check the Lastcalr
  229.             ;file before updating it and then update (risky),
  230.             ;or make a seperate program that BYE calls when
  231.             ;logging off a user (prefered)
  232. ;
  233.      IF LOGLDS
  234. UPLDS:    EQU    054H    ;Clear these values to Zero from your BBS program
  235. DNLDS:    EQU    055H    ;when somebody logs in. NOTE :Clear ONLY when a user
  236.      ENDIF        ;logs in. Not when he reenters the BBS program from
  237.             ;CPM....
  238. ;
  239. ;=======================================================================
  240. ;
  241. ; Some modems will either go onhook immediately after carrier loss or
  242. ; can be set to lower values.  A good value with the Smartmodem is five
  243. ; seconds, since it catches all "call forwarding" breaks.  Not all is
  244. ; lost after timeout in XMODEM; BYE will still wait some more, but the
  245. ; chance of someone slipping in is less now.
  246. ;
  247. TIMOUT: EQU    5    ;seconds to abort after carrier loss
  248. ;
  249. ;=======================================================================
  250. ;
  251. ; Length of external patch program.  If over 128 bytes, get/set size
  252. ;
  253. LARGEIO:EQU    NO    ;yes, if modem patch area over 128 bytes
  254. LARSIZE:EQU    0    ;if 'LARGEIO' set patch area size here
  255. ;
  256. ;=======================================================================
  257. ;
  258. ; Type of modem being used - an external patch file needed in any event.
  259. ;
  260. ALTOS:    EQU    NO    ;yes, if Altos computer
  261. EXTMOD: EQU    YES    ;yes, if external modem
  262. INTER3: EQU    NO    ;yes, if compupro Interfacer3/4 card
  263. ;
  264. ;=======================================================================
  265. ;
  266. ; Allows uploading to be done on a specified driver and user area so all
  267. ; viewers (indluding the SYSOP) can readily find the latest entries.
  268. ;
  269. SETAREA:EQU    YES    ;yes, if using designated area to receive files
  270. DRV:    EQU    'B'    ;drive to receive file on
  271. USR:    EQU    0    ;user area to receive file in
  272. ;
  273. ;=======================================================================
  274. ;
  275. ; Selects the drive/user area for uploading private files for the SYSOP.
  276. ; This permits experimental files, replacement files and proprietary
  277. ; programs to be sent to the sysop.
  278. ;
  279. PRDRV:    EQU    'B'    ;private drive for SYSOP to receive file
  280. PRUSR:    EQU    15    ;private user area for SYSOP to receive file
  281. ;
  282. ;=======================================================================
  283. ;
  284. ; Selects the drive/user area for downloading private files for SYSOP
  285. ; use.    This permits him to put a special file in this area, then leave
  286. ; a private note to that person mentioning the name of the file and its
  287. ; location.  Although anybody could download that program, they don't
  288. ; know what (if any) files are there.  A high degree of security exists,
  289. ; while the sysop still has the ability to make special files available.
  290. ; Thus any person can be a temporary "privileged user".
  291. ;
  292. SPLDRV: EQU    'B'    ;special drive area for downloading SYSOP files
  293. SPLUSR: EQU    15    ;special user area for downloading SYSOP files    
  294. ;
  295. ;=======================================================================
  296. ;
  297. ; File transfer logging options
  298. ;
  299. LOGCAL: EQU    YES    ;yes, logs XMODEM transfers
  300. LOGUSR: EQU    14    ;user area to put 'XMODEM.LOG' file
  301. LOGDRV: EQU    'A'    ;drive to place 'XMODEM.LOG' file
  302. LASTUSR:EQU    14    ;user area of 'LASTCALR' file, if 'LOGCAL' yes
  303. OLDRBBS:EQU    NO    ;yes, look for 'LASTCALR' file - no, look for
  304.             ;'LASTCALR.DAT' file
  305. ;
  306. ;=======================================================================
  307. ;
  308. ; The receiving station sends an 'ACK' for each valid sector received.
  309. ; It sends a 'NAK' for each sector incorrectly received.  In poor con-
  310. ; ditions either may be garbled.  Waiting for a valid 'NAK' can slow
  311. ; things down somewhat, giving more time for the interference to quit.
  312. ;
  313. ACKNAK: EQU    YES    ;yes resends a record after any non-ACK
  314.             ;no requires a valid NAK to resend a record
  315. ;
  316. ;=======================================================================
  317. ;
  318. ; Normal disk systems can transfer 16k from computer to disk in 2-3-4
  319. ; seconds and less.  Some very slow 5-1/4" floppy systems (such as North
  320. ; Star) may take up to 20-30 seconds to transfer 16k.  This would cause
  321. ; several timeout at 10 seconds each.  If you experience any such time
  322. ; out, change the BUFSIZ to somethng smaller, perhaps 4k or even 2k.
  323. ; (16k is the same buffer length used in MDM7 so both systems would be
  324. ; transferring from the buffer simultaneously, minimizing any delays.)
  325. ;
  326. BUFSIZ: EQU    16    ;file transfer buffer size in Kbytes
  327. ;            
  328. ;
  329. ;=======================================================================
  330. ;
  331. ; Maximum transfer time allowed.  I have found 30 minutes to be a good
  332. ; value.  It limits callers at 300 bps to a 48K file and 1200 bps users
  333. ; can still transfer up to a 190K file.
  334. ;
  335. ;            TIME       300 BPS     1200 BPS
  336. ;           30 min    48.7k       180k
  337. ;           45 min    73.1k       270k        
  338. ;           60 min    97.5k       360k
  339. ;
  340. MAXTIM: EQU    YES    ;yes if limiting transmission time
  341. ;
  342. MAXMIN: EQU    60    ;minutes for maximum file transfer time
  343.             ;this should be set to 60 if TIMEON is YES
  344.             ;(99 minutes maximum.)
  345. ;
  346. ;=======================================================================
  347. ;
  348. ; Slightly faster file transfer times will occur if SHOWHEX is set NO as
  349. ; it does take time to caclulate, send and display the additonal count
  350. ; after each record in the file.  The decimal count is already shown..
  351. ;
  352. SHOWHEX:EQU    NO    ;yes, shows both decimal and hex record count
  353. ;            ;no, shows only decimal record count
  354. ;            ;(some users consider both to be superfluous,
  355. ;            ;redundant, time-consuming and even confusing.)
  356. ;
  357. ;=======================================================================
  358. ;
  359. ; XMODEM is used with BYE to transfer files.  It is nice to see the re-
  360. ; cord count displayed, but this creates a problem as those characters
  361. ; usually go out the modem as well.  This would foul up the file tran-
  362. ; fer.    The BYE program stores the original CONOUT vector which can be
  363. ; used for local display only.    If the the following USECON equate is
  364. ; set YES, this address can be automatically located and placed in the
  365. ; XMODEM CONOUT location.  ByeII programs support this feature.     Also
  366. ; BYE3-17 and higher.  If using some other program you have two options
  367. ; remaining, both apply to the external XMODEM overlay files.  (Be sure
  368. ; to set USECON to NO in that case.)
  369. ;
  370. ;       1)  Leave CONOUT in the overlay set to 00000H.
  371. ;        This allows normal operation but without
  372. ;        local visual update of the record count.
  373. ;       2)  Manually find the address to set CONOUT in
  374. ;        in the overlay.     This procedure is listed
  375. ;        in each overlay, if needed.
  376. ;
  377. USECON: EQU    YES    ;yes to get the original CONOUT address from
  378.             ;ByeII or BYE3 (v1.7 and up).  'No' to get the
  379.             ;CONOUT address the value set in the XMODEM
  380.             ;overlay.
  381. ;
  382. ;=======================================================================
  383. ;
  384.     IF    STDCPM
  385. BASE:    EQU    0000H        ;CP/M base address
  386.     ENDIF
  387. ;
  388.     IF    NOT STDCPM
  389. BASE:    EQU    4200H        ;alternate CP/M base address
  390.     ENDIF
  391. ;
  392. ;
  393. ;-----------------------------------------------------------------------
  394. ;
  395. ;            PROGRAM STARTS HERE
  396. ;
  397. ;-----------------------------------------------------------------------
  398. ;
  399. ;
  400.     ORG    BASE+100H
  401. ;
  402.     JMP    BEGIN
  403. ;
  404. ;
  405. ;-----------------------------------------------------------------------
  406. ;
  407. ; This is the I/O patch area.  Assemble the appropriate I/O patch file
  408. ; for your modem, then integrate it into this program via DDT (or SID).
  409. ;
  410. ; Initially, all jumps are to zero, which will cause an unpatched
  411. ; XMODEM to simply execute a warm boot.     All routines must end with RET.
  412. ;
  413. CONOUT: JMP    0        ;see 'CONOUT' discussion above
  414. MINIT:    JMP    0        ;initialization routine (if needed)
  415. UNINIT: JMP    0        ;undo whatever MINIT did (or return)
  416. SENDR:    JMP    0        ;send character (via POP PSW)
  417. CAROK:    JMP    0        ;test for carrier
  418. MDIN:    JMP    0        ;receive data byte
  419. GETCHR: JMP    0        ;get character from modem
  420. RCVRDY: JMP    0        ;check receive ready
  421. SNDRDY: JMP    0        ;check send ready (A-ERRCDE)
  422. SPEED:    JMP    0        ;get speed value for transfer time
  423. EXTRA1: JMP    0        ;extra for custom routine
  424. EXTRA2: JMP    0        ;extra for custom routine
  425. EXTRA3: JMP    0        ;extra for custom routine
  426. ;.....
  427. ;
  428. ;
  429. ;-----------------------------------------------------------------------
  430. ;
  431.      IF    NOT LARGEIO    ;I/O patch area size up to 128 bytes
  432.     ORG    BASE+100H+80H    ;origin plus 128 bytes for patches
  433.      ENDIF
  434.  
  435.      IF    LARGEIO        ;I/O patch area size if over 128 bytes
  436.     ORG    BASE+100H+LARSIZE
  437.      ENDIF
  438. ;.....
  439. ;
  440. ;
  441. ; Save CP/M stack, initialize new one for this program
  442. ;
  443. BEGIN:    LXI    H,0
  444.     DAD    SP
  445.     SHLD    STACK
  446.     LXI    SP,STACK    ;initialize new stack
  447. ;
  448. ;
  449. ; Save the current drive and user area
  450. ;
  451.     MVI    E,0FFH        ;get the current user area
  452.     MVI    C,USER
  453.     CALL    BDOS
  454.     STA    OLDUSR        ;save user number here
  455.     MVI    C,CURDRV    ;get the current drive
  456.     CALL    BDOS
  457.     STA    OLDDRV        ;save drive here
  458. ;
  459.      IF    TIMEON
  460.     CALL    TIME        ;get user's time status
  461.      ENDIF            ;TIMEON
  462. ;
  463.     CALL    ILPRT        ;print:
  464.     DB    CR,LF,'XMODEM v',VERSION+'0','.',MODLEV+'0',' ',0
  465. ;
  466. ;
  467. ; Stuff address of BIOS CONOUT vector in our routine as default.
  468. ;
  469.      IF    USECON
  470.     LHLD    1        ;point to warm boot for normal bios
  471.     LXI    D,9
  472.     DAD    D        ;calc addr of normal BIOS conout vector
  473.     SHLD    CONOUT+1    ;save in case no BYE program is active
  474. ;
  475. ;
  476. ; Go through a big search to see if BYE is active.  If so, need to re-
  477. ; calculate the address of the original BIOS vector.
  478. ;
  479.     LHLD    1        ;point to warm boot again
  480.     DCX    H        ;if BYE active,
  481.     MOV    D,M        ;  pick up pointer to BYE variables
  482.     DCX    H
  483.     MOV    E,M
  484.     LXI    H,15        ;calculate address of BYE variable
  485.     DAD    D        ;  where ptr to orig BIOS vector stored
  486.     MOV    E,M        ;load that address
  487.     INX    H        ;  into DE.  If BIOS active, DE now pnts
  488.     MOV    D,M        ;  to original BIOS cold boot vector.
  489.     INX    H        ;point to BYE signon message
  490. ;
  491. ;
  492. ; Note that if more BYE variables are added after the cold boot pointer,
  493. ; extra INX may be needed.  Fix to match your BYE.
  494. ;
  495.     MOV    A,M        ;get letter
  496.     CPI    'B'        ;try to match 'BYE' (or 'Bye')
  497.     JNZ    NOBYE        ;out if BYE not active
  498.     INX    H
  499.     MOV    A,M
  500.     ANI    05FH        ;convert to upper case if needed
  501.     CPI    'Y'
  502.     JNZ    NOBYE
  503.     INX    H
  504.     MOV    A,M
  505.     ANI    05FH        ;convert to upper case if needed
  506.     CPI    'E'
  507.     JNZ    NOBYE
  508.     XCHG            ;point to the console output routine
  509.     SHLD    CONOUT+1    ;save vector address supplied by BYE
  510. ;
  511. NOBYE:     ENDIF            ;USECON
  512. ;
  513. ;
  514. ; Get option
  515. ;
  516.     LXI    H,FCB+2        ;first off, check for 'P' (private)
  517.     MOV    A,M
  518.     CPI    'P'        ;if not, then normal stuff...
  519.     JNZ    CHKOPT
  520.     DCX    H        ;first character in buffer
  521.     MOV    A,M
  522.     CPI    'R'
  523.     JNZ    OPTNERR        ;if not, is an error
  524.     STA    PRVTFL        ;otherwise set 'PRIVATE' flag
  525.     INX    H
  526.     INX    H
  527.     MOV    A,M
  528.     CPI    'C'        ;checksum checking requested?
  529.     JZ    CHKOPT1        ;if yes, go set flag
  530.     JMP    CHKOPT2
  531. ;
  532. CHKOPT: CPI    'C'        ;checksum checking requested?
  533.     JNZ    CHKOPT2        ;no, go check primary
  534.     LDA    FCB+1        ;get primary option
  535.     CPI    'R'        ;checksum only for receive
  536.     JNZ    OPTNERR        ;print error message then abort
  537. ;
  538. CHKOPT1:STA    CRCFLG        ;turn on the checksum flag
  539. ;
  540. CHKOPT2:LDA    FCB+1        ;get option (L, R or S)
  541.     STA    OPTSAV        ;save option for later use
  542.     PUSH    PSW
  543.     CPI    'R'
  544.     JNZ    CHKOPT4
  545.     LDA    CRCFLG
  546.     ORA    A
  547.     JZ    CHKOPT3
  548.     CALL    ILPRT
  549.     DB    'Checksum enabled',0
  550.     JMP    CHKOPT4
  551. ;
  552. CHKOPT3:CALL    ILPRT
  553.     DB    '(CRC is enabled)',0
  554. ;
  555. CHKOPT4:CALL    ILPRT
  556.     DB    CR,LF,0
  557. ;
  558. ;
  559. ; Gobble up garbage characters from the line prior to receive or send
  560. ; and initialize whatever has to be initialized
  561. ;
  562.     CALL    GETCHR
  563.     CALL    GETCHR
  564.     CALL    MINIT
  565. ;
  566. ;
  567. ; Jump to appropriate function
  568. ;
  569.     POP    PSW        ;get option
  570. ;
  571.      IF    LOGCAL
  572.     PUSH    PSW        ;but save it
  573.      ENDIF            ;LOGCAL
  574. ;
  575.     CPI    'L'        ;to send a file from a library?
  576.     JZ    SENDFIL
  577.     CPI    'R'        ;to receive a file?
  578.     JZ    RCVFIL
  579.     CPI    'S'
  580.     JZ    SENDFIL        ;otherwise go send a file
  581. ;
  582. ;
  583. ; Invalid option
  584. ;
  585. OPTNERR:CALL    ILPRT
  586.     DB    CR,LF,'++ Examples of valid options: ++',0
  587. ;
  588.      IF    NOT SETAREA
  589.     CALL    ILPRT
  590.     DB    CR,LF,0
  591.      ENDIF            ;NOT SETAREA
  592. ;
  593.      IF    SETAREA
  594.     CALL    ILPRT
  595.     DB    '   (Uploads files to ',DRV,0
  596.     LXI    H,USR
  597.     CALL    DECOUT
  598.     CALL    ILPRT
  599.     DB    ':)',CR,LF,0
  600.      ENDIF            ;SETAREA
  601. ;
  602.     CALL    ERXIT        ;exit with error
  603.     DB    '   XMODEM L PRINT.LBR PRINT.INF    to send a file '
  604.     DB    'from a library',CR,LF
  605.     DB    '   XMODEM L CATALOG CAT2.OBJ       (.LBR extent may '
  606.     DB    'be omitted)',CR,LF
  607.     DB    '   XMODEM S FILENAME.TYP           to send a file'
  608.     DB    CR,LF
  609.     DB    '   XMODEM S B6:HELLO.DOC           to send from a '
  610.     DB    'named drive/area',CR,LF
  611.     DB    '   XMODEM R (or RC) FILENAME.TYP   to receive a file'
  612.     DB    CR,LF
  613.     DB    '   XMODEM RP (or RPC) FILENAME.TYP to receive in a '
  614.     DB    'private area',CR,LF,CR,LF
  615.     DB    '   (The "C" in RC or RPC receives via checksum rather '
  616.     DB    'than CRC)'
  617.     DB    '$'
  618. ;.....
  619. ;
  620. ;    
  621. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  622. ;
  623. ; ---> SENDFIL    sends a CP/M file
  624. ;
  625. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  626. ;
  627. ; The CP/M file specified in the XMODEM command is transferred over the
  628. ; phone to another computer running modem with the "R" (receive) option.
  629. ; The data is sent one record at a time with headers and checksums, and
  630. ; retransmission on errors.
  631. ;
  632. SENDFIL:CALL    LOGDU        ;check file name or drive/user option
  633.     LDA    OPTSAV
  634.     CPI    'L'        ;if library option skip 'CNREC'
  635.     CNZ    CNREC        ;ignore if in library mode
  636.     CALL    OPENFIL        ;open the file
  637.     MVI    E,100        ;wait 100 sec for initial 'NAK'
  638.     CALL    WAITNAK
  639. ;
  640. SENDLP: CALL    RDRECD        ;read a record
  641.     JC    SENDEOF        ;send 'EOF' if done
  642.     CALL    INCRRNO        ;bump record number
  643.     XRA    A        ;initialize error count to zero
  644.     STA    ERRCT
  645. ;
  646. SENDRPT:CALL    SENDHDR        ;send a header
  647.     CALL    SENDREC        ;send data record
  648.     LDA    CRCFLG        ;get 'CRC' flag
  649.     ORA    A        ;'CRC' in effect?
  650.     CZ    SENDCRC        ;yes, send 'CRC'
  651.     CNZ    SENDCKS        ;no, send checksum
  652.     CALL    GETACK        ;get the 'ACK'
  653.     JC    SENDRPT        ;repeat if no 'ACK'
  654.     LDA    OPTSAV        ;get the command option again
  655.     CPI    'L'
  656.     JNZ    SNRPT1        ;if not library option, exit
  657.     LHLD    RCNT
  658.     MOV    A,H
  659.     ORA    L        ;see if L and H both zero now
  660.     JZ    SENDEOF        ;if finished, exit
  661.     DCX    H        ;if not both zero, more remaining
  662.     SHLD    RCNT        ;one less to go
  663. ;
  664. SNRPT1: JMP    SENDLP        ;loop until EOF
  665. ;.....
  666. ;
  667. ;
  668. ; File sent, send EOT's
  669. ;
  670. SENDEOF:MVI    A,EOT        ;send an 'EOT'
  671.     CALL    SEND
  672.     CALL    GETACK        ;get the ACK
  673.     JC    SENDEOF        ;loop if no ACK
  674. ;
  675.      IF     LOGLDS
  676.     LDA    DNLDS        ;get Down loads Counter
  677.     INR    A        ;one more download since log in
  678.     STA    DNLDS        ;and update counter
  679.      ENDIF            ;LOGLDS
  680. ;
  681.     JMP    EXITLG        ;all done
  682. ;.....
  683. ;
  684. ;
  685. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  686. ;
  687. ; ---> RCVFIL    Receive a CP/M file
  688. ;
  689. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  690. ;
  691. ; Receives a file in block format as sent by another person doing
  692. ; "XMODEM S FM.FT".  Can be invoked by "XMODEM R FN.FT" or by
  693. ; "XMODEM RC FN.FT" if Checksum is to be used.
  694. ;
  695. RCVFIL: CALL    LOGDU        ;check file name or drive/user option
  696. ;
  697.      IF    SETAREA
  698.     MVI    A,DRV-40H
  699.     STA    FCB
  700.      ENDIF            ;SETAREA
  701. ;
  702.     LDA    PRVTFL        ;receiving to a private area?
  703.     ORA    A
  704.     JZ    RCVFIL1        ;if not, exit
  705.     MVI    A,PRDRV-40H    ;private area takes precedence
  706.     STA    FCB        ;store drive to be used
  707. ;
  708. RCVFIL1: IF    NOCOMR
  709.     LXI    H,FCB+9        ;point to filetype
  710.     MVI    A,'C'        ;1st letter
  711.     CMP    M        ;is it C ?
  712.     JNZ    CONTINU        ;if not, continue normally
  713.     INX    H        ;get 2nd letter
  714.     MVI    A,'O'        ;2nd letter
  715.     CMP    M        ;is it O ?
  716.     JNZ    CONTINU        ;if not, continue normally
  717.     INX    H        ;get 3rd letter
  718.     MVI    A,'M'        ;3rd letter
  719.     CMP    M        ;is it M ?
  720.     JNZ    CONTINU        ;if not, continue normally
  721.     CALL    ILPRT        ;print renaming message
  722.     DB    'Auto-renaming file to ".OBJ"',CR,LF,0
  723.     LXI    H,FCB+9
  724.     MVI    M,'O'
  725.     INX    H
  726.     MVI    M,'B'
  727.     INX    H
  728.     MVI    M,'J'
  729.      ENDIF            ;NOCMR
  730. ;
  731. CONTINU:CALL    ILPRT        ;print the message
  732.     DB    'File will be received on ',0
  733.     LDA    PRVTFL        ;going to store in the private area?
  734.     ORA    A
  735.     LDA    XPRDRV        ;get private drive
  736.     JNZ    CONTIN1        ;if yes, it takes priority
  737.     LDA    OLDDRV        ;otherwise get current drive
  738.     ADI    'A'        ;convert to ASCII
  739. ;
  740.      IF    SETAREA
  741.     LDA    XDRV        ;setarea uses a specified drive
  742.      ENDIF            ;SETAREA
  743. ;
  744.      IF    NOT SETAREA
  745. NOTDRV: DB    0,0        ;filled in by 'GETDU' if requested
  746.      ENDIF            ;NOT SETAREA
  747. ;
  748. CONTIN1:CALL    CTYPE        ;print the drive to store on
  749.     LDA    PRVTFL        ;going to store in the private area?
  750.     ORA    A
  751.     LDA    XPRUSR        ;get private user area
  752.     JNZ    CONTIN2        ;if yes, it takes priority
  753.     LDA    OLDUSR        ;get current drive
  754. ;
  755.      IF    SETAREA
  756.     LDA    XUSR        ;setarea takes next precedence
  757.      ENDIF            ;SETAREA
  758. ;
  759.      IF    NOT SETAREA
  760. NOTUSR: DB    0,0        ;filled in by 'GETDU' if requested
  761.      ENDIF            ;NOT SETAREA
  762. ;
  763. CONTIN2:MVI    H,0
  764.     MOV    L,A
  765.     CALL    DECOUT        ;print the user area
  766.     CALL    ILPRT
  767.     DB    ':',CR,LF,0
  768.     CALL    CHEKFIL        ;see if file exists
  769.     CALL    MAKEFIL        ;if not, start a new file
  770.     CALL    ILPRT
  771.     DB    'File open - ready to receive',CR,LF,0
  772. ;
  773. RCVLP:    CALL    RCVRECD        ;get a record
  774.     JC    RCVEOT        ;got 'EOT'
  775.     CALL    WRRECD        ;write the record
  776.     CALL    INCRRNO        ;bump record number
  777.     CALL    SENDACK        ;ack the record
  778.     JMP    RCVLP        ;loop until 'EOF'
  779. ;.....
  780. ;
  781. ;
  782. ; Got EOT on record so flush buffers then done
  783. ;
  784. RCVEOT: LDA    RECDNO        ;check for zero length record
  785.     CPI    0
  786.     JNZ    EOT1        ;at least some upload
  787.     LDA    RECDNO+1    ;check second byte
  788.     CPI    0
  789.     JNZ    EOT1
  790.     CALL    RCVSABT        ;abort and erase the zero length file
  791.     JMP    EXIT        ;and exit
  792. EOT1    CALL    WRBLOCK        ;write the last block
  793.     CALL    SENDACK        ;ack the record
  794.     CALL    CLOSFIL        ;close the file
  795. ;
  796.      IF     LOGLDS
  797.     LDA    UPLDS        ;get Upload Counter
  798.     INR    A        ;one more upload since log in
  799.     STA    UPLDS        ;update Counter
  800.      ENDIF            ;LOGLDS
  801. ;
  802.     JMP    EXITLG        ;all done
  803. ;.....
  804. ;
  805. ;
  806. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  807. ;
  808. ;            SUBROUTINES
  809. ;
  810. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * *
  811. ;
  812. ;
  813. ; ---> LOGDU  Log into drive and user (if specified).  If none mentioned
  814. ;          it falls through to 'TRAP' routine for normal use.
  815. ;
  816. LOGDU:    LXI    H,DEFDMA    ;point to default buffer command line
  817.     MOV    B,M        ;store number of chars. in command
  818.     INR    B        ;add in current location
  819. ;
  820. LOG1:    CALL    CHKSP        ;skip spaces to find 1st command
  821.     JZ    LOG1
  822. ;
  823. LOG2:    CALL    CHKSP        ;skip 1st command (non-spaces)
  824.     JNZ    LOG2
  825.     INX    H
  826.     CALL    CHKFSP        ;skip spaces to find 2nd command
  827.     SHLD    SAVEHL        ;save start address of the 2nd command
  828. ;
  829. ;
  830. ; Now point to the first byte in the argument, i.e., if it was of format
  831. ; similar to:  B6:HELLO.DOC then we point at the drive character 'B'.
  832. ;
  833.     LXI    D,DUSAVE
  834.     MVI    C,4        ;drive/user is 4 chars. maximum
  835. ;
  836. CPLP:    MOV    A,M
  837.     CPI    ' '+1        ;space or return, finished
  838.     JC    TRAP
  839.     STAX    D
  840.     INX    H
  841.     INX    D
  842.     CPI    ':'
  843.     JZ    GETDU        ;if colon, get drive/user and log in
  844.     DCR    B        ;one less position to check
  845.     DCR    C        ;one less to go
  846.     JNZ    CPLP
  847. ;
  848. ;
  849. ; ---> TRAP  Check for no file name or ambiguous name
  850. ;
  851. TRAP:    CALL    MOVEFCB        ;move the filename into the file block
  852.     LXI    H,FCB+1        ;point to file name
  853.     MOV    A,M        ;get first char of file name
  854.     CPI    ' '        ;any there?
  855.     JNZ    ATRAP        ;yes, check for ambigous file name
  856. ;
  857. NFN:    CALL    ERXIT        ;print message, exit
  858.     DB    '++ No file name requested ++$'
  859. ;...
  860. ;
  861. ;
  862. ATRAP:    MVI    B,11        ;11 characters to check
  863. ;
  864. TRLOOP: MOV    A,M        ;get char from FCB
  865.     CPI    '?'        ;ambiguous?
  866.     JZ    TRERR        ;yes, exit with error message
  867.     CPI    '*'        ;even more ambiguous?
  868.     JZ    TRERR        ;yes, exit with error message
  869.     INX    H        ;point to next character
  870.     DCR    B        ;one less to go
  871.     JNZ    TRLOOP        ;not done, check some more
  872.     RET
  873. ;......
  874. ;
  875. ;
  876. TRERR:    CALL    ERXIT        ;print message, exit
  877.     DB    '++ Wild-card options are not valid ++$'
  878. ;.....
  879. ;
  880. ;
  881. ; ---> GETDU  Get <D>isk and <U>ser from DUSAVE and log in if valid.
  882. ;
  883. GETDU:    CALL    CHKFSP        ;see if a file name is included
  884.     SHLD    SAVEHL        ;save location of the filename
  885.     LDA    PRVTFL        ;uploading to a private area?
  886.     ORA    A
  887.     JNZ    TRAP        ;if yes, going to a specified area
  888.     LXI    H,DUSAVE    ;point to drive/user
  889.     MVI    A,YES        ;reset to provide for current drive
  890.     STA    DUD
  891.     MOV    A,M        ;get 1st character
  892.     CPI    'A'-1
  893.     JC    NUMERIC        ;satisfied with current drive
  894.     SUI    'A'
  895. ;
  896.      IF    NOT USEMAX
  897.     CPI    MAXDRV
  898.     JNC    ILLDU        ;drive selection not available
  899.      ENDIF            ;NOT USEMAX
  900. ;
  901.      IF    USEMAX
  902.     PUSH    H
  903.     LXI    H,DRIVMAX    ;point to max drive byte
  904.     INR    M
  905.     CMP    M        ;and check it
  906.     PUSH    PSW        ;save flags from the CMP
  907.     DCR    M        ;restore max drive to normal
  908.     POP    PSW        ;restore flags from the CPM
  909.     JNC    ILLDU
  910.     POP    H
  911.      ENDIF            ;USEMAX
  912. ;
  913.     STA    DUD        ;save drive
  914.     INX    H        ;get 2nd character
  915. ;
  916. NUMERIC:MOV    A,M
  917.     CPI    ':'
  918.     JZ    OK4        ;colon for drive only, no user number
  919.     CALL    CKNUM        ;check if numeric
  920.     SUI    '0'        ;convert ascii to binary
  921.     STA    DUU        ;save it
  922.     INX    H        ;get 3rd character if any
  923.     MOV    A,M
  924.     CPI    ':'
  925.     JZ    OK1
  926.     LDA    DUU
  927.     CPI    1        ;is first number a '1'?
  928.     JNZ    ILLDU
  929.     MOV    A,M
  930.     CALL    CKNUM
  931.     SUI    '0'-10
  932.     STA    DUU
  933.     INX    H        ;get 4th (and last character) if any
  934.     MOV    A,M
  935.     CPI    ':'
  936.     JNZ    ILLDU
  937. ;
  938. OK1:    LDA    OPTSAV        ;get the option back
  939.     CPI    'R'        ;receiving a file?
  940.     LDA    DUU        ;get desired user area
  941.     JZ    OK2        ;yes, can not use special download area
  942.     LDA    DUD        ;get desired drive
  943.     CPI    SPLDRV-'A'    ;special download drive requested?
  944.     LDA    DUU        ;get user area requested
  945.     JNZ    OK2        ;if none, exit
  946.     CPI    SPLUSR        ;special download area requested?
  947.     JZ    OK3        ;if yes, process request
  948. ;
  949. OK2:     IF    NOT USEMAX
  950.     CPI    MAXUSR+1    ;check for maximum user download area
  951.     JNC    ILLDU        ;error if more (and not special area)
  952.      ENDIF            ;NOT USEMAX
  953. ;
  954.      IF    USEMAX
  955.     PUSH    H
  956.     LXI    H,USRMAX    ;point at max user byte
  957.     CMP    M        ;and check it
  958.     JNC    ILLDU
  959.     POP    H
  960.      ENDIF            ;USEMAX
  961. ;
  962. OK3:    MOV    E,A
  963. ;
  964.      IF    NOT SETAREA
  965.     STA    NOTUSR+1    ;store requested user area
  966.     MVI    A,3EH        ;'MVI A,--' instruction
  967.     STA    NOTUSR
  968.      ENDIF            ;NOT SETAREA
  969. ;
  970.     MVI    C,USER
  971.     CALL    BDOS        ;set to requested user area
  972. ;
  973. OK4:    LDA    DUD        ;get drive
  974.     MOV    E,A
  975. ;
  976.      IF    NOT SETAREA
  977.     ADI    'A'
  978.     STA    NOTDRV+1    ;store requested drive
  979.     MVI    A,3EH        ;'MVI A,--' instruction
  980.     STA    NOTDRV
  981.      ENDIF            ;NOT SETAREA
  982. ;
  983.     MVI    C,SELDRV
  984.     CALL    BDOS        ;set to requested drive
  985. ;
  986. XIT:    JMP    TRAP        ;now find file selected
  987. ;.....
  988. ;
  989. ;
  990. CKNUM:    CPI    '0'
  991.     JC    ILLDU        ;error if less than ascii '0'
  992.     CPI    '9'+1
  993.     RC            ;error if more than ascii '9'
  994. ;...
  995. ;
  996. ;
  997. ILLDU:    CALL    ERXIT
  998.     DB    '++ Improper drive/user combination ++$'
  999. ;.....
  1000. ;
  1001. ;
  1002. ; Check next character to see if a space or non-space, file name error
  1003. ; if no ASCII character.
  1004. ;
  1005. CHKFSP: DCR    B
  1006.     JZ    NFN        ;error if end of chars.
  1007.     MOV    A,M
  1008.     CPI    ' '+1
  1009.     RNC            ;ok if valid character so return
  1010.     INX    H
  1011.     JMP    CHKFSP        ;look at next character
  1012. ;.....
  1013. ;
  1014. ;
  1015. ; Check next character to see if a space or non-space, go to menu if a
  1016. ; command error.
  1017. ;
  1018. CHKSP:    DCR    B
  1019.     JZ    OPTNERR
  1020.     INX    H
  1021.     MOV    A,M        ;get the char. there
  1022.     CPI    ' '        ;space character?
  1023.     RET            ;JZ = space, JNZ = non-space
  1024. ;.....
  1025. ;
  1026. ;
  1027. ; ---> RCVRECD    Receive a record
  1028. ;
  1029. ; Returns with carry bit set if EOT received
  1030. ;
  1031. RCVRECD:XRA    A        ;initialize error count to zero
  1032.     STA    ERRCT
  1033. ;
  1034. RCVRPT: XRA    A        ;get 0
  1035.     STA    ERRCDE        ;clear receive error code
  1036.     MVI    B,10-1        ;10-second timeout
  1037.     CALL    RECV        ;get any character received
  1038.     JC    RCVSTOT        ;timeout
  1039.     CPI    SOH        ;hoping for a 'SOH'
  1040.     JZ    RCVSOH        ;yes
  1041.     ORA    A
  1042.     JZ    RCVRPT        ;ignore nulls
  1043.     CPI    CRC        ;ignore our own 'CRC' if needed
  1044.     JZ    RCVRPT
  1045.     CPI    NAK        ;ignore our own 'NAK' if needed
  1046.     JZ    RCVRPT
  1047.     CPI    EOT        ;end of transfer?
  1048.     STC            ;return with carry set if 'EOT'
  1049.     RZ
  1050. ;
  1051. ;
  1052. ; Didn't get SOH or EOT - or - didn't get valid header - purge the line,
  1053. ; then send nak
  1054. ;
  1055. RCVSERR:MVI    B,1        ;wait for 1 second
  1056.     CALL    RECV        ;after last char. received
  1057.     JNC    RCVSERR        ;loop until sender done
  1058.     LDA    CRCFLG        ;get 'CRC' flag
  1059.     ORA    A        ;'CRC' in effect?
  1060.     MVI    A,NAK        ;put 'NAK' in accum
  1061.     JNZ    RCVSER2        ;no, send the 'NAK'
  1062.     LDA    FRSTIM        ;get first time switch
  1063.     ORA    A        ;has first 'SOH' been received?
  1064.     MVI    A,NAK
  1065.     JNZ    RCVSER2        ;yes, then send 'NAK'
  1066.     MVI    A,CRC        ;tell sender 'CRC' is in effect
  1067. ;
  1068. RCVSER2:CALL    SEND        ;   the 'NAK' or 'CRC' request
  1069.     LDA    ERRCT        ;abort if
  1070.     INR    A        ;   we have reached
  1071.     STA    ERRCT        ;the error
  1072.     CPI    10        ;   limit?
  1073.     JC    RCVRPT        ;   no, try again
  1074. ;
  1075. ;
  1076. ; Error limit exceeded, so abort
  1077. ;
  1078. RCVSABT:CALL    CLOSFIL        ;keep whatever we got
  1079.     CALL    ILPRT
  1080.     DB    CR,LF,CR,LF,'++ RECEIVED FILE CANCELLED ++',0
  1081.     CALL    DELFILE        ;delete received file
  1082.     CALL    ERXIT        ;print second half of message
  1083.     DB    '++ UNFINISHED FILE DELETED ++$'
  1084. ;
  1085. ;
  1086. ; ---> DELFILE    deletes the received file (used if receive aborts)
  1087. ;
  1088. DELFILE:LXI    D,FCB        ;point to file
  1089.     MVI    C,ERASEF    ;get function
  1090.     CALL    BDOS        ;delete it
  1091.     INR    A        ;delete ok?
  1092.     RNZ            ;  yes, return
  1093.     CALL    ERXIT        ;  no, abort
  1094.     DB    '++ Can''t delete received file ++$'
  1095. ;
  1096. ;
  1097. ; Timed out on receive
  1098. ;
  1099. RCVSTOT:JMP    RCVSERR        ;bump error count, etc.
  1100. ;
  1101. ;
  1102. ; Got SOH - get block number, block number complemented
  1103. ;
  1104. RCVSOH: MVI    B,1        ;timeout = 1 sec
  1105.     STA    FRSTIM        ;indicate first 'SOH' received
  1106.     CALL    RECV        ;get record
  1107.     JC    RCVSTOT        ;got timeout
  1108.     MOV    D,A        ;D=block number
  1109.     MVI    B,1        ;timeout = 1 sec
  1110.     CALL    RECV        ;get complimented record number
  1111.     JC    RCVSTOT        ;timeout
  1112.     CMA            ;calculate the    complement
  1113.     CMP    D        ;good record number?
  1114.     JZ    RCVDATA        ;yes, get data
  1115. ;
  1116. ;
  1117. ; Got bad record number
  1118. ;
  1119.     JMP    RCVSERR        ;bump error count
  1120. ;...
  1121. ;
  1122. ;
  1123. RCVDATA:MOV    A,D        ;get record number
  1124.     STA    RCVRNO        ;save it
  1125.     MVI    C,0        ;initialize checksum
  1126.     CALL    CLRCRC        ;clear CRC counter
  1127.     MVI    D,128        ;initialize the count
  1128.     LHLD    RECPTR        ;get buffer address
  1129. ;
  1130. RCVCHR: MVI    B,1        ;1 sec timeout
  1131.     CALL    RECV        ;get the character
  1132.     JC    RCVSTOT        ;timeout
  1133.     MOV    M,A        ;store the character
  1134.     INX    H        ;point to next character
  1135.     DCR    D        ;done?
  1136.     JNZ    RCVCHR        ;no, loop if <= 128
  1137.     LDA    CRCFLG        ;get 'CRC' flag
  1138.     ORA    A        ;'CRC' in effect?
  1139.     JZ    RCVCRC        ;yes, to receive 'CRC'
  1140. ;
  1141. ;
  1142. ; Verify checksum
  1143. ;
  1144.     MOV    D,C        ;save checksum
  1145.     MVI    B,1        ;timeout length
  1146.     CALL    RECV        ;get checksum
  1147.     JC    RCVSTOT        ;timeout
  1148.     CMP    D        ;checksum ok?
  1149.     JNZ    RCVSERR        ;no, error
  1150. ;
  1151. ;
  1152. ; Got a record, it's a duplicate if = previous, or OK if = 1 + previous
  1153. ; record
  1154. ;
  1155. CHKSNUM:LDA    RCVRNO        ;get received
  1156.     MOV    B,A        ;save it
  1157.     LDA    RECDNO        ;get previous
  1158.     CMP    B        ;prev repeated?
  1159.     JZ    RECVACK        ;'ACK' to catch up
  1160.     INR    A        ;calculate next record number
  1161.     CMP    B        ;match?
  1162.     JNZ    ABORT        ;no match - stop sender, exit
  1163.     RET            ;carry off - no errors
  1164. ;
  1165. ; ---> RCVCRC    Receive the Cyclic Redundancy Check characters (2 bytes)
  1166. ;        and see if the CRC received matches the one calculated.
  1167. ;        If they match, get next record, else send a NAK request-
  1168. ;        ing the record be sent again.
  1169. ;
  1170. RCVCRC: MVI    E,2        ;number of bytes to receive
  1171. ;
  1172. RCVCRC2:MVI    B,1        ;1 sececond timeout
  1173.     CALL    RECV        ;get crc byte
  1174.     JC    RCVSTOT        ;timeout
  1175.     DCR    E        ;decrement the number of bytes
  1176.     JNZ    RCVCRC2        ;get both bytes
  1177.     CALL    CHKCRC        ;check received CRC against calc'd CRC
  1178.     ORA    A        ;is CRC okay?
  1179.     JZ    CHKSNUM        ;yes, go check record numbers
  1180.     JMP    RCVSERR        ;go check error limit and send NAK
  1181. ;
  1182. ;
  1183. ; Previous record repeated, due to the last ACK being garbaged.     ACK it
  1184. ; so sender will catch up
  1185. ;
  1186. RECVACK:CALL    SENDACK        ;send the ACK
  1187.     JMP    RCVRECD        ;get next block
  1188. ;.....
  1189. ;
  1190. ;
  1191. ; Send an ACK for the record
  1192. ;
  1193. SENDACK:MVI    A,ACK        ;get 'ACK'
  1194.     CALL    SEND        ;  and send it
  1195.     RET
  1196. ;.....
  1197. ;
  1198. ;
  1199. ; ---> SENDHDR    Send the record header
  1200. ;
  1201. ; Send    (SOH) (block number) (complemented block number)
  1202. ;
  1203. SENDHDR:MVI    A,SOH        ;send
  1204.     CALL    SEND        ;  'SOH',
  1205.     LDA    RECDNO        ;then send
  1206.     CALL    SEND        ;  record number
  1207.     LDA    RECDNO        ;then record number
  1208.     CMA            ;  complemented
  1209.     CALL    SEND        ;  record number
  1210.     RET            ;from SENDHDR
  1211. ;.....
  1212. ;
  1213. ;
  1214. ; ---> SENDREC    send the data record
  1215. ;
  1216. SENDREC:MVI    C,0        ;initialize checksum
  1217.     CALL    CLRCRC        ;clear the 'CRC' counter
  1218.     MVI    D,128        ;initialize the count
  1219.     LHLD    RECPTR        ;get buffer address
  1220. ;
  1221. SENDC:    MOV    A,M        ;get a character
  1222.     CALL    SEND        ;send it
  1223.     INX    H        ;point to next character
  1224.     DCR    D        ;done?
  1225.     JNZ    SENDC        ;loop if <=128
  1226.     RET            ;from SENDREC
  1227. ;.....
  1228. ;
  1229. ;
  1230. ; ---> SENDCKS    send the checksum
  1231. ;
  1232. SENDCKS:MOV    A,C        ;send the
  1233.     CALL    SEND        ;  checksum
  1234.     RET            ;from 'SENDCKS'
  1235. ;.....
  1236. ;
  1237. ;
  1238. ; ---> SENDCRC    Send the two Cyclic Redundancy Check characters.  Call
  1239. ;        FINCRC to calculate the CRC which will be in 'DE' upon
  1240. ;        return.
  1241. ;
  1242. SENDCRC:CALL    FINCRC        ;calculate the 'CRC' for this record
  1243.     MOV    A,D        ;put first 'CRC' byte in accumulator
  1244.     CALL    SEND        ;send it
  1245.     MOV    A,E        ;put second 'CRC' byte in accumulator
  1246.     CALL    SEND        ;send it
  1247.     XRA    A        ;set zero return code
  1248.     RET
  1249. ;.....
  1250. ;
  1251. ;
  1252. ; ---> GETACK  Get the ACK on the record
  1253. ;
  1254. ; Returns with carry clear if ACK received.  If an ACK is not received,
  1255. ; the error count is incremented, and if less than 10, carry is set and
  1256. ; the record is resent.     if the error count is 10, the program aborts.
  1257. ; waits 12 seconds to avoid any collision with the receiving station.
  1258. ;
  1259. GETACK: MVI    B,12        ;wait 12 seconds max
  1260.     CALL    RECVDG        ;receive with garbage collect
  1261.     JC    ACKERR        ;timed out
  1262.     CPI    ACK        ;was it an 'ACK' character?
  1263.     RZ            ;yes, return
  1264. ;
  1265.      IF    NOT ACKNAK
  1266.     CPI    NAK        ;was it an authentic 'NAK'?
  1267.     JNZ    GETACK        ;ignore if neither 'ACK' nor 'NAK'
  1268.      ENDIF            ;NOT ACKNAK
  1269. ;
  1270. ;
  1271. ; Timeout or error on ACK - bump error count then resend the record if
  1272. ; error limit is not exceeded
  1273. ;
  1274. ACKERR: LDA    ERRCT        ;get count
  1275.     INR    A        ;bump it
  1276.     STA    ERRCT        ;save back
  1277.     CPI    10        ;at limit?
  1278.     RC            ;if not, go resend the record
  1279. ;
  1280. ;
  1281. ; Reached error limit
  1282. ;
  1283. CSABORT:CALL    ERXIT
  1284.     DB    '++ SEND FILE CANCELLED ++$'
  1285. ;.....
  1286. ;
  1287. ;
  1288. ABORT:    LXI    SP,STACK
  1289. ;
  1290. ABORTL: MVI    B,1        ;one second without characters
  1291.     CALL    RECV
  1292.     JNC    ABORTL        ;loop until sender done
  1293.     MVI    A,CAN        ;CTL-X
  1294.     CALL    SEND        ;stop sending end
  1295. ;
  1296. ABORTW: MVI    B,1        ;one second without chracters
  1297.     CALL    RECV
  1298.     JNC    ABORTW        ;loop until sender done
  1299.     MVI    A,CR        ;get a space...
  1300.     CALL    SEND        ;to clear out CTL-X
  1301.     CALL    ERXIT        ;exit with abort message
  1302.     DB    '++ XMODEM ABORTED ++$'     
  1303. ;.....
  1304. ;
  1305. ;
  1306. ; ---> INCRRNO    increment record number
  1307. ;
  1308. INCRRNO:PUSH    H
  1309.     LHLD    RECDNO        ;increment record number
  1310.     INX    H
  1311.     SHLD    RECDNO
  1312. ;
  1313.      IF    NOT USECON    ;save the check if guaranteed conout
  1314.     LHLD    CONOUT+1    ;check to see if showing count on crt
  1315.     MOV    A,H        ;if both zero, user did not fill out
  1316.     ORA    L        ;   'CONOUT:  jmp 0000H' in patch area
  1317.     JZ    INCRN5        ;   with his own console output address
  1318.      ENDIF            ;NOT USECON
  1319. ;
  1320. ;
  1321. ; Display the record count on the local CRT if "CONOUT" was filled in by
  1322. ; the implementor
  1323.  
  1324.     MVI    A,1
  1325.     STA    CONONL        ;set local only
  1326.     LDA    OPTSAV        ;see if receive or send mode
  1327.     CPI    'R'
  1328.     JZ    RMSG
  1329.     CALL    ILPRT
  1330.     DB    CR,'Sent # ',0
  1331.     JMP    REST
  1332. RMSG:    CALL    ILPRT
  1333.     DB    CR,'Received # ',0
  1334. REST:    LHLD    RECDNO
  1335.     CALL    DECOUT
  1336.     CALL    ILPRT
  1337.     DB    ' ',0
  1338. ;
  1339.      IF    SHOWHEX        ;if yes, shows both decimal and hex
  1340.     CALL    ILPRT
  1341.     DB    '(',0
  1342.     CALL    DHXOUT
  1343.     CALL    ILPRT
  1344.     DB    'H) ',0
  1345.      ENDIF            ;SHOWHEX
  1346. ;
  1347.     XRA    A        ;reset the flag for local only
  1348.     STA    CONONL
  1349. ;
  1350. INCRN5: POP    H        ;here from above if no CONOUT
  1351.     RET
  1352. ;.....
  1353. ;
  1354. ;
  1355. ; ---> CHEKFIL    See if file exists
  1356. ;
  1357. ; If it exists, say use a different name.
  1358. ;
  1359. CHEKFIL: IF    NOT SETAREA
  1360.     LDA    PRVTFL        ;receiving in private area?
  1361.     ORA    A
  1362.     CNZ    RECAREA        ;if yes, set drive and user area
  1363.      ENDIF            ;NOT SETAREA
  1364. ;
  1365.      IF    SETAREA
  1366.     CALL    RECAREA        ;set the designated area up
  1367.      ENDIF            ;SETAREA
  1368. ;
  1369. CHEKFIL1:
  1370.     LXI    D,FCB        ;point to control block
  1371.     MVI    C,SRCHF        ;see if it
  1372.     CALL    BDOS        ;   exists
  1373.     INR    A        ;found?
  1374.     RZ            ;   no, return
  1375.     CALL    ERXIT        ;exit, print error message
  1376.     DB    '++ File exists, use a different name ++$'
  1377. ;
  1378. ;
  1379. ; ---> MAKEFIL    Makes the file to be received
  1380. ;
  1381. MAKEFIL:XRA    A        ;set extent and record number to 0
  1382.     STA    FCBEXT
  1383.     STA    FCBRNO
  1384.     LXI    D,FCB        ;point to FCB
  1385.     MVI    C,MAKE        ;get bdos FNC
  1386.     CALL    BDOS        ;to the make
  1387.     INR    A        ;0FFH=bad?
  1388.     RNZ            ;open ok
  1389. ;
  1390. ;
  1391. ; Directory full - can't make file
  1392. ;
  1393.     CALL    ERXIT
  1394.     DB    '++ Error: can''t make file -'
  1395.     DB    ' directory may be full? ++$'
  1396. ;
  1397. ;
  1398. ; ---> CNREC  Computes record count, and saves it until a successful
  1399. ;           file-open.
  1400. ;
  1401. ; Look up the FCB in the directory
  1402. ;
  1403. CNREC:    MVI    C,CFSIZE    ;computes file size
  1404.     LXI    D,FCB
  1405.     CALL    BDOS        ;read first
  1406.     LHLD    RANDOM        ;get the file size
  1407.     SHLD    RCNT        ;save total record count
  1408.     MOV    A,H
  1409.     ORA    L
  1410.     RNZ            ;return if not zero length
  1411. ;
  1412. NONAME: CALL    ERXIT
  1413.     DB    '++ No file with that name ++$'
  1414. ;.....
  1415. ;
  1416. ;
  1417. ; ---> OPENFIL    Opens the file to be sent
  1418. ;
  1419. OPENFIL:XRA    A        ;set extent and rec number to 0
  1420.     STA    FCBEXT        ;  for proper open
  1421.     STA    FCBRNO
  1422.     LXI    D,FCB        ;point to file
  1423.     MVI    C,OPEN        ;get function
  1424.     CALL    BDOS        ;open it
  1425.     INR    A        ;open ok?
  1426.     JNZ    OPENOK        ;if yes, exit
  1427.     LDA    OPTSAV        ;get command line option
  1428.     CPI    'L'        ;want to send a library file?
  1429.     JNZ    NONAME        ;exit, if not
  1430.     CALL    ILPRT
  1431.     DB    CR,LF,'++ No library file with that name ++',CR,LF,0
  1432.     JMP    OPTNERR
  1433. ;.....
  1434. ;
  1435. ;
  1436. ; Check for distribution-protected file
  1437. ;
  1438. OPENOK: LDA    FCB+1        ;first char of file name
  1439.     ANI    80H        ;check bit 7
  1440.     JNZ    OPENOT        ;if on, file can not be sent
  1441.     LDA    FCB+2        ;also check 'F2' for tab
  1442.     ANI    80H        ;is is set?
  1443. ;
  1444.      IF    NOSYS
  1445.     JNZ    OPENOT
  1446.     LDA    FCB+10
  1447.     ANI    80H
  1448.     JNZ    NONAME        ;if $SYS then fake a "file not found"
  1449.      ENDIF            ;NOSYS
  1450. ;
  1451.     JZ    OPENOK2        ;if not, ok to send file
  1452. ;
  1453. OPENOT: CALL    ERXIT        ;exit with message
  1454.     DB    '++ File is not for distribution, sorry ++$'
  1455. ;...
  1456. ;
  1457. ;
  1458. OPENOK2:LDA    OPTSAV
  1459.     CPI    'L'
  1460.     JNZ    OPN2
  1461.     LXI    D,DEFDMA
  1462.     MVI    C,SETDMA
  1463.     CALL    BDOS
  1464.     MVI    C,READ
  1465.     LXI    D,FCB
  1466.     CALL    BDOS
  1467.     LHLD    8EH
  1468.     SHLD    DIRSZ
  1469.     LXI    H,DEFDMA
  1470.     MOV    A,M
  1471.     ORA    A
  1472.     JZ    CKDIR        ;check directory present?
  1473. ;
  1474. NOTLBR: CALL    ERXIT
  1475.     DB    '++ Library directory invalid? ++$'
  1476. ;.....
  1477. ;
  1478. ;
  1479. ; --> CKDIR  Check to see if there is a .LBR file directory with that
  1480. ;         name and complain if not.
  1481. ;
  1482. CKDIR:    MVI    B,11        ;maximum length of file name
  1483.     MVI    A,' '        ;first entry must be all blanks
  1484.     INX    H
  1485. ;
  1486. CKDLP:    CMP    M
  1487.     JNZ    NOTLBR
  1488.     DCR    B
  1489.     INX    H
  1490.     JNZ    CKDLP
  1491. ;
  1492. ;
  1493. ; The first entry in the .LBR directory is indeed blank.  Now see if the
  1494. ; directory size is more than 0.
  1495.  
  1496.     MOV    D,M        ;get directory starting location
  1497.     INX    H        ;...which must be 0000H...
  1498.     MOV    A,M
  1499.     ORA    D
  1500.     JNZ    NOTLBR        ;directory does not start in record 0
  1501.     INX    H
  1502.     MOV    A,M        ;get size of directory
  1503.     INX    H
  1504.     ORA    M
  1505.     JZ    NOTLBR        ;directory must be >0 records!
  1506.     LXI    H,DEFDMA    ;point to directory
  1507. ;
  1508. ;
  1509. ; The next routine checks the .LBR directory for the specified member.
  1510. ; Name one sector at a time.
  1511. ;
  1512. CMLP:    MOV    A,M        ;get member active flag
  1513.     ORA    A        ;00=active, anything else can be...
  1514.     MVI    B,11        ;...regarded as invalid (erased or blank)
  1515.     INX    H        ;point to member name
  1516.     JNZ    NOMTCH        ;no match if inactive entry
  1517. ;
  1518. CKLP:    LDAX    D        ;now compare the file name specified...
  1519.     CMP    M        ;...against the member file name
  1520.     JNZ    NOMTCH        ;exit loop if no match found
  1521.     INX    H
  1522.     INX    D
  1523.     DCR    B
  1524.     JNZ    CKLP        ;check all 11 characters
  1525.     MOV    E,M        ;got the file - get file address
  1526.     INX    H
  1527.     MOV    D,M
  1528.     XCHG
  1529.     SHLD    INDEX        ;save file address in .LBR
  1530.     XCHG
  1531.     INX    H
  1532.     MOV    E,M        ;get the file size
  1533.     INX    H
  1534.     MOV    D,M
  1535.     XCHG
  1536.     DCX    H
  1537.     SHLD    RCNT        ;save size a # of records
  1538.     LHLD    INDEX        ;get file address
  1539.     SHLD    RANDOM        ;place it into random field
  1540.     XRA    A
  1541.     STA    RANDOM+2    ;must zero the 3rd byte
  1542.     STA    FCBRNO        ;also zero FCB record #
  1543.     LXI    D,FCB        ;point to FCB of .LBR file
  1544.     MVI    C,RRDM        ;read random
  1545.     CALL    BDOS
  1546.     JMP    OPENOK3        ;no need to error check
  1547. ;...
  1548. ;
  1549. ;
  1550. ; Come here if no file name match and another sector is needed
  1551. ;
  1552. NOMTCH: INX    H        ;skip past the end of the file entry
  1553.     DCR    B
  1554.     JNZ    NOMTCH
  1555.     LXI    B,20        ;point to next file entry
  1556.     DAD    B
  1557.     LXI    D,MEMFCB    ;point to member name again
  1558.     MOV    A,H        ;see if we checked all 4 entries
  1559.     ORA    A
  1560.     JZ    CMLP        ;no, check next
  1561.     LHLD    DIRSZ        ;get directory size
  1562.     MOV    A,H
  1563.     ORA    L
  1564.     JNZ    INLBR        ;continue if still more to check
  1565.     CALL    ERXIT
  1566.     DB    '++ Library does not have that file ++$'
  1567. ;
  1568. INLBR:    DCX    H        ;decrement dirctory size
  1569.     SHLD    DIRSZ
  1570.     MVI    C,READ        ;read next sector of directory
  1571.     LXI    D,FCB
  1572.     CALL    BDOS
  1573.     LXI    H,DEFDMA    ;set our pointers for compare
  1574.     LXI    D,MEMFCB
  1575.     JMP    CMLP        ;check next sector
  1576. ;.....
  1577. ;
  1578. ;
  1579. OPN2:     IF    NOLBS OR NOCOMS ;check for send restrictions
  1580.     LXI    H,FCB+11
  1581.     MOV    A,M        ;check for protect attr
  1582.     ANI    7FH        ;remove CP/M 2.x attrs
  1583.      ENDIF            ;NOLBS OR NOCOMS
  1584. ;
  1585.      IF    NOLBS        ;do not allow '#' to be sent
  1586.     CPI    '#'        ;chk for '#' as last first
  1587.     JZ    OPENOT        ;if '#', can not send, show why
  1588.      ENDIF            ;NOLBS
  1589. ;
  1590.      IF    NOCOMS        ;do not allow '.COM' to be sent
  1591.     CPI    'M'        ;if not, check for '.COM'
  1592.     JNZ    OPENOK3        ;if not, ok to send
  1593.     DCX    H
  1594.     MOV    A,M        ;check next character
  1595.     ANI    7FH        ;strip attributes
  1596.     CPI    'O'        ;'O'?
  1597.     JNZ    OPENOK3        ;if not, ok to send
  1598.     DCX    H
  1599.     MOV    A,M        ;now check 1st character
  1600.     ANI    7FH        ;strip attributes
  1601.     CPI    'C'        ;'C' as in '.COM'?
  1602.     JNZ    OPENOK3        ;if not, continue
  1603.     CALL    ERXIT        ;exit with message
  1604.     DB    '++ Can''t Send a .COM File ++$'
  1605.      ENDIF            ;NOCOMS
  1606. ;.....
  1607. ;
  1608. ;
  1609. OPENOK3:CALL    ILPRT        ;print the message
  1610.     DB    'File open: ',0
  1611.     LHLD    RCNT        ;get record count
  1612.     LDA    OPTSAV
  1613.     CPI    'L'
  1614.     JNZ    OPENOK4        ;if send from library add 1 to
  1615.     INX    H        ;show correct record count
  1616. ;
  1617. OPENOK4:CALL    DECOUT        ;print decimal number of records
  1618.     CALL    ILPRT
  1619. ;
  1620.      IF    SHOWHEX        ;if yes, shows both decimal and hex
  1621.     DB    ' (',0
  1622.     CALL    DHXOUT
  1623.     CALL    ILPRT
  1624.     DB    'H)'
  1625.      ENDIF            ;SHOWHEX
  1626. ;
  1627.     DB    ' records',CR,LF
  1628.     DB    'Send time: ',0
  1629.     CALL    SPEED        ;get speed indicator
  1630.     LXI    D,0
  1631.     MOV    E,A        ;set up for table access
  1632.     LXI    H,BTABLE    ;point to baud factor table
  1633.     DAD    D        ;index to proper factor
  1634.     MOV    A,M        ;factor in 'A'
  1635.     LHLD    RCNT        ;get number of records
  1636.     CALL    DIVHLA        ;divide HL by value in a (records/min)
  1637.     PUSH    H
  1638. ;
  1639.      IF    LOGCAL
  1640.     SHLD    PGSIZE
  1641.      ENDIF            ;LOGCAL
  1642. ;    
  1643.     MVI    H,0
  1644. ;
  1645.      IF    MAXTIM
  1646.     MOV    A,L        ;If limiting send time,
  1647.     PUSH    H
  1648.     LXI    H,TON
  1649.     ADD    M        ;add time on to xfer time, TON will al-
  1650.     POP    H        ;  ways be 0 if TIMEON is NO
  1651.     STA    MINUTE        ;store value for later comparison
  1652.      ENDIF            ;MAXTIM
  1653. ;
  1654.     CALL    DECOUT        ;print decimal number of minutes
  1655.     CALL    ILPRT
  1656.     DB    ' mins, ',0
  1657.     LXI    H,RECTBL    ;point to divisors for seconds calc.
  1658.     LXI    D,0
  1659.     CALL    SPEED        ;get speed indicator
  1660.     MOV    E,A
  1661.     DAD    D        ;index into table
  1662.     MOV    A,M        ;get multiplier
  1663.     POP    H        ;get remainder
  1664.     CALL    MULHA        ;multiply 'H' by 'A'
  1665.     CALL    SHFTHL
  1666.     CALL    SHFTHL
  1667.     CALL    SHFTHL
  1668.     CALL    SHFTHL
  1669.     MVI    H,0
  1670.     CALL    DECOUT        ;print the seconds portion
  1671.     CALL    ILPRT
  1672.     DB    ' secs at ',0
  1673.     LXI    H,SPTBL        ;start of baud rate speeds
  1674.     MVI    D,0        ;zero the 'D' register
  1675.     CALL    SPEED        ;get speed indicator
  1676.     ADD    A        ;index into the baud rate table
  1677.     ADD    A
  1678.     MOV    E,A        ;now have the index factor in 'DE'
  1679.     DAD    D        ;add to 'HL'
  1680.     XCHG            ;put address in 'DE' regs.
  1681.     MVI    C,PRINT        ;show the baud
  1682.     CALL    BDOS
  1683.     CALL    ILPRT
  1684.     DB    ' bps',CR,LF,0
  1685. ;
  1686.      IF    MAXTIM
  1687.     LDA    MINUTE        ;get minute count
  1688.     CPI    MAXMIN+1    ;compare to MAXTIM value
  1689.     JNC    OVERTM        ;if greater than MAXTIM
  1690.      ENDIF            ;MAXTIM
  1691. ;
  1692.     CALL    ILPRT
  1693.     DB    'To cancel: use CTRL-X',CR,LF,0
  1694.     RET
  1695. ;...
  1696. ;
  1697. ;
  1698.      IF    MAXTIM
  1699. OVERTM: CALL    ILPRT
  1700.     DB    CR,LF,'++ XMODEM ABORTED - send time exceeds the ',0
  1701.     LXI    H,MAXMIN
  1702.     CALL    DECOUT
  1703.     CALL    ERXIT1
  1704.     DB    ' minutes allowed ++',BELL,'$'
  1705.      ENDIF            ;MAXTIM
  1706. ;.....
  1707. ;
  1708. ;
  1709. BTABLE: DB    5,13,20,24,30,48,0
  1710. RECTBL: DB    192,74,48,40,32,20,0
  1711. SPTBL:    DB    '110$','300$','450$','600$','710$','1200$'
  1712. ;...
  1713. ;
  1714. ;
  1715. ; ---> DIVHLA  Divides 'HL' by value in 'A'
  1716. ;           upon exit: L=quotient, H=remainder
  1717. ;
  1718. DIVHLA: PUSH    B
  1719.     MVI    B,8        ;shift factor to 'B'
  1720.     MOV    C,A        ;divisor to 'C'
  1721. ;
  1722. DIV2:    XRA    A        ;clear carry flag and accumulator
  1723.     DAD    H
  1724.     MOV    A,H
  1725.     SUB    C
  1726.     JM    DIV3        ;do not borrow on negative results
  1727.     MOV    H,A
  1728.     MOV    A,L
  1729.     ORI    1        ;borrow 1
  1730.     MOV    L,A
  1731. ;
  1732. DIV3:    DCR    B
  1733.     JNZ    DIV2
  1734.     POP    B
  1735.     RET
  1736. ;...
  1737. ;
  1738. ;
  1739. ; ---> MULHA  Multiply the value in 'H' by the value in 'A'
  1740. ;           Return with answer in 'HL'.
  1741. ;
  1742. MULHA:    MOV    B,A        ;put loop count in 'B'
  1743.     MVI    D,0
  1744.     MOV    E,H
  1745.     MOV    L,H
  1746.     MVI    H,0
  1747. ;
  1748. MULLP:    DCR    B
  1749.     RZ
  1750.     DAD    D
  1751.     JMP    MULLP
  1752.     RET
  1753. ;
  1754. ;
  1755. ; Shift the 'HL' pair one bit to the right
  1756. ;
  1757. SHFTHL: MOV    A,L
  1758.     RAR
  1759.     MOV    L,A
  1760.     ORA    A        ;clear the carry bit
  1761.     MOV    A,H
  1762.     RAR
  1763.     MOV    H,A
  1764.     RNC
  1765.     MVI    A,80H
  1766.     ORA    L
  1767.     MOV    L,A
  1768.     RET
  1769. ;.....
  1770. ;
  1771. ;
  1772. ; ---> CLOSFIL    Closes the received file
  1773. ;
  1774. CLOSFIL:LXI    D,FCB        ;point to file
  1775.     MVI    C,CLOSE        ;get function
  1776.     CALL    BDOS        ;close it
  1777.     INR    A        ;close ok?
  1778.     RNZ            ;  yes, return
  1779.     CALL    ERXIT        ;  no, abort
  1780.     DB    '++ Can''t close file ++$'
  1781. ;.....
  1782. ;
  1783. ;
  1784. ; ---> DECOUT  Decimal output routine - call with decimal value in 'HL'
  1785. ;
  1786. DECOUT: PUSH    B
  1787.     PUSH    D
  1788.     PUSH    H
  1789.     LXI    B,-10
  1790.     LXI    D,-1
  1791. ;
  1792. DECOU2: DAD    B
  1793.     INX    D
  1794.     JC    DECOU2
  1795.     LXI    B,10
  1796.     DAD    B
  1797.     XCHG
  1798.     MOV    A,H
  1799.     ORA    L
  1800.     CNZ    DECOUT
  1801.     MOV    A,E
  1802.     ADI    '0'
  1803.     CALL    CTYPE
  1804.     POP    H
  1805.     POP    D
  1806.     POP    B
  1807.     RET
  1808. ;.....
  1809. ;
  1810. ;
  1811. ; ---> DHXOUT  Double precision hex output routine.  Call with hex
  1812. ;           value in 'HL'.
  1813. ;
  1814. DHXOUT: PUSH    H        ;save HL
  1815.     PUSH    PSW        ;save a
  1816.     MOV    A,H        ;get MSBs byte
  1817.     CALL    HEXO        ;output high order byte
  1818.     MOV    A,L        ;get LSB byte
  1819.     CALL    HEXO        ;output low order byte
  1820.     POP    PSW        ;restore 'A' reg.
  1821.     POP    H        ;restore 'HL'
  1822.     RET            ;return to caller
  1823. ;
  1824. ;
  1825. ; ---> RDRECD  Reads a record
  1826. ;
  1827. ; For speed, this routine buffers up 16 records at a time.
  1828. ;
  1829. RDRECD: LDA    RECNBF        ;get number of records in buffer
  1830.     DCR    A        ;decrement it
  1831.     STA    RECNBF
  1832.     JM    RDBLOCK        ;exhausted?  need more
  1833.     LHLD    RECPTR        ;get buffer address
  1834.     LXI    D,128        ;add length of one record
  1835.     DAD    D        ;  to next buffer
  1836.     SHLD    RECPTR        ;save buffer address
  1837.     RET            ;from 'READRED'
  1838. ;.....
  1839. ;
  1840. ;
  1841. ; Buffer is empty - read in another block of 16
  1842. ;
  1843. RDBLOCK:LDA    EOFLG        ;get 'EOF' flag
  1844.     CPI    1        ;is it set?
  1845.     STC            ;to show 'EOF'
  1846.     RZ            ;got 'EOF'
  1847.     MVI    C,0        ;records in block
  1848.     LXI    D,DBUF        ;to disk buffer
  1849. ;
  1850. RDRECLP:PUSH    B
  1851.     PUSH    D
  1852.     MVI    C,SETDMA    ;set DMA address
  1853.     CALL    BDOS
  1854.     LXI    D,FCB
  1855.     MVI    C,READ
  1856.     CALL    BDOS
  1857.     POP    D
  1858.     POP    B
  1859.     ORA    A        ;read ok?
  1860.     JZ    RDRECOK        ;yes
  1861.     DCR    A        ;'EOF'?
  1862.     JZ    REOF        ;got 'EOF'
  1863. ;
  1864. ;
  1865. ; Read error
  1866. ;
  1867.     CALL    ERXIT
  1868.     DB    '++ File read error ++$'
  1869. ;...
  1870. ;
  1871. ;
  1872. RDRECOK:LXI    H,128        ;add length of one record
  1873.     DAD    D        ;  to next buffer
  1874.     XCHG            ;buffer to 'DE'
  1875.     INR    C        ;more records?
  1876.     MOV    A,C        ;get count
  1877.     CPI    BUFSIZ*8    ;done?
  1878.     JZ    RDBFULL        ;  yes, buffer is full
  1879.     JMP    RDRECLP        ;read more
  1880. ;...
  1881. ;
  1882. ;
  1883. REOF:    MVI    A,1
  1884.     STA    EOFLG        ;set EOF flag
  1885.     MOV    A,C
  1886. ;
  1887. ;
  1888. ; Buffer is full, or got eof
  1889. ;
  1890. RDBFULL:STA    RECNBF        ;store record count
  1891.     LXI    H,DBUF-128    ;init buffer pointear
  1892.     SHLD    RECPTR        ;save buffer address
  1893.     LXI    D,DEFDMA    ;reset DMA address
  1894.     MVI    C,SETDMA
  1895.     CALL    BDOS
  1896.     JMP    RDRECD        ;pass record to caller
  1897. ;.....
  1898. ;
  1899. ;
  1900. ; ---> WRRECD  Write a record
  1901. ;
  1902. ; Writes the record into a buffer.  When 16 have been written, writes
  1903. ; the block to disk.
  1904. ;
  1905. ; Entry point "WRBLOCK" flushes the buffer at EOF
  1906. ;
  1907. WRRECD: LHLD    RECPTR        ;get buffer address
  1908.     LXI    D,128        ;add length of one record
  1909.     DAD    D        ;  to next buffer
  1910.     SHLD    RECPTR        ;save buffer address
  1911.     LDA    RECNBF        ;bump the
  1912.     INR    A        ;  record number
  1913.     STA    RECNBF        ;  in the buffer
  1914.     CPI    BUFSIZ*8    ;have we 16?
  1915.     RNZ            ;no, return
  1916. ;
  1917. ;
  1918. ; ---> WRBLOCK    Writes a block to disk
  1919. ;
  1920. WRBLOCK:LDA    RECNBF        ;number of records in the buffer
  1921.     ORA    A        ;0 means end of file
  1922.     RZ            ;none to write
  1923.     MOV    C,A        ;save count
  1924.     LXI    D,DBUF        ;point to disk buff
  1925. ;
  1926. DKWRLP: PUSH    H
  1927.     PUSH    D
  1928.     PUSH    B
  1929.     MVI    C,SETDMA    ;set DMA
  1930.     CALL    BDOS        ;to buffer
  1931.     LXI    D,FCB        ;then write the block
  1932.     MVI    C,WRITE
  1933.     CALL    BDOS
  1934.     POP    B
  1935.     POP    D
  1936.     POP    H
  1937.     ORA    A
  1938.     JNZ    WRERR        ;oops, error
  1939.     LXI    H,128        ;length of 1 record
  1940.     DAD    D        ;'HL'= next buff
  1941.     XCHG            ;to 'DE' for setdma
  1942.     DCR    C        ;more records?
  1943.     JNZ    DKWRLP        ;  yes, loop
  1944.     XRA    A        ;get a zero
  1945.     STA    RECNBF        ;reset number of records
  1946.     LXI    H,DBUF        ;reset buffer buffer
  1947.     SHLD    RECPTR        ;save buffer address
  1948. ;
  1949. RSDMA:    LXI    D,DEFDMA    ;reset DMA address
  1950.     MVI    C,SETDMA
  1951.     CALL    BDOS
  1952.     RET
  1953. ;.....
  1954. ;
  1955. ;
  1956. WRERR:    CALL    RSDMA        ;reset DMA to normal
  1957.     MVI    C,CAN        ;cancel
  1958.     CALL    SEND        ;  sender
  1959.     CALL    RCVSABT        ;kill receive file
  1960.     CALL    ERXIT        ;exit with msg:
  1961.     DB    '++ Error writing file ++$'
  1962. ;.....
  1963. ;
  1964. ;
  1965. ;----> RECV  Receive a character
  1966. ;
  1967. ; Timeout time is in 'B' in seconds.  Entry via 'RECVDG' deletes garbage
  1968. ; characters on the line.  For example, having just sent a record,
  1969. ; calling 'RECVDG' will delete any line-noise-induced characters "long"
  1970. ; before the ACK/NAK would be received.
  1971. ;
  1972. RECVDG: CALL    GETCHR
  1973.     CALL    GETCHR
  1974. ;
  1975. RECV:    PUSH    D        ;save 'DE' regs.
  1976.     MVI    E,MHZ        ;get the clock speed
  1977.     XRA    A        ;clear the 'A' reg.
  1978. ;
  1979. MSLOOP: ADD    B        ;number of seconds
  1980.     DCR    E        ;one less mhz. to go
  1981.     JNZ    MSLOOP        ;if not zero, continue
  1982.     MOV    B,A        ;put total value back into 'B'
  1983. ;
  1984. MSEC:    LXI    D,6600        ;1 second DCR count
  1985. ;
  1986. MWTI:    CALL    RCVRDY        ;input from modem ready
  1987. ;
  1988.     JZ    MCHAR        ;got the character
  1989.     DCR    E        ;count down for timeout
  1990.     JNZ    MWTI
  1991.     DCR    D
  1992.     JNZ    MWTI
  1993.     DCR    B        ;more seconds?
  1994.     JNZ    MSEC        ;yes, wait
  1995. ;
  1996. ;
  1997. ; Test for the presence of carrier - if none, go to 'CARCK' and continue
  1998. ; testing for specified time.  If carrier returns, continue.  If it does
  1999. ; not return, exit.
  2000. ;
  2001.     CALL    CAROK        ;is carrier still on?
  2002.     CNZ    CARCK        ;if not, test for 15 seconds
  2003. ;
  2004. ;
  2005. ; Modem timed out receiving - but carrier is still on.
  2006. ;
  2007.     POP    D        ;restore 'DE'
  2008.     STC            ;carry shows timeout
  2009.     RET
  2010. ;.....
  2011. ;
  2012. ;
  2013. ; Get character from modem.
  2014. ;
  2015. MCHAR:    CALL    MDIN        ;get data byte from modem
  2016.     POP    D        ;restore 'DE'
  2017. ;
  2018. ;
  2019. ; Calculate Checksum and CRC
  2020. ;
  2021.     PUSH    PSW        ;save the character
  2022.     CALL    UPDCRC        ;calculate CRC
  2023.     ADD    C        ;add to checksum
  2024.     MOV    C,A        ;save checksum
  2025.     POP    PSW        ;restore the character
  2026.     ORA    A        ;carry off: no error
  2027.     RET            ;from 'RECV'
  2028. ;.....
  2029. ;
  2030. ;
  2031. ; CARCK - common carrier test for receive and send.  If carrier returns
  2032. ; within TIMOUT seconds, normal program execution continues.  Else, it
  2033. ; will abort to CP/M via EXIT.
  2034. ;
  2035. CARCK:    MVI    E,TIMOUT*10    ;value for 15 second delay
  2036. ;
  2037. CARCK1: CALL    DELAY        ;kill .1 seconds
  2038.     CALL    CAROK        ;is carrier still on?
  2039.     RZ            ;return if carrier on
  2040.     DCR    E        ;has 15 seconds expired?
  2041.     JNZ    CARCK1        ;if not, continue testing
  2042. ;
  2043. ;
  2044. ; See if got a local console, and report if so.
  2045. ;
  2046.      IF    NOT USECON    ;do not test if guaranteed conout
  2047.     LHLD    CONOUT+1    ;get conout address
  2048.     MOV    A,H        ;zero if no local console
  2049.     ORA    L
  2050.     JZ    CARCK2
  2051.      ENDIF            ;NOT USECON
  2052. ;
  2053.     MVI    A,1        ;print local only
  2054.     STA    CONONL
  2055.     CALL    ILPRT        ;report loss of carrier
  2056.     DB    CR,LF,'++ Carrier lost in XMODEM ++',CR,LF,0
  2057. ;
  2058. CARCK2: LDA    OPTSAV        ;get option
  2059.     CPI    'R'        ;if not receive
  2060.     JNZ    EXIT        ;then abort now, else
  2061.     CALL    DELFILE        ;get rid of the junk first
  2062.     JMP    EXIT        ;else, abort to CP/M
  2063. ;
  2064. ;
  2065. ; Delay - 100 millisecond delay.
  2066. ;
  2067. DELAY:    PUSH    B        ;save 'BC'
  2068.     LXI    B,MHZ*4167    ;value for 100 ms. delay
  2069. ;
  2070. DELAY2: DCX    B        ;update count
  2071.     MOV    A,B        ;get MSP byte
  2072.     ORA    C        ;count = zero?
  2073.     JNZ    DELAY2        ;if not, continue
  2074.     POP    B        ;restore 'BC'
  2075.     RET            ;return to CARCK1
  2076. ;.....
  2077. ;
  2078. ;
  2079. ; ---> SEND  Send a character to the modem
  2080. ;
  2081. SEND:    PUSH    PSW        ;save the character
  2082.     CALL    UPDCRC        ;calculate CRC
  2083.     ADD    C        ;calcculate checksum
  2084.     MOV    C,A        ;save cksum
  2085. ;
  2086. SENDW:    CALL    SNDRDY        ;is transmit ready
  2087.     JZ    SENDR        ;  yes, go send
  2088. ;
  2089. ;
  2090. ; Xmit status not ready, so test for carrier before looping - if lost,
  2091. ; go to CARCK and give it up to 15 seconds to return.  If it doesn't,
  2092. ; return abort via EXIT.
  2093. ;
  2094.     PUSH    D        ;save 'DE'
  2095.     CALL    CAROK        ;is carrier still on?
  2096.     CNZ    CARCK        ;if not, continue testing it
  2097.     POP    D        ;restore 'DE'
  2098.     JMP    SENDW        ;else, wait for xmit ready
  2099. ;
  2100. ;
  2101. ; ---> WAITNAK    Waits for initial NAK
  2102. ;
  2103. ; To ensure no data is sent until the receiving program is ready, this
  2104. ; routine waits for the first timeout-nak or the letter 'C' for CRC
  2105. ; from the receiver.  If CRC is in effect, then Cyclic Redundancy Checks
  2106. ; are used instead of checksums.  'E' contains the number of seconds to
  2107. ; wait.
  2108. ;
  2109. ; If the first character received is a CAN (CTL-X) then the send will be
  2110. ; aborted as though it had timed out.
  2111. ;
  2112. WAITNAK:MVI    B,1        ;timeout delay
  2113.     CALL    RECV        ;did we get
  2114.     CPI    CRC        ;'CRC' indicated?
  2115.     RZ            ;yes, send block
  2116.     CPI    NAK        ;a 'NAK' indicating checksum?
  2117.     JZ    SETNAK        ;yes go put checksum in effect
  2118.     CPI    CAN        ;was it a cancel (CTL-X)?
  2119.     JZ    ABORT        ;yes, abort
  2120.     DCR    E        ;finished yet?
  2121.     JZ    ABORT        ;yes, abort
  2122.     JMP    WAITNAK        ;no, loop
  2123. ;.....
  2124. ;
  2125. ;
  2126. ; ---> WAITCRC    Turn on CRC flag
  2127. ;
  2128. SETNAK: MVI    A,'C'        ;make sure in checksum
  2129.     STA    CRCFLG
  2130.     RET
  2131. ;.....
  2132. ;
  2133. ;
  2134. ; ---> MOVEFCB    Moves the filename to the FCB
  2135. ;
  2136. ; This routine moves the filename from the default command line buffer
  2137. ; to the file control block (FCB).
  2138. ;
  2139. MOVEFCB:LHLD    SAVEHL        ;get position on command line
  2140.     CALL    GETB        ;get numeric position
  2141.     LXI    D,FCB+1
  2142.     CALL    MOVENAM        ;move name to FCB
  2143.     XRA    A
  2144.     STA    FCBRNO        ;zero record number
  2145.     STA    FCBEXT        ;zero extent
  2146.     LDA    OPTSAV        ;this going to be a library file?
  2147.     CPI    'L'
  2148.     RNZ            ;if not, finished
  2149. ;
  2150. ;
  2151. ; Handles library entries, first checks for proper .LBR extent.     If no
  2152. ; extent was included, it adds one itself.
  2153. ;
  2154.     SHLD    SAVEHL
  2155.     LXI    H,FCB+9        ;1st extent character
  2156.     MOV    A,M
  2157.     CPI    ' '
  2158.     JZ    NOEXT        ;no extent, make one
  2159.     CPI    'L'        ;check 1st character in extent
  2160.     JNZ    LBRERR
  2161.     INX    H        
  2162.     MOV    A,M
  2163.     CPI    'B'        ;check 2nd character in extent
  2164.     JNZ    LBRERR
  2165.     INX    H
  2166.     MOV    A,M
  2167.     CPI    'R'        ;check 3rd character in extent
  2168.     JNZ    LBRERR
  2169. ;...
  2170. ;
  2171. ;
  2172. ; Get the name of the desired file in the library
  2173. ;
  2174. MOVEF1: LHLD    SAVEHL        ;get current position on command line
  2175.     CALL    CHKMSP        ;see if valid library member file name
  2176.     INR    B        ;increment for move name
  2177.     LXI    D,MEMFCB    ;store member name in special buffer
  2178.     JMP    MOVENAM        ;move from command line to buffer, done
  2179. ;.....
  2180. ;
  2181. ;
  2182. ; Check for any spaces prior to library member file name, if none (or
  2183. ; only spaces remaining), no name.
  2184. ;
  2185. CHKMSP: DCR    B
  2186.     JZ    MEMERR
  2187.     MOV    A,M
  2188.     CPI    ' '+1
  2189.     RNC
  2190.     INX    H
  2191.     JMP    CHKMSP
  2192. ;.....
  2193. ;
  2194. ;
  2195. ; Gets the count of characters remaining on the command line
  2196. ;
  2197. GETB:    MOV    A,L
  2198.     SUI    DEFDMA+2    ;start location of 1st command
  2199.     MOV    B,A        ;store for now
  2200.     LDA    DEFDMA        ;find length of command line
  2201.     SUB    B        ;subtract those already used
  2202.     MOV    B,A        ;now have number of bytes remaining
  2203.     RET
  2204. ;.....
  2205. ;
  2206. ;
  2207. LBRERR: CALL    ERXIT
  2208.     DB    '++ Invalid library name ++$'
  2209. ;.....
  2210. ;
  2211. ;
  2212. MEMERR: CALL    ILPRT
  2213.     DB    CR,LF,'++ No library member file requested ++',CR,LF,0
  2214.     JMP    OPTNERR
  2215. ;.....
  2216. ;
  2217. ;
  2218. ; Add .LBR extent to the library file name
  2219. ;
  2220. NOEXT:    LXI    H,FCB+9        ;location of extent
  2221.     MVI    M,'L'
  2222.     INX    H
  2223.     MVI    M,'B'
  2224.     INX    H
  2225.     MVI    M,'R'
  2226.     JMP    MOVEF1        ;now get the library member name
  2227. ;.....
  2228. ;
  2229. ;
  2230. ; Move a file name from the 'DEFDMA' command line buffer into FCB
  2231. ;
  2232. MOVENAM:MVI    C,1
  2233. ;
  2234. MOVEN1: MOV    A,M
  2235.     CPI    ' '+1        ;name ends with space or return
  2236.     JC    FILLSP        ;fill with spaces if needed
  2237.     CPI    '.'
  2238.     JZ    CHKFIL        ;file name might be less than 8 chars.
  2239.     STAX    D        ;store
  2240.     INX    D        ;next position to store the character
  2241.     INR    C        ;one less to go
  2242.     MOV    A,C
  2243.     CPI    12+1
  2244.     JNC    NONAME        ;11 chars. maximum filename plus extent
  2245. ;
  2246. MOVEN2: INX    H        ;next char. in file name
  2247.     DCR    B
  2248.     JZ    OPTNERR        ;end of name, see if done yet
  2249.     JMP    MOVEN1
  2250. ;.....
  2251. ;
  2252. ;
  2253. ; See if any spaces needed between file name and .ext
  2254. ;
  2255. CHKFIL: CALL    FILLSP        ;fill with spaces
  2256.     JMP    MOVEN2
  2257. ;.....
  2258. ;
  2259. ;
  2260. FILLSP: MOV    A,C
  2261.     CPI    9
  2262.     RNC            ;up to 1st character in .ext now
  2263.     MVI    A,' '        ;be sure there is a blank there now
  2264.     STAX    D
  2265.     INR    C
  2266.     INX    D
  2267.     JMP    FILLSP        ;go do another
  2268. ;.....
  2269. ;
  2270. ;
  2271. CTYPE:    PUSH    B        ;save all registers
  2272.     PUSH    D
  2273.     PUSH    H
  2274.     MOV    E,A        ;character to 'E' in case BDOS (normal)
  2275.     LDA    CONONL        ;want to bypass 'BYE' output to modem?
  2276.     ORA    A
  2277.     JNZ    CTYPEL        ;yes, go directly to CRT, then
  2278.     MVI    C,WRCON        ;BDOS console output, to CRT and modem
  2279.     CALL    BDOS        ;   since 'BYE' intercepts the char.
  2280.     POP    H        ;restore all registers
  2281.     POP    D
  2282.     POP    B
  2283.     RET
  2284. ;...
  2285. ;
  2286. ;
  2287. CTYPEL: MOV    C,E        ;BIOS needs it in 'C'
  2288.     CALL    CONOUT        ;BIOS console output routine, not BDOS
  2289.     POP    H        ;restore all registers saved by 'CTYPE'
  2290.     POP    D
  2291.     POP    B
  2292.     RET
  2293. ;.....
  2294. ;
  2295. ;
  2296. HEXO:    PUSH    PSW        ;save for right digit
  2297.     RAR            ;right justify the left digit
  2298.     RAR
  2299.     RAR
  2300.     RAR
  2301.     CALL    NIBBL        ;print left digit
  2302.     POP    PSW        ;restore right
  2303. ;
  2304. NIBBL:    ANI    0FH        ;isolate digit
  2305.     ADI    90H
  2306.     DAA
  2307.     ACI    40H
  2308.     DAA
  2309.     JMP    CTYPE        ;type it
  2310. ;.....
  2311. ;
  2312. ;
  2313. ; ---> ILPRT  Inline print of message
  2314. ;
  2315. ; The call to ILPRT is followed by a message, binary 0 for its end.
  2316. ;
  2317. ILPRT:    XTHL            ;save HL, get HL=message
  2318. ;
  2319. ILPLP:    MOV    A,M        ;get the character
  2320.     ORA    A        ;end of message?
  2321.     JZ    ILPRET        ;  yes, return
  2322.     CALL    CTYPE        ;type the message
  2323.     INX    H        ;to next character
  2324.     JMP    ILPLP        ;loop
  2325. ;...
  2326. ;
  2327. ;
  2328. ILPRET: XTHL            ;restore HL
  2329.     RET            ;past message
  2330. ;.....
  2331. ;
  2332. ;
  2333. EXITLG:     IF    LOGCAL        ;special log caller exit
  2334.     JMP    LOGCALL
  2335.      ENDIF            ;LOGCAL
  2336. ;
  2337.     JMP    EXIT
  2338. ;.....
  2339. ;
  2340. ;
  2341. ; ---> ERXIT  Exit printing message following call
  2342. ;
  2343. ERXIT:    CALL    ILPRT
  2344.     DB    CR,LF,0
  2345. ;
  2346. ERXIT1: POP    D        ;get message
  2347.     MVI    C,PRINT        ;get BDOS FNC
  2348.     CALL    BDOS        ;print message
  2349.     CALL    ILPRT
  2350.     DB    CR,LF,0
  2351. ;
  2352. EXIT:     IF    TIMEON
  2353.     CALL    TIME        ;tell user how long he's been on
  2354.      ENDIF            ;TIMEON
  2355. ;
  2356.     CALL    UNINIT        ;reset vectors (if needed)
  2357.     LDA    OLDDRV        ;restore the original drive
  2358.     MOV    E,A
  2359.     CALL    RECDRX
  2360.     LDA    OLDUSR        ;restore the original number
  2361.     MOV    E,A
  2362.     CALL    RECARE
  2363.     XRA    A
  2364.     LHLD    STACK
  2365.     SPHL
  2366.     RET
  2367. ;.....
  2368. ;
  2369. ;
  2370. ; ---> Restore the old user area and drive from a received file
  2371. ;
  2372. ; ---> Set user area to receive file
  2373. ;
  2374. RECAREA:CALL    RECDRV        ;ok set the drive to its place
  2375.     LDA    PRVTFL        ;private area wanted?
  2376.     ORA    A
  2377.     MVI    E,PRUSR        ;yes, set to private area
  2378.     JNZ    RECARE
  2379.     MVI    E,USR        ;ok now set the user area
  2380. ;
  2381. RECARE: MVI    C,USER        ;tell BDOS what we want to do
  2382.     CALL    BDOS        ;now do it
  2383.     RET
  2384. ;.....
  2385. ;
  2386. ;
  2387. RECDRV: LDA    PRVTFL
  2388.     ORA    A
  2389.     MVI    E,PRDRV-'A'    ;make drive CP/M number
  2390.     JNZ    RECDRX
  2391.     MVI    E,DRV-'A'    ;make drive CP/M number
  2392. ;
  2393. RECDRX: MVI    C,SELDRV    ;tell BDOS
  2394.     CALL    BDOS        ;do it
  2395.     RET            ;back
  2396. ;.....
  2397. ;
  2398. ;
  2399. ; Move 128 characters from 'HL' to 'DE' length in 'B'
  2400. ;
  2401. MOVE128: MVI    B,128        ;set move count
  2402. ;
  2403. MOVE:    MOV    A,M        ;get a character
  2404.     STAX    D        ;store it
  2405.     INX    H        ;to next 'from'
  2406.     INX    D        ;to next 'to'
  2407.     DCR    B        ;more?
  2408.     JNZ    MOVE        ;  yes, loop
  2409.     RET            ;  no, return
  2410. ;.....
  2411. ;
  2412. ;
  2413. ;***********************************************************************
  2414. ;
  2415. ;            CRC SUBROUTINES
  2416. ;
  2417. ;***********************************************************************
  2418. ;
  2419. ;
  2420. CHKCRC: PUSH    H        ;check 'CRC' bytes of received message
  2421.     LHLD    CRCVAL
  2422.     MOV    A,H
  2423.     ORA    L
  2424.     POP    H
  2425.     RZ
  2426.     MVI    A,0FFH
  2427.     RET
  2428. ;.....
  2429. ;
  2430. ;
  2431. CLRCRC: PUSH    H        ;reset 'CRC' store for a new message
  2432.     LXI    H,0
  2433.     SHLD    CRCVAL
  2434.     POP    H
  2435.     RET
  2436. ;.....
  2437. ;
  2438. ;
  2439. FINCRC: PUSH    PSW        ;finish 'CRC' calculation for final xmsn
  2440.     XRA    A
  2441.     CALL    UPDCRC
  2442.     CALL    UPDCRC
  2443.     PUSH    H
  2444.     LHLD    CRCVAL
  2445.     MOV    D,H
  2446.     MOV    E,L
  2447.     POP    H
  2448.     POP    PSW
  2449.     RET
  2450. ;.....
  2451. ;
  2452. ;
  2453. UPDCRC: PUSH    PSW        ;update 'CRC' store  with byte in 'A'
  2454.     PUSH    B
  2455.     PUSH    H
  2456.     MVI    B,8
  2457.     MOV    C,A
  2458.     LHLD    CRCVAL
  2459. ;
  2460. UPDLOOP:MOV    A,C
  2461.     RLC
  2462.     MOV    C,A
  2463.     MOV    A,L
  2464.     RAL
  2465.     MOV    L,A
  2466.     MOV    A,H
  2467.     RAL
  2468.     MOV    H,A
  2469.     JNC    SKIPIT
  2470.     MOV    A,H        ;the generator is x^16 + x^12 + x^5 + 1
  2471.     XRI    10H
  2472.     MOV    H,A
  2473.     MOV    A,L
  2474.     XRI    21H
  2475.     MOV    L,A
  2476. ;
  2477. SKIPIT: DCR    B
  2478.     JNZ    UPDLOOP
  2479.     SHLD    CRCVAL
  2480.     POP    H
  2481.     POP    B
  2482.     POP    PSW
  2483.     RET
  2484. ;.....
  2485. ;
  2486. ;              end of CRC routines
  2487. ;***********************************************************************
  2488. ;            start of LOGCAL routine
  2489. ;
  2490.          IF    LOGCAL
  2491. BSIZE:        EQU    80H
  2492. SECT:        EQU    80H
  2493. ;
  2494. ;
  2495. ; The following allocations are used by the 'FILE' macros
  2496. ;
  2497. DEFAULT$USER:    DB    LASTUSR
  2498. CUR$USER:    DB    0FFH
  2499. DEFAULT$DISK:    DB    LOGDRV-'A'
  2500. CUR$DISK:    DB    0FFH
  2501. PGSIZE:        DB    0,0
  2502. ;
  2503. LOGCALL:    JMP    M010
  2504. ;
  2505. FCBCALLER:    DB    0,'LASTCALR'
  2506.          ENDIF            ;LOGCAL
  2507. ;
  2508.          IF    LOGCAL AND OLDRBBS
  2509.         DB    '   ',0
  2510.          ENDIF            ;LOCAL AND OLDRBBS
  2511. ;
  2512.          IF    LOGCAL AND (NOT OLDRBBS)
  2513.         DB    'DAT',0
  2514.          ENDIF            ;LOGCAL AND (NOT OLDRBBS)
  2515. ;
  2516.          IF    LOGCAL
  2517.         DS    23
  2518.         DB    0FFH
  2519. ;
  2520. CALLERADR:    DW    DBUF
  2521. CALLERSIZ:    EQU    BSIZE
  2522. CALLERLEN:    DW    BSIZE
  2523. CALLERPTR:    DS    2
  2524. M010:        JMP    M001
  2525. ;
  2526. GETCALLER:
  2527.     LHLD    CALLERLEN
  2528.     XCHG
  2529.     LHLD    CALLERPTR
  2530.     MOV    A,L
  2531.     SUB    E
  2532.     MOV    A,H
  2533.     SBB    D
  2534.     JC    M007
  2535.     LXI    H,0
  2536.     SHLD    CALLERPTR
  2537. ;
  2538. M004:    XCHG
  2539.     LHLD    CALLERLEN
  2540.     MOV    A,E
  2541.     SUB    L
  2542.     MOV    A,D
  2543.     SBB    H
  2544.     JNC    M006
  2545.     LHLD    CALLERADR
  2546.     DAD    D
  2547.     XCHG
  2548.     MVI    C,SETDMA
  2549.     CALL    BDOS
  2550.     LDA    FCBCALLER+36
  2551.     CPI    0FFH
  2552.     JZ    M009
  2553.     MVI    C,USER
  2554.     MOV    E,A
  2555.     CALL    BDOS
  2556. ;
  2557. M009:    LXI    D,FCBCALLER
  2558.     MVI    C,RRDM
  2559.     CALL    BDOS
  2560.     CALL    RESET$SYSTEM
  2561.     ORA    A
  2562.     JNZ    M005
  2563.     LHLD    FCBCALLER+33
  2564.     INX    H
  2565.     SHLD    FCBCALLER+33
  2566.     LXI    D,SECT
  2567.     LHLD    CALLERPTR
  2568.     DAD    D
  2569.     SHLD    CALLERPTR
  2570.     JMP    M004
  2571. ;
  2572. M005:    LHLD    CALLERPTR
  2573.     SHLD    CALLERLEN
  2574. ;
  2575. M006:    LXI    D,DEFDMA
  2576.     MVI    C,SETDMA
  2577.     CALL    BDOS
  2578.     LXI    H,0
  2579.     SHLD    CALLERPTR
  2580. ;
  2581. M007:    XCHG
  2582.     LHLD    CALLERADR
  2583.     DAD    D
  2584.     XCHG
  2585.     LHLD    CALLERLEN
  2586.     MOV    A,L
  2587.     ORA    H
  2588.     MVI    A,EOF
  2589.     RZ
  2590.     LDAX    D
  2591.     LHLD    CALLERPTR
  2592.     INX    H
  2593.     SHLD    CALLERPTR
  2594.     RET
  2595. ;.....
  2596. ;
  2597. ;
  2598. OPENF:    PUSH    D    
  2599.     MVI    A,0FFH        ;declare current user area on file
  2600.     STA    FILEUA
  2601.     MVI    C,VERNO        ;get version number
  2602.     CALL    BDOS
  2603.     MOV    A,H        ;cp/m 1.x?
  2604.     ORA    L
  2605.     JZ    START2$DISK    ;check for default disk if so
  2606.     MVI    E,0FFH        ;get current user number
  2607.     MVI    C,USER        ;get user code
  2608.     CALL    BDOS
  2609.     MOV    C,A
  2610.     LDA    DEFAULT$USER    ;check if at default user
  2611.     CMP    C
  2612.     JZ    START2$DISK    ;do not try if at default user area
  2613.     STA    FILEUA        ;where the file is if anywhere
  2614.     MOV    E,A
  2615.     MOV    A,C
  2616.     STA    CUR$USER    ;where we are (save for later)
  2617.     MVI    C,USER        ;set user code to default user
  2618.     CALL    BDOS
  2619. ;    
  2620. START2$DISK:
  2621.     MVI    C,CURDRV    ;see if current disk is default drive
  2622.     CALL    BDOS
  2623.     MOV    C,A
  2624.     LDA    DEFAULT$DISK    ;check if at default disk
  2625.     CMP    C
  2626.     POP    H        ;FCB into HL
  2627.     PUSH    H        ;preserve stack
  2628.     JZ    START3$DISK
  2629.     INR    A        ;add one to disk number
  2630.     MOV    M,A        ;put into FCB
  2631. ;
  2632. START3$DISK:
  2633.     XCHG            ;FCB into DE
  2634.     MVI    C,OPEN        ;open file
  2635.     CALL    BDOS
  2636.     CPI    255        ;not present?
  2637. ;
  2638. M012:    POP    D        ;get the FCB again (and clean up stack)
  2639.     PUSH    PSW        ;save open status on file
  2640.     LXI    H,36
  2641.     DAD    D
  2642.     LDA    FILEUA        ;get the user area for the file
  2643.     MOV    M,A        ;put user area into FCB
  2644.     POP    PSW
  2645.     RET
  2646. ;.....
  2647. ;
  2648. ;
  2649. M001:    XRA    A
  2650.     STA    FCBCALLER+12
  2651.     STA    FCBCALLER+32
  2652.     LXI    H,CALLERSIZ
  2653.     SHLD    CALLERLEN
  2654.     SHLD    CALLERPTR
  2655.     LXI    D,FCBCALLER
  2656.     JMP    M011
  2657. ;.....
  2658. ;
  2659. ;
  2660. RESET$SYSTEM:
  2661.     PUSH    PSW
  2662.     LDA    CUR$USER    ;check user
  2663.     CPI    0FFH        ;0FFH=no change
  2664.     JZ    RESET$RET
  2665.     MOV    E,A        ;user in 'E' reg.
  2666.     MVI    C,USER        ;get/set user code
  2667.     CALL    BDOS
  2668. ;
  2669. RESET$RET:
  2670.     POP    PSW
  2671.     RET
  2672. ;.....
  2673. ;
  2674. ;
  2675. FILEUA: DS    1
  2676. ;
  2677. M011:    CALL    OPENF
  2678.     JNZ    M003
  2679.     CALL    ERXIT
  2680.     DB    '++ NO LASTCALR ++$'
  2681.      ENDIF            ;LOGCAL
  2682. ;
  2683.      IF    LOGCAL AND (NOT OLDRBBS)
  2684.     DB    '.DAT'
  2685.      ENDIF            ;LOGCAL AND (NOT OLDRBBS)
  2686. ;
  2687.      IF    LOGCAL
  2688.     DB    ' FILE FOUND ++'
  2689. ;
  2690. M003:    MVI    C,SETRRD    ;get random record #
  2691.     LXI    D,FCBCALLER
  2692.     CALL    BDOS
  2693.     CALL    RESET$SYSTEM
  2694.     MVI    A,LOGUSR
  2695.     STA    DEFAULT$USER
  2696.     JMP    M022
  2697. ;...
  2698. ;
  2699. ;
  2700. FCBLOG: DB    0,'XMODEM  LOG',0
  2701.     DS    23
  2702.     DB    0FFH
  2703. ;
  2704. LOGADR: DW    LOGBUF
  2705. LOGSIZ: EQU    BSIZE
  2706. LOGLEN: DW    BSIZE
  2707. LOGPTR: DS    2
  2708. ;
  2709. M022:    JMP    M013
  2710. ;...
  2711. ;
  2712. ;
  2713. GETLOG: LHLD    LOGLEN
  2714.     XCHG
  2715.     LHLD    LOGPTR
  2716.     MOV    A,L
  2717.     SUB    E
  2718.     MOV    A,H
  2719.     SBB    D
  2720.     JC    M019
  2721.     LXI    H,0
  2722.     SHLD    LOGPTR
  2723. ;
  2724. M016:    XCHG
  2725.     LHLD    LOGLEN
  2726.     MOV    A,E
  2727.     SUB    L
  2728.     MOV    A,D
  2729.     SBB    H
  2730.     JNC    M018
  2731.     LHLD    LOGADR
  2732.     DAD    D
  2733.     XCHG
  2734.     MVI    C,SETDMA
  2735.     CALL    BDOS
  2736.     LDA    FCBLOG+36
  2737.     CPI    0FFH
  2738.     JZ    M021
  2739.     MVI    C,USER
  2740.     MOV    E,A
  2741.     CALL    BDOS
  2742. ;
  2743. M021:    LXI    D,FCBLOG
  2744.     MVI    C,RRDM
  2745.     CALL    BDOS
  2746.     CALL    RESET$SYSTEM
  2747.     ORA    A
  2748.     JNZ    M017
  2749.     LHLD    FCBLOG+33
  2750.     INX    H
  2751.     SHLD    FCBLOG+33
  2752.     LXI    D,SECT
  2753.     LHLD    LOGPTR
  2754.     DAD    D
  2755.     SHLD    LOGPTR
  2756.     JMP    M016
  2757. ;
  2758. M017:    LHLD    LOGPTR
  2759.     SHLD    LOGLEN
  2760. ;
  2761. M018:    LXI    D,DEFDMA
  2762.     MVI    C,SETDMA
  2763.     CALL    BDOS
  2764.     LXI    H,0
  2765.     SHLD    LOGPTR
  2766. ;
  2767. M019:    XCHG
  2768.     LHLD    LOGADR
  2769.     DAD    D
  2770.     XCHG
  2771.     LHLD    LOGLEN
  2772.     MOV    A,L
  2773.     ORA    H
  2774.     MVI    A,EOF
  2775.     RZ
  2776.     LDAX    D
  2777.     LHLD    LOGPTR
  2778.     INX    H
  2779.     SHLD    LOGPTR
  2780.     RET
  2781. ;.....
  2782. ;
  2783. ;
  2784. M013:    XRA    A
  2785.     STA    FCBLOG+12
  2786.     STA    FCBLOG+32
  2787.     LXI    H,LOGSIZ
  2788.     SHLD    LOGLEN
  2789.     SHLD    LOGPTR
  2790.     LXI    D,FCBLOG
  2791.     CALL    OPENF
  2792.     JNZ    M015
  2793.     MVI    A,EOF
  2794.     STA    LOGBUF
  2795.     LXI    H,0
  2796.     SHLD    LOGPTR
  2797.     LXI    D,FCBLOG
  2798.     MVI    C,MAKE
  2799.     CALL    BDOS
  2800.     INR    A
  2801.     JNZ    M015
  2802.     CALL    ERXIT
  2803.     DB    '++ NO DIR SPACE: LOG ++$'
  2804. ;...
  2805. ;
  2806. ;
  2807. BACKLOG:LXI    H,LOGSIZ
  2808.     SHLD    LOGLEN
  2809.     LHLD    LOGPTR
  2810.     MOV    A,L
  2811.     ORA    H
  2812.     RZ
  2813.     DCX    H
  2814.     SHLD    LOGPTR
  2815. ;
  2816. LLOG:    LHLD    FCBLOG+33
  2817.     MOV    A,L
  2818.     ORA    H
  2819.     RZ
  2820.     DCX    H
  2821.     SHLD    FCBLOG+33
  2822.     RET
  2823. ;.....
  2824. ;
  2825. ;
  2826. M015:    JMP    M023
  2827. ;
  2828. PUTLOG: PUSH    PSW
  2829.     LHLD    LOGLEN
  2830.     XCHG
  2831.     LHLD    LOGPTR
  2832.     MOV    A,L
  2833.     SUB    E
  2834.     MOV    A,H
  2835.     SBB    D
  2836.     JC    M029
  2837.     LXI    H,0
  2838.     SHLD    LOGPTR
  2839. ;
  2840. M026:    XCHG
  2841.     LHLD    LOGLEN
  2842.     MOV    A,E
  2843.     SUB    L
  2844.     MOV    A,D
  2845.     SBB    H
  2846.     JNC    M028
  2847.     LHLD    LOGADR
  2848.     DAD    D
  2849.     XCHG
  2850.     MVI    C,SETDMA
  2851.     CALL    BDOS
  2852.     LDA    FCBLOG+36
  2853.     CPI    0FFH
  2854.     JZ    M031
  2855.     MVI    C,USER
  2856.     MOV    E,A
  2857.     CALL    BDOS
  2858. ;
  2859. M031:    LXI    D,FCBLOG
  2860.     MVI    C,WRDM
  2861.     CALL    BDOS
  2862.     CALL    RESET$SYSTEM
  2863.     ORA    A
  2864.     JNZ    M027
  2865.     LHLD    FCBLOG+33
  2866.     INX    H
  2867.     SHLD    FCBLOG+33
  2868.     LXI    D,SECT
  2869.     LHLD    LOGPTR
  2870.     DAD    D
  2871.     SHLD    LOGPTR
  2872.     JMP    M026
  2873. ;
  2874. M027:    CALL    ERXIT
  2875.     DB    '++ DISK FULL - CANNOT ADD TO LOG ++$'
  2876. ;
  2877. M028:    LXI    D,DEFDMA
  2878.     MVI    C,SETDMA
  2879.     CALL    BDOS
  2880.     LXI    H,0
  2881.     SHLD    LOGPTR
  2882. ;
  2883. M029:    XCHG
  2884.     LHLD    LOGADR
  2885.     DAD    D
  2886.     XCHG
  2887.     POP    PSW
  2888.     STAX    D
  2889.     LHLD    LOGPTR
  2890.     INX    H
  2891.     SHLD    LOGPTR
  2892.     RET
  2893. ;.....
  2894. ;
  2895. ;
  2896. M023:    MVI    C,CFSIZE    ;get file length
  2897.     LXI    D,FCBLOG
  2898.     CALL    BDOS
  2899.     CALL    LLOG
  2900. ;
  2901. M030:    CALL    GETLOG
  2902.     CPI    EOF
  2903.     JNZ    M030
  2904.     CALL    BACKLOG
  2905.     CALL    RESET$SYSTEM
  2906.     POP    PSW        ;get option back
  2907.     CALL    PUTLOG
  2908.     CALL    SPEED        ;get speed factor
  2909.     ADI    30H
  2910.     CALL    PUTLOG
  2911.     LDA    PGSIZE        ;now the program size in minuntes..
  2912.     CALL    PNDEC        ;..of transfer time
  2913.     MVI    A,' '        ;blank
  2914.     CALL    PUTLOG
  2915. ;
  2916. ;
  2917. ; Log the drive and user area as a prompt
  2918. ;
  2919.     LDA    FCB
  2920.     ORA    A
  2921.     JNZ    WDRV
  2922.     MVI    C,CURDRV
  2923.     CALL    BDOS
  2924.     INR    A
  2925. ;
  2926. WDRV:    ADI    'A'-1
  2927.     CALL    PUTLOG
  2928.     MVI    C,USER        ;now the user area (as decimal number)
  2929.     MVI    E,0FFH
  2930.     CALL    BDOS
  2931.     CALL    PNDEC
  2932.     MVI    A,'>'        ;make it look like a prompt
  2933.     CALL    PUTLOG
  2934.     LDA    OPTSAV
  2935.     CPI    'L'
  2936.     JNZ    WDRV1
  2937.     LXI    H,MEMFCB    ;name of file in library
  2938.     MVI    B,11
  2939.     CALL    PUTSTR
  2940.     MVI    A,' '
  2941.     CALL    PUTLOG
  2942. ;
  2943. WDRV1:    LXI    H,FCB+1        ;now the name of the file
  2944.     MVI    B,11
  2945.     CALL    PUTSTR
  2946.     LDA    OPTSAV
  2947.     CPI    'L'
  2948.     JNZ    WDRV2
  2949.     MVI    C,1
  2950.     JMP    SPLOOP
  2951. ;
  2952. WDRV2:    MVI    C,13
  2953. ;
  2954. SPLOOP: PUSH    B
  2955.     MVI    A,' '
  2956.     CALL    PUTLOG
  2957.     POP    B
  2958.     DCR    C
  2959.     JNZ    SPLOOP
  2960. ;
  2961. CLOOP:    CALL    GETCALLER    ;and the caller
  2962.     CPI    EOF
  2963.     JZ    QUIT
  2964.     CPI    CR        ;do not print 2nd line of 'LASTCALR'
  2965.     JNZ    CLOP1
  2966.     CALL    PUTLOG
  2967.     MVI    A,LF
  2968.     CALL    PUTLOG        ;and add a LF
  2969.     JMP    QUIT
  2970. ;...
  2971. ;
  2972. ;
  2973. CLOP1:    CPI    ','        ;do not print the ',' between names
  2974.     JNZ    CLOP2
  2975.     MVI    A,' '        ;instead send a ' '
  2976. ;
  2977. CLOP2:    CALL    PUTLOG
  2978.     JMP    CLOOP
  2979. ;.....
  2980. ;
  2981. ;
  2982. PNDEC:    CPI    10        ;two column decimal format routine
  2983.     JC    ONE        ;one or two digits to area number?
  2984.     JMP    TWO
  2985. ;...
  2986. ;
  2987. ;
  2988. ONE:    PUSH    PSW
  2989.     MVI    A,'0'
  2990.     CALL    PUTLOG
  2991.     POP    PSW
  2992. ;
  2993. TWO:    MVI    H,0
  2994.     MOV    L,A
  2995. ;
  2996. DECOT:    PUSH    B
  2997.     PUSH    D
  2998.     PUSH    H
  2999.     LXI    B,-10
  3000.     LXI    D,-1
  3001. ;
  3002. DECOT2: DAD    B
  3003.     INX    D
  3004.     JC    DECOT2
  3005.     LXI    B,10
  3006.     DAD    B
  3007.     XCHG
  3008.     MOV    A,H
  3009.     ORA    L
  3010.     CNZ    DECOT
  3011.     MOV    A,E
  3012.     ADI    '0'
  3013.     CALL    PUTLOG
  3014.     POP    H
  3015.     POP    D
  3016.     POP    B
  3017.     RET
  3018. ;.....
  3019. ;
  3020. ;
  3021. PUTSTR: MOV    A,M
  3022.     PUSH    H
  3023.     PUSH    B
  3024.     CALL    PUTLOG
  3025.     POP    B
  3026.     POP    H
  3027.     INX    H
  3028.     DCR    B
  3029.     JNZ    PUTSTR
  3030.     RET
  3031. ;.....
  3032. ;
  3033. ;
  3034. QUIT:
  3035. M033:    LHLD    LOGPTR
  3036.     MOV    A,L
  3037.     ANI    (SECT-1) AND 0FFH
  3038.     JNZ    M034
  3039.     SHLD    LOGLEN
  3040. ;
  3041. M034:    MVI    A,EOF
  3042.     PUSH    PSW
  3043.     CALL    PUTLOG
  3044.     POP    PSW
  3045.     JNZ    M033
  3046.     LDA    FCBLOG+36
  3047.     CPI    0FFH
  3048.     JZ    M037
  3049.     MVI    C,USER
  3050.     MOV    E,A
  3051.     CALL    BDOS
  3052. ;
  3053. M037:    LXI    D,FCBLOG
  3054.     MVI    C,CLOSE
  3055.     CALL    BDOS
  3056.     CALL    RESET$SYSTEM
  3057.     INR    A
  3058.     JNZ    EXIT
  3059.     CALL    ERXIT
  3060.     DB    '++ CANNOT CLOSE LOG ++$'
  3061. ;
  3062. ;.....
  3063.      ENDIF            ;LOGCAL
  3064. ;
  3065. ;             end of LOGCAL routine
  3066. ;***********************************************************************
  3067. ;
  3068. ;        TIME   CALCULATE USERS ELAPSED TIME
  3069. ;
  3070. ;***********************************************************************
  3071. ;
  3072. ; Calculate time on system and inform user.  Log him off if =>MAXMIN
  3073. ; unless STATUS is non-zero.
  3074. ;
  3075.      IF TIMEON
  3076. TIME:    NOP            ;the sysop must add the code here neces-
  3077.     NOP            ;  sary to read his clock and store
  3078.     NOP            ;  binary values of current hours (0-23)
  3079.     NOP            ;  in CHOUR and minutes (0-59) in CMIN
  3080.     LXI    H,LHOUR        ;point to log-on hour
  3081.     LDA    CHOUR        ;current hour
  3082.     CMP    M        ;equal?
  3083.     JNZ    TIME1        ;no
  3084.     LDA    LMIN        ;log on minutes
  3085.     MOV    D,A
  3086.     LDA    CMIN        ;current minutes
  3087.     SUB    D
  3088.     STA    TON        ;store total time on
  3089.     JMP    TIME2
  3090. ;
  3091. TIME1:    LDA    LMIN        ;log on min
  3092.     MOV    D,A
  3093.     MVI    A,03CH        ;60 min into A
  3094.     SUB    D
  3095.     LXI    H,CMIN        ;point at current min
  3096.     ADD    M        ;add current minutes
  3097.     STA    TON
  3098. ;
  3099. TIME2:    LDA    STATUS        ;look at user status
  3100.     ORA    A        ;special user?
  3101.     JNZ    TIME3        ;yes, skip log off check
  3102.     XRA    A        ;clear status bits
  3103.     LDA    TON
  3104.     SUI    MAXMIN        ;subtract max time allowed
  3105.     JC    TIME3        ;still time left
  3106.     CALL    TIMEUP        ;time is up, inform user
  3107.     MVI    A,0CDH        ;alter jump vector
  3108.     STA    0        ;at zero
  3109.     JMP    0        ;and log him off
  3110. ;
  3111. TIME3:    LXI    H,MSG1+015H    ;point at message insert bytes
  3112.     LDA    TON        ;convert to ASCII
  3113.     MVI    B,-1
  3114. ;
  3115. TIME4:    INR    B
  3116.     SUI    0AH        ;subtract 10
  3117.     JNC    TIME4        ;until done
  3118.     ADI    0AH
  3119.     ORI    '0'        ;make ASCII
  3120.     MOV    M,A
  3121.     DCX    H
  3122.     MVI    A,'0'
  3123.     ADD    B
  3124.     MOV    M,A
  3125.     CALL    ILPRT
  3126. ;
  3127. MSG1:    DB    CR,LF,'Time on system is 00 minutes',CR,LF,0
  3128.     LDA    STATUS        ;check user status
  3129.     ORA    A        ;special user?
  3130.     JNZ    TIME5        ;yes, reset TON
  3131.     RET
  3132. ;
  3133. TIME5:    MVI    A,0        ;reset timeout for good guys
  3134.     STA    TON
  3135.     RET
  3136. ;
  3137. TIMEUP: CALL    ILPRT
  3138.     DB    CR,LF,CR,LF
  3139.     DB    'Your time is up - wait 24 hours to call back',CR,LF,0
  3140.     RET
  3141.      ENDIF
  3142. ;
  3143. TON:    DB    0        ;TON will always be 0 if TIMEON is NO    
  3144. ;    
  3145. ;
  3146. ;***********************************************************************
  3147. ;
  3148. ; Temporary storage area
  3149. ;
  3150. ;***********************************************************************
  3151. ;
  3152. MINUTE: DB    0        ;transfer time in mins for MAXTIM
  3153. MEMFCB: DB    '                '  ;library name (16 bytes required)
  3154. CONONL: DB    0        ;CTYPE console-only flag
  3155. CRCFLG: DB    0        ;sets to 'C' if checksum requested
  3156. CRCVAL: DB    0,0        ;current CRC value
  3157. DIRSZ:    DB    0,0        ;directory size
  3158. DUD:    DB    0        ;specified disk
  3159. DUSAVE: DB    0,0,0,0        ;buffer for drive/user
  3160. DUU:    DB    0        ;specified user
  3161. ERRCDE: DB    0        ;receive error code
  3162. ERRCT:    DB    0        ;error count
  3163. FRSTIM: DB    0        ;turned on after first 'SOH' received
  3164. INDEX:    DB    0,0        ;index into directory
  3165. MAXEXT: DB    0        ;highest ext. # seen in file size calc.
  3166. RCNT:    DB    0,0        ;record count
  3167. RCVRNO: DB    0        ;record number received
  3168. RECDNO: DB    0,0        ;current record number
  3169. OLDDRV: DB    0        ;save the original drive number
  3170. OLDUSR: DB    0        ;save the original user number
  3171. OPTSAV: DB    0        ;save option here for carrier loss
  3172. PRVTFL: DB    0        ;private user area option flag
  3173. SAVEHL: DB    0,0        ;saves DEFDMA command line address
  3174. XDRV:    DB    DRV
  3175. XPRDRV: DB    PRDRV
  3176. XUSR:    DB    USR
  3177. XPRUSR: DB    PRUSR
  3178. ;
  3179. ;
  3180. ; Following 3 used by disk buffering routines
  3181. ;
  3182. EOFLG:    DB    0        ;'EOF' flag (1=yes)
  3183. RECPTR: DW    DBUF
  3184. RECNBF: DW    0        ;number of records in the buffer
  3185. ;
  3186.     DS    60        ;stack area
  3187. STACK:    DS    2        ;save original stack address
  3188. ;
  3189. ;
  3190. ; 16-record disk buffer
  3191. ;
  3192. DBUF:    DS    0        ;16-record disk buffer
  3193. LOGBUF: EQU    DBUF+128    ;for use with LOGCAL
  3194. ;
  3195. ;
  3196. ;***********************************************************************
  3197. ;
  3198. ; BDOS equates
  3199. ;
  3200. ;***********************************************************************
  3201. ;
  3202. WRCON:    EQU    2
  3203. PRINT:    EQU    9
  3204. VERNO:    EQU    12        ;get CP/M version number
  3205. SELDRV: EQU    14        ;select drive
  3206. OPEN:    EQU    15        ;0FFH = not found
  3207. CLOSE:    EQU    16        ;    "       "
  3208. SRCHF:    EQU    17        ;    "       "
  3209. SRCHN:    EQU    18        ;    "       "
  3210. ERASEF: EQU    19        ;no return code
  3211. READ:    EQU    20        ;0=OK, 1=EOF
  3212. WRITE:    EQU    21        ;0=OK, 1=ERR, 2=?, 0FFH=no directory spc
  3213. MAKE:    EQU    22        ;0FFH=bad
  3214. CURDRV: EQU    25        ;get current drive
  3215. SETDMA: EQU    26        ;set DMA
  3216. USER:    EQU    32        ;set user area to receive file
  3217. RRDM:    EQU    33        ;read random
  3218. WRDM:    EQU    34        ;write random
  3219. CFSIZE: EQU    35        ;compute file size
  3220. SETRRD: EQU    36        ;set random record
  3221. BDOS:    EQU    BASE+05H
  3222. DEFDMA: EQU    BASE+80H    ;default DMA address
  3223. FCB:    EQU    BASE+5CH    ;system FCB
  3224. FCB1:    EQU    BASE+6CH    ;second FCB
  3225. FCBEXT: EQU    FCB+12        ;file extent
  3226. FCBRNO: EQU    FCB+32        ;record number
  3227. RANDOM: EQU    FCB+33        ;random record field
  3228. ;.....
  3229. ;
  3230. ;
  3231.     END
  3232.