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

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