home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / NUBYE / NUKMD111.ARK / NUKMD111.FL2 < prev    next >
Text File  |  1987-02-03  |  89KB  |  3,777 lines

  1. ; Got a record, it's a duplicate if equal to the previous number, it's
  2. ; OK if previous + 1 record
  3. ;
  4. CHKSNUM:LDA    RCVCNT        ; Get received record number
  5.     MOV    B,A        ; Save it
  6.     LDA    RCDCNT        ; Get previous record number
  7.     CMP    B        ; Rrevious record repeated?
  8.     JZ    RCVACK        ; If yes 'ACK' to catch up
  9.     INR    A        ; Increment by 1 for 120 character block
  10.     CMP    B        ; Match this one we just got?
  11.     JNZ    ABORT        ; No, stop the sender and exit
  12.     RET            ; Else return with carry not set, was ok
  13. ;
  14. ; Receive the Cyclic Redundancy Check characters (2 bytes) and see if
  15. ; the CRC received matches the one calculated.    If they match, get next
  16. ; record, else send a NAK requesting the record be sent again.
  17. ;
  18. RCVCRC:    MVI    E,2        ; Number of bytes to receive
  19. ;
  20. RCVCRC2:MVI    B,5        ; 5 second timeout
  21.     CALL    RECV        ; Get CRC byte
  22.     JC    RCVSTOT        ; Timeout
  23.     DCR    E        ; Decrement the number of bytes
  24.     JNZ    RCVCRC2        ; Get both bytes
  25.     CALL    CRCCHK        ; Check received CRC against calc'd CRC
  26.     ORA    A        ; Is CRC okay?
  27.     JZ    CHKSNUM        ; Yes, go check record numbers
  28.     CALL    ILPRTL        ; Show locally only
  29.     DB    '++ CRC error ++',CR,LF,0
  30.     JMP    RCVSR        ; Go check error limit and send NAK
  31. ;
  32. ; Previous record repeated, due to the last ACK being garbaged.  ACK it
  33. ; so sender will catch up
  34. ;
  35. RCVACK:    CALL    SNDACK        ; Send the ACK
  36.     JMP    RCVRECD        ; Get next block
  37. ;
  38. ; Send an ACK for the record
  39. ;
  40. SNDACK:    MVI    A,ACK        ; Get 'ACK'
  41.     JMP    SEND        ; And send it
  42. ;
  43. ; Send the record header
  44. ; Send SOH, block number and complemented block number (3 bytes total)
  45. ;
  46. SNDHDR:    LDA    KFLG        ; Sending 1k blocks?
  47.     ORA    A
  48.     MVI    A,STX        ; If yes, send a STX rather than SOH
  49.     JNZ    $+5
  50.     MVI    A,SOH        ; Send start of header
  51.     CALL    SEND
  52. ;
  53. SNDHNM:    LDA    RCDCNT        ; Send the current record number
  54.     CALL    SEND
  55.     LDA    RCDCNT        ; Get the record number again
  56.     CMA            ; Complemented
  57.     JMP    SEND        ; From SENDHDR
  58. ;
  59. ; Send the data record
  60. ;
  61. SNDREC:    LDA    CRCFLG
  62.     ORA    A
  63.     JNZ    SNDREC1
  64.     CALL    CATCH
  65. ;
  66. SNDREC1:MVI    C,0        ; Initialize checksum
  67.     LXI    H,0        ; Initialize CRC
  68.     SHLD    CRCVAL
  69.     LDA    KFLG        ; Sending 1k blocks?
  70.     ORA    A
  71.     LXI    D,1024
  72.     JNZ    $+6        ; If yes, skip the next line
  73.     LXI    D,128
  74.     LHLD    RECPTR        ; Get buffer address
  75. ;
  76. SENDC:    MOV    A,M        ; Get a character
  77.     CALL    SEND        ; Send it
  78.     INX    H        ; Point to next character
  79.     DCX    D
  80.     MOV    A,E
  81.     ORA    D
  82.     JNZ    SENDC        ; If DE not zero, keep going
  83.     RET            ; From SENDREC
  84. ;
  85. ; Send the CRC or checksum value, whichever appropriate
  86. ;
  87. SNDCHK:    LDA    CRCFLG        ; See if sending 'CRC' or 'checksum'
  88.     ORA    A
  89.     JNZ    SNDCRC        ; If not zero, send the 'CRC' value
  90. ;
  91. ; Send the checksum
  92. ;
  93. SNDCKS:    MOV    A,C        ; Send the checksum
  94.     JMP    SEND        ; From SNDCKS
  95. ;
  96. ; Send the two Cyclic Redundancy Check characters.  Call FINCRC to cal-
  97. ; culate the CRC which will be in 'DE' upon return.
  98. ;
  99. SNDCRC:    CALL    FINCRC        ; Calculate the 'CRC' for this record
  100.     MOV    A,D        ; Put first 'CRC' byte in accumulator
  101.     CALL    SEND        ; Send it
  102.     MOV    A,E        ; Put second 'CRC' byte in accumulator
  103.     CALL    SEND        ; Send it
  104.     XRA    A        ; Set zero return code
  105.     RET
  106. ;
  107. ; After a record has been sent, and accepted, move the pointers forward
  108. ; 128 or 1024 characters for the next record.
  109. ;
  110. SETPTR:    LXI    D,128        ; For 128 character blocks
  111.     LDA    KFLG
  112.     ORA    A        ; Last block 1k?
  113.     JZ    $+6        ; No, skip next line
  114.     LXI    D,1024        ; Else set for 1024 character blocks
  115.     LHLD    RECPTR        ; Get the buffer pointer
  116.     DAD    D        ; Increment for the record just sent
  117.     SHLD    RECPTR        ; New buffer address for next block
  118.     RET
  119. ;
  120. ; After a library transmission has been made, decrement the remaining
  121. ; records in that library file, then reset the 1k flag if less than 8
  122. ; remaining.
  123. ;
  124. SETLBR:    LDA    KFLG
  125.     LXI    D,-1
  126.     ORA    A
  127.     JZ    $+6
  128.     LXI    D,-8
  129.     LHLD    RCNT        ; Alter the records-sent count
  130.     DAD    D
  131.     SHLD    RCNT        ; One less transmission to go
  132.     ORA    A        ; 'K' flag already zero?
  133.     RZ            ; If yes, skip the rest
  134. ;
  135. ; See if enough records left to use 1k protocol if requested
  136. ;
  137. SETFLG:    LHLD    RCNT
  138.     MOV    A,H        ; Anything in the 'H' register?
  139.     ORA    A
  140.     RNZ
  141.     MOV    A,L        ; Get number of records in 'L' register
  142.     CPI    8        ; At least 8 yet?
  143.     RNC            ; If 8 or more, keep going
  144.     XRA    A        ; Reset the 'K' flag
  145.     STA    KFLG
  146.     RET
  147. ;
  148. ; After a record is sent, a character is returned telling if it was re-
  149. ; ceived properly or not.  An ACK allows the next record to be sent.  A
  150. ; NAK causes the current record to be resent.  If no character (or any
  151. ; character other than ACK or NAK) is received after a short wait (about
  152. ; 10 seconds), a timeout error message is shown and the record will be
  153. ; re-sent.
  154. ;
  155. GTACK:    CALL    MDINST        ; See if a character is ready, now
  156.     JZ    GTACK1        ; If not exit
  157.     CALL    MDINP        ; Get the character in 'A' register
  158.     CPI    ACK        ; See if an ACK already
  159.     RZ            ; If yes, return
  160.     CPI    NAK        ; See if a NAK
  161.     JZ    GTACK2        ; If yes, print error, then resend
  162.     CPI    CANCEL        ; ^X to cancel attempt?
  163.     JZ    GTCAN
  164. ;
  165. GTACK1:    MVI    B,1        ; 1 second for an ACK or NAK
  166.     CALL    RECV        ; Go wait for a character
  167.     JC    GTACK2        ; No character, timed out
  168.     CPI    ACK        ; ACK?
  169.     RZ            ; Yes
  170.     CPI    NAK        ; NAK?
  171.     JZ    GTACK3
  172.     CPI    CANCEL        ; ^X to cancel?
  173.     JZ    GTCAN
  174. ;
  175. GTACK2:    MVI    B,12        ; 12-seconds more for an ACK or NAK
  176.     CALL    RECV        ; Go wait for a character
  177.     JC    GTATOT        ; No character, timed out
  178.     CPI    ACK        ; ACK?
  179.     RZ            ; Yes
  180.     CPI    7BH        ; V.22 synch character?
  181.     JZ    GTACK2        ; Yes, ignore
  182.     CPI    0FBH        ; V.22 synch character?
  183.     JZ    GTACK2        ; Yes, ignore
  184.     CPI    CANCEL        ; ^X to cancel?
  185.     JZ    GTCAN
  186. ;
  187. GTACK3:    MOV    B,A        ; Save the character
  188.     LDA    CHKEOT        ; EOT?
  189.     ORA    A
  190.     JNZ    ACKERR        ; If yes, don't show error (for NUKMD)
  191.     CALL    ILPRTL
  192.     DB    '++ ',0
  193.     MOV    A,B
  194.     CPI    NAK
  195.     JZ    GTACK4
  196.     CALL    HEXO
  197.     CALL    ILPRT
  198.     DB    'H',0
  199.     JMP    GTACK5
  200. ;
  201. GTACK4:    CALL    ILPRT
  202.     DB    'NAK',0
  203. ;
  204. GTACK5:    CALL    ILPRT
  205.     DB    ' received not ACK ++',CR,LF,0
  206. ;
  207. ; Timeout or error on ACK - bump error count then resend the record if
  208. ; error limit is not exceeded
  209. ;
  210. ACKERR:    LDA    ACCERR        ; Count accumulated errors on ACK
  211.     INR    A        ; Add in this error
  212.     STA    ACCERR
  213.     LDA    ERRCT        ; Get count
  214.     INR    A        ; Bump it
  215.     STA    ERRCT        ; Save back
  216.     CPI    10        ; At limit?
  217.     JNC    ACKMSG        ; If yes, send error message and abort
  218.     CALL    RDCOUNT        ; Else show the record count for repeat
  219.     STC            ; Make sure carry is set for repeat
  220.     RET            ; And go back
  221. ;
  222. ; Reached error limit
  223. ;
  224. ACKMSG:    CALL    WAIT1        ; Wait for any input to stop
  225.     MVI    A,CANCEL    ; Tell remote we are quitting
  226.     CALL    SEND
  227.     CALL    SEND
  228.     CALL    SEND
  229.     MVI    B,1        ; Wait for remote to perhaps quit too
  230.     CALL    RECV
  231.     MVI    A,BS
  232.     CALL    SEND        ; Clear any ^X's from buffer
  233.     CALL    SEND
  234.     CALL    SEND
  235.     XRA    A        ; Reset flag to show remote also
  236.     STA    CONONL
  237.     CALL    ERXIT
  238.     DB    CR,LF,'++ TRANSFER ABORTED ++','$'
  239. ;
  240. ; Timed out, with no character - set the carry bit and return
  241. ;
  242. GTATOT:    CALL    EOTCHK        ; See if EOT has been received
  243.     CALL    ILPRTL
  244.     DB    '++ Timeout - no character received ++',CR,LF,0
  245.     JMP    ACKERR
  246. ;
  247. ; Two or more ^X's will cancel the file transfer
  248. ;
  249. GTCAN:    MVI    B,2        ; Up to two seconds for another ^X
  250.     CALL    RECV
  251.     MVI    A,CANCEL    ; Get original character back
  252.     JC    GTACK3        ; If no more ^X, display the first
  253.     CPI    CANCEL        ; Second one?
  254.     JZ    ACKMSG        ; Yes, abort
  255.     JMP    GTACK3        ; ...else, display the character
  256. ;
  257. ; Check the total error count vs. records sent, switch from 1k to 128
  258. ; character transmissions if higher than operator selected value.
  259. ;
  260. GTRATIO:LDA    KFLG        ; Using 1k blocks?
  261.     ORA    A
  262.     RZ            ; If not, skip this routine
  263.     LDA    ERRCT        ; See if we got any errors last record
  264.     CPI    4
  265.     JNC    GTRATIO1    ; If 4 or more, switch to 128 size
  266.     LDA    ACCERR        ; See if up to minimum errors yet
  267.     CPI    3        ; Had as many as three errors yet?
  268.     RC            ; If not, don't get excited too quickly
  269.     LHLD    RECDNO        ; Get current record number increment
  270.     LXI    D,-8        ; Have not successfully sent this 1k yet
  271.     DAD    D        ; Subtract the current increment, then
  272.     XCHG            ; Put in DE for now
  273.     LHLD    ACCERR        ; Number of non-'ACK' errors in HL
  274.     XCHG            ; Back to normal
  275.     CALL    DVHLDE        ; Get ratio in BC of records/hit
  276.     LDA    MSPEED        ; Get current speed
  277.     CPI    5        ; 1200 baud?
  278.     MVI    A,71-1        ;  (for 1200 bps)
  279.     JZ    $+5        ; If 1200, skip next line
  280.     MVI    A,43-1        ;  (for 2400 bps)
  281.     CMP    C        ; Compare with actual ratio
  282.     RC            ; Return if less hits than allowed
  283. ;
  284. GTRATIO1:
  285.     MVI    A,1
  286.     STA    NOISY        ; Show noisy lines caused switch
  287.     XRA    A        ; Else reset the system to 128
  288.     STA    KFLG
  289.     CALL    ILPRTL
  290.     DB    CR,LF
  291.     DB    '++ Noisy line -- switching to 128-byte Xmodem protocol ++'
  292.     DB    CR,LF,0
  293.     RET
  294. ;
  295. CKABORT:CALL    CONSTAT
  296.     ORA    A
  297.     RZ
  298.     CALL    CONIN
  299.     CPI    CANCEL
  300.     RNZ
  301. ;
  302. ; Aborts send or receive routines and returns to command line
  303. ;
  304. ABORT:    LXI    SP,STACK
  305.     CALL    WAIT1        ; 1-second delay to clear input
  306.     MVI    A,CANCEL    ; Show you are cancelling
  307.     CALL    SEND        ; They may quit also with enough ^X
  308.     CALL    SEND
  309.     CALL    SEND
  310.     CALL    WAIT1        ; 1-second delay to clear input
  311.     MVI    A,BS
  312.     CALL    SEND
  313.     CALL    SEND
  314.     CALL    SEND
  315. ;
  316. ABORTX:    CALL    CATCH
  317.     LDA    OPTSAV
  318.     CPI    'R'        ; Receiving?
  319.     JZ    RCVSABT        ; Yes, so delete and abort
  320.     CALL    ERXIT        ; Exit with abort message
  321.     DB    CR,LF,'++ NUKMD ABORTED ++','$'
  322. ;
  323. ; Check to see if an EOT has been received -- if so, remote end doesn't
  324. ; wait for ACK of first EOT sent to us.
  325. ;
  326. EOTCHK:    LDA    EOTFLG        ; Get status
  327.     ORA    A        ; EOT received?
  328.     RZ            ; Return if not
  329.     CALL    ILPRTL        ; Else, display local only for sysop
  330.     DB    CR,LF,LF
  331.     DB    '++ User''s terminal program doesn''t wait for EOT ACK ++'
  332.     DB    CR,LF,0
  333.     JMP    RCVEOT        ; All finished -- save file
  334. ;
  335. ; Increment record number
  336. ;
  337. INCRNO:    PUSH    H
  338.     PUSH    D
  339.     XRA    A
  340.     STA    EOTFLG        ; Clear end of transmission flag
  341.     LHLD    RCDCNT        ; Increment the transmission count
  342.     INX    H
  343.     SHLD    RCDCNT
  344.     LXI    D,1        ; Increment one record only
  345.     LDA    KFLG        ; Sending 1k blocks?
  346.     ORA    A
  347.     JZ    INCRN1        ; If not, exit
  348.     LXI    D,8        ; If yes, increment count by 8
  349. ;
  350. INCRN1:    LHLD    RECDNO        ; Get current record count
  351.     DAD    D        ; Increment that count properly
  352.     SHLD    RECDNO
  353.     CALL    RDCOUNT
  354.     POP    D
  355.     POP    H
  356.     RET
  357. ;
  358. ; Display the record count on the local CRT
  359. ;
  360. RDCOUNT:
  361.     LDA    OPTSAV
  362.     CPI    'R'        ; Receiving?
  363.     JZ    RMSG        ; Yes, else...
  364.     LDA    KFLG
  365.     ORA    A        ; Ymodem download?
  366.     JZ    XSCNT        ; No, else...
  367.     CALL    ILPRTL
  368.     DB    CR,'Snd Ymdm # ',0        ; Sending
  369.     JMP    REST
  370. ;
  371. XSCNT:    CALL    ILPRTL
  372.     DB    CR,'Snd Xmdm # ',0        ; Sending
  373.     JMP    REST
  374. ;
  375. RMSG:    LDA    KFLG
  376.     ORA    A        ; Ymodem upload?
  377.     JZ    XRCNT        ; No, else...
  378.     CALL    ILPRTL
  379.     DB    CR,'Rcv Ymdm # ',0
  380.     JMP    REST
  381. ;
  382. XRCNT:    CALL    ILPRTL
  383.     DB    CR,'Rcv Xmdm # ',0
  384. ;
  385. REST:    LDA    KFLG
  386.     ORA    A        ; Ymodem xfr?
  387.     LHLD    RECDNO
  388.     JZ    $+6        ; No, so skip next line
  389.     CALL    DIVREC
  390.     CALL    DECOUT
  391.     CALL    ILPRT
  392.     DB    ' ',0
  393.     CALL    FUNCHK        ; Check for function keys
  394.     RET            ; From INCRNO
  395. ;
  396. ; See if file exists - if it exists, ask for a different name.
  397. ;
  398. CHEKFIL: IF    NOT SETAREA
  399.     LDA    PRVTFL        ; Receiving in private area?
  400.     ORA    A
  401.     CNZ    RECAREA        ; If yes, set drive and user area
  402.      ENDIF
  403. ;
  404.      IF    SETAREA
  405.     CALL    RECAREA        ; Set the designated area up
  406.      ENDIF
  407. ;
  408.     CALL    FILSCH        ; See if file exists
  409.     INR    A
  410.     RZ            ; No, so return
  411.     MVI    A,CANCEL    ; Else inform user and abort
  412.     CALL    SEND        ; Several cancel requests
  413.     CALL    SEND
  414.     CALL    SEND
  415. ;
  416. CHEKF1:    MVI    B,1
  417.     CALL    RECV
  418.     JNC    CHEKF1        ; Wait until no more characters
  419.     LDA    BCHFLG        ; Using batch mode now?
  420.     STA    CONONL        ; If not, send message to modem also
  421.     ORA    A
  422.     JZ    CHEKF2        ; If not, exit
  423.     MVI    A,CANCEL
  424.     CALL    SEND
  425.     CALL    SEND
  426.     CALL    SEND
  427.     MVI    A,BS
  428.     CALL    SEND
  429. ;
  430. CHEKF2:    CALL    ERXIT        ; Exit, print error message
  431.     DB    '++ File exists -- use a different name ++','$'
  432. ;
  433. ; Creates the file to be received
  434. ;
  435. MAKEFIL:XRA    A        ; Set extent and record number to 0
  436.     STA    FCBEXT
  437.     STA    FCBRNO
  438.     MVI    C,MAKE        ; Get BDOS FNC
  439.     CALL    FCBSET        ; Make file
  440.     INR    A        ; 0FFH=bad?
  441.     RNZ            ; Open ok
  442. ;
  443. ; Directory full - can't make file
  444. ;
  445.     CALL    ERXIT
  446.     DB    '++ Can''t create file - '
  447.     DB    'directory may be full ++','$'
  448. ;
  449. ; Computes record count, and saves it until a successful file-open.
  450. ;
  451. CNREC:    MVI    C,FILSIZ    ; Computes file size
  452.     CALL    FCBSET        ; Read first
  453.     LHLD    RANDOM        ; Get the file size
  454.     SHLD    RCNT        ; Save total record count
  455.     RET
  456. ;
  457. ; -----
  458. ;
  459. ; Opens the file to be sent
  460. ;
  461. OPNFIL:    XRA    A        ; Set extent and rec number to 0
  462.     STA    FCBEXT        ; For proper open
  463.     STA    FCBRNO
  464.     LXI    D,FCB        ; Point to file
  465.     CALL    OPENIT        ; Open it
  466.     INR    A        ; Open ok?
  467.     JNZ    OPNOK        ; If yes, exit
  468.     LDA    OPTSAV
  469.     CPI    'L'        ; .LBR?
  470.     JZ    NOLBR        ; Abort, not found
  471.     CPI    'A'        ; .ARK/.ARC?
  472.     JNZ    NONAME        ; Abort, no match
  473. ;
  474.      IF    NOT ARCEXT
  475.     JMP    NOARK        ; Abort, .ARK/.ARC not found
  476.      ENDIF
  477. ;
  478. ; If the file doesn't open with .ARK (default if no type specified),
  479. ; try .ARC and abort if still no good.
  480. ;
  481.      IF    ARCEXT
  482. ARCSET:    LXI    H,FCBTYP    ; Point to type
  483.     MVI    M,'A'
  484.     INX    H
  485.     MVI    M,'R'
  486.     INX    H
  487.     MVI    M,'C'        ; Force .ARC type
  488.     LXI    D,FCB        ; Point to file
  489.     CALL    OPENIT        ; Open it
  490.     INR    A        ; Open ok?
  491.     JNZ    OPNOK        ; Yes, continue
  492.      ENDIF
  493. ;
  494. NOARK:    CALL    ERXIT
  495.     DB    CR,LF,'++ No .ARK/.ARC file found with that name ++','$'
  496. ;
  497. NOLBR:    CALL    ERXIT
  498.     DB    CR,LF,'++ No .LBR file found with that name ++','$'
  499. ;
  500. NONAME:    CALL    ERXIT
  501.     DB    CR,LF,'++ No file found with that name ++','$'
  502. ;
  503. ZEROLN:    CALL    ERXIT
  504.     DB    CR,LF,'++ Can''t send a 0-length file ++','$'
  505. ;
  506. OPNOK:     IF    ZCPR
  507.     LDA    WHEEL        ; Check wheel status if ZCPR
  508.     ORA    A        ; Is it zero
  509.     JNZ    OPNOK1        ; If non-zero skip all restrictions
  510.      ENDIF
  511. ;
  512.      IF    TAGLBR
  513.     LDA    LBRARC
  514.     ORA    A        ; Member extraction?
  515.     JNZ    OPNOK0        ; Yes, skip SYS check
  516.     LDA    FCB+1        ; First character of file name
  517.     ANI    80H        ; Check bit 7
  518.     JNZ    OPNOT1        ; If bit 7 is set, file is tagged
  519.     LDA    FCB+2        ; Also check 'F2' for a tag
  520.     ANI    80H        ; Is it set?
  521.     JNZ    OPNOT        ; If yes, cannot be downloaded
  522.      ENDIF
  523. ;
  524. OPNOK0:     IF    SNDSYS
  525.     LDA    FCB+10
  526.     ANI    80H
  527.     JNZ    NONAME        ; If $SYS then fake a "file not found"
  528.      ENDIF
  529. ;
  530.     JMP    OPNOK1        ; If not, ok to send file
  531. ;
  532. OPNOT:    CALL    ERXIT        ; Exit with message
  533.     DB    CR,LF,'++ File is not for download ++','$'
  534. ;
  535. OPNOT1:    CALL    ERXIT        ; Exit with message
  536.     DB    CR,LF
  537.     DB    '++ Download internal .LBR/.ARK/.ARC member files ++'
  538.     DB    '$'
  539. ;
  540. OPNOK1:    LDA    BCHFLG        ; Requesting batch mode?
  541.     ORA    A
  542.     JNZ    OPNOK2        ; If yes, skip .LBR/.ARK/.ARC stuff
  543.     LDA    LBRARC
  544.     ORA    A        ; Member extraction?
  545.     JZ    OPNOK2        ; No, skip .LBR/.ARK/.ARC stuff
  546. ;
  547. OPOK1A:    CALL    RSTDMA        ; Reset to default DMA
  548.     CALL    REDFCB        ; Read FCB
  549.     ORA    A        ; Read ok?
  550.     JNZ    RDERR        ; No, abort
  551.     LDA    FCBTYP        ; Get 1st character of filename type
  552.     ANI    7FH        ; Strip high bit
  553.     CPI    'A'        ; ARK/ARC?
  554.     JZ    CKARC        ; Yes, skip .LBR stuff
  555.     LHLD    8EH        ; Get filesize
  556.     SHLD    DIRSIZ        ; Store
  557.     LXI    H,TBUF
  558.     MOV    A,M
  559.     ORA    A
  560.     JZ    CKDIR        ; Check directory present?
  561. ;
  562. NOTLBR:    CALL    ERXIT
  563.     DB    CR,LF,'++ Bad .LBR directory -- please tell Sysop ++','$'
  564. ;
  565. ; Check to see if there is a .LBR file directory with that name and
  566. ; complain if not.
  567. ;
  568. CKDIR:    MVI    B,11        ; Maximum length of file name
  569.     MVI    A,' '        ; First entry must be all blanks
  570.     INX    H
  571. ;
  572. CKDLP:    CMP    M
  573.     JNZ    NOTLBR
  574.     DCR    B
  575.     INX    H
  576.     JNZ    CKDLP
  577. ;
  578. ; The first entry in the .LBR directory is indeed blank.  Now see if the
  579. ; directory size is more than 0.
  580. ;
  581.     MOV    D,M        ; Get directory starting location
  582.     INX    H        ; Which must be 0...
  583.     MOV    A,M
  584.     ORA    D
  585.     JNZ    NOTLBR        ; Directory does not start in record 0
  586.     INX    H
  587.     MOV    A,M        ; Get size of directory
  588.     INX    H
  589.     ORA    M
  590.     JZ    NOTLBR        ; Directory must be >0 records
  591.     LXI    H,TBUF        ; Point to directory
  592. ;
  593. ; The next routine checks the .LBR directory for the specified member.
  594. ; Name one sector at a time.
  595. ;
  596. CMLP:    MOV    A,M        ; Get member active flag
  597.     ORA    A        ; 0 = Active, anything else can be...
  598.     MVI    B,11        ;   regarded as invalid (erased or blank)
  599.     INX    H        ; Point to member name
  600.     JNZ    NOMTCH        ; No match if inactive entry
  601. ;
  602. CKLP:    LDAX    D        ; Now compare the file name specified...
  603.     CMP    M        ; Against the member file name
  604.     JNZ    NOMTCH        ; Exit loop if no match found
  605.     INX    H
  606.     INX    D
  607.     DCR    B
  608.     JNZ    CKLP        ; Check all 11 characters
  609.     MOV    E,M        ; Got the file - get file address
  610.     INX    H
  611.     MOV    D,M
  612.     XCHG
  613.     SHLD    INDEX        ; Save file address in .LBR
  614.     XCHG
  615.     INX    H
  616.     MOV    E,M        ; Get the file size
  617.     INX    H
  618.     MOV    D,M
  619.     XCHG
  620.     SHLD    RCNT        ; Save size a # of records
  621.     LHLD    INDEX        ; Get file address
  622.     SHLD    RANDOM        ; Place it into random field
  623.     XRA    A
  624.     STA    RANDOM+2    ; Must zero the 3rd byte
  625.     STA    FCBRNO        ; Also zero FCB record #
  626.     LXI    D,FCB        ; Point to FCB of .LBR file
  627.     CALL    RRANDM        ; Read it
  628.     JMP    OPNOK3        ; No need to error check
  629. ;
  630. ; Come here if no file name match and another sector is needed
  631. ;
  632. NOMTCH:    INX    H        ; Skip past the end of the file entry
  633.     DCR    B
  634.     JNZ    NOMTCH
  635.     LXI    B,20        ; Point to next file entry
  636.     DAD    B
  637.     LXI    D,MEMFCB    ; Point to member name again
  638.     MOV    A,H        ; See if we checked all 4 entries
  639.     ORA    A
  640.     JZ    CMLP        ; No, check next
  641.     LHLD    DIRSIZ        ; Get directory size
  642.     MOV    A,H
  643.     ORA    L
  644.     JNZ    INLBR        ; Continue if still more to check
  645. ;
  646. NOMBER:    CALL    ERXIT
  647.     DB    CR,LF
  648.     DB    '++ Requested file member not found ++','$'
  649. ;
  650. INLBR:    DCX    H        ; Decrement dirctory size
  651.     SHLD    DIRSIZ
  652.     CALL    REDFCB        ; Read next sector of DIR to TBUF
  653.     ORA    A        ; Read ok?
  654.     JNZ    NOTLBR        ; If not, error or end of file
  655.     LXI    H,TBUF        ; Set our pointers for compare
  656.     LXI    D,MEMFCB
  657.     JMP    CMLP        ; Check next sector
  658. ;
  659. ; .ARK/.ARC file routine -- for "self-unpacking" archive files (i.e.
  660. ; MS-DOS ARC and PKARK .COM files and CP/M's NOAH in the future), up
  661. ; to 3 extra bytes are allowed before finding the ARCMaRK.
  662. ;
  663. CKARC:    LXI    H,TBUF        ; Set pointer for compare
  664.     MVI    B,3        ; Allow up to 3 xtra bytes at start
  665.     MVI    A,ARCMRK
  666. ;
  667. CKARC1:    CMP    M        ; Header marker found?
  668.     JZ    CKARC3        ; Yes, else...
  669.     INR    L        ; Bump record pointer
  670.     DCR    B
  671.     JNZ    CKARC1        ; Loop through extra bytes allowed
  672. ;
  673. CKARC2:    MOV    A,M        ; Get next character
  674.     CPI    ARCMRK        ; Header marker?
  675.     JNZ    ARCERR        ; No, report
  676. ;
  677. CKARC3:    LXI    D,DBUF        ; Disk buffer pointer
  678.     STAX    D        ; Store header marker
  679.     INX    D
  680.     INR    L        ; Bump to next byte
  681.     CZ    ARCRD        ; Read next record if end
  682.     MVI    B,HDRSIZ    ; Set up counter (normal header size)
  683.     MOV    A,M        ; Get next char (compression type 1-8)
  684.     STA    ARCVER        ; Store it
  685.     MOV    C,A        ; Save in C for counter
  686.     CPI    1        ; Compare against vers 1 (old type)
  687.     JC    NOMBER        ; EOF, report member not found
  688.     JNZ    CKARC4        ; >1, so skip next
  689.     INR    A        ; 1, so bump to type 2
  690.     MVI    B,HDRSIZ-4    ; Set up counter (old header size)
  691. ;
  692. CKARC4:    STAX    D        ; Store byte
  693.     INX    D
  694.     DCR    B        ; Decrement header byte counter
  695.     JZ    CKARC5        ; Header stored, so continue
  696.     INR    L        ; Bump to next byte
  697.     CZ    ARCRD
  698.     MOV    A,M        ; Get next header byte
  699.     JMP    CKARC4        ; Loop until header is stored
  700. ;
  701. CKARC5:    SHLD    ARCPTR        ; Store current record pointer
  702.     LXI    H,DBUF+15    ; Get compression type
  703.     DCR    C        ; If compression type was 1...
  704.     MVI    C,4        ; ...4 more bytes
  705.     CZ    MOVER        ; ...move up to uncompressed file size
  706.     LXI    D,DBUF+2    ; Get next member filename
  707.     LXI    H,MEMFCB    ; Get requested member filename
  708.     MVI    B,11        ; Length of filename.ext
  709. ;
  710. CKARC6:    LDAX    D        ; Get next byte
  711.     ANI    7FH        ; Strip high bit
  712.     JZ    CKARC7        ; Fill with blanks if end of name
  713.     INX    D        ; Bump to next byte
  714.     CALL    UCASE        ; Ensure it's upper case
  715.     CPI    '.'        ; Type separator?
  716.     JNZ    CKARC8        ; No, compare
  717.     MOV    A,B        ; Get character count
  718.     CPI    4
  719.     JC    CKARC6        ; Yes, so bypass separator
  720.     DCX    D        ; Backup to '.'
  721. ;
  722. CKARC7:    MVI    A,' '        ; Use blank to fill file name and/or type
  723. ;
  724. CKARC8:    CMP    M        ; Match requested member name?
  725.     JNZ    ONWARD        ; No, skip to next member
  726.     INX    H        ; Bump to next byte
  727.     DCR    B        ; One less for filename.ext length
  728.     JNZ    CKARC6        ; Loop until all characters are compared
  729.     LXI    H,DBUF+18    ; Point to MSB of size
  730.     MOV    A,M
  731.     ORA    A        ; 0?
  732.     JNZ    ARCERR        ; Abort - corrupt header?
  733.     DCX    H
  734.     MOV    D,M        ; Middle two bytes of size to DE
  735.     DCX    H
  736.     MOV    E,M
  737.     DCX    H        ; Point to LSB of size
  738.     ORA    M        ; Test it
  739.     XCHG            ; Page count
  740.     DAD    H
  741.     JC    TOOBIG        ; Abort, too big
  742.     JP    CKARC9        ; Skip next if byte count <128
  743.     INX    H        ; Add another record
  744.     ANI    7FH        ; Reduce byte count
  745. ;
  746. CKARC9:    LXI    D,1        ; Need one more byte
  747.     ADI    30
  748.     JP    CKARCA        ; Skip next unless xtra 2 records needed
  749.     ANI    7FH        ; Last byte offset
  750.     INR    E        ; Show one more record needed    
  751. ;
  752. CKARCA:    DAD    D        ; Total records
  753.     JC    TOOBIG        ; Abort, too big
  754.     SHLD    RCNT        ; Save record count
  755.     SHLD    ARCCNT
  756.     STA    ARCLST        ; Save last record count -1
  757.     JMP    OPNOK3        ; All done, continue with download
  758. ;
  759. ARCERR:    CALL    ERXIT
  760.     DB    CR,LF,'  ++  Requested member not found, or  ++'
  761.     DB    CR,LF,'++ bad .ARK/.ARC header -- inform Sysop ++','$'
  762. ;
  763. TOOBIG:    CALL    ERXIT
  764.     DB    CR,LF,'++ Aborted -- too large for CP/M ++','$'
  765. ;
  766. ; Read record
  767. ;
  768. ARCRD:    PUSH    B        ; Save registers
  769.     PUSH    D
  770.     LHLD    ARCREC        ; Get current record number
  771.     INX    H        ; Bump it
  772.     SHLD    ARCREC        ; Store next record number
  773.     CALL    REDFCB        ; Read next sector of DIR to TBUF
  774.     POP    D        ; Restore registers
  775.     POP    B
  776.     LXI    H,TBUF        ; Set pointers
  777.     ORA    A
  778.     RZ            ; EOF
  779. ;
  780. ARCRD1:    MOV    M,H        ; Fill record with 0's
  781.     INR    L
  782.     JNZ    ARCRD1        ; Loop until record filled with 0's
  783.     STA    ARCEOF        ; Set EOF flag
  784.     MVI    L,TBUF        ; Set pointers
  785.     RET
  786. ;
  787. ; Increment to next .ARK/.ARC member
  788. ;
  789. ONWARD:    LHLD    DBUF+16        ; # whole pages to skip
  790.     DAD    H        ; # records to skip
  791.     LDA    DBUF+15        ; # extra bytes to skip
  792.     ORA    A        ; >128?
  793.     JP    ONWRD1
  794.     INX    H        ; Add one more record
  795.     ANI    7FH        ; Reuce byte count 
  796. ;
  797. ONWRD1:    XCHG            ; Record offset to DE
  798.     LHLD    ARCPTR        ; Set to last byte of header
  799.     INR    L        ; Bump to next byte
  800.     ADD    L        ; Add byte offset
  801.     JP    ONWRD2        ; Skip next if overflows current record
  802.     MOV    L,A        ; Set to start of next record
  803.     MOV    A,D        ; Check record offset
  804.     ORA    E
  805.     JZ    CKARC2        ; Still in same record, so loop
  806.     JMP    ONWRD3        ; Read new record
  807. ;
  808. ONWRD2:    ORI    80H        ; Get proper byte offset in DMA
  809.     MOV    L,A        ; Point to next header
  810.     INX    D
  811. ;
  812. ONWRD3:    SHLD    ARCPTR        ; Save buffer pointer
  813.     LHLD    ARCREC        ; Get current record number
  814.     DAD    D        ; Add record offset
  815.     SHLD    ARCREC        ; Save new record number
  816.     SHLD    RANDOM
  817.     XRA    A
  818.     STA    7FH        ; Clear 3rd byte
  819.     LXI    D,FCB
  820.     CALL    RRANDM        ; Read random record
  821.     ORA    A        ; Ok?
  822.     JNZ    NOMBER        ; No, report member not found
  823.     LXI    H,FCBRNO    ; Current record
  824.     INR    M        ; Bump for sequential read
  825.     LHLD    ARCPTR        ; Get buffer pointer
  826.     JMP    CKARC2        ; Loop to next member (1)
  827. ;
  828. ; =====
  829. ;
  830. OPNOK2:     IF    ZCPR
  831.     LDA    WHEEL        ; Check status of wheel if ZCPR
  832.     ORA    A        ; Is it set?
  833.     JNZ    OPNOK3        ; If yes, skip filetype checks
  834.      ENDIF
  835. ;
  836.      IF    SNDWILD    OR SNDCOM
  837.     LXI    H,FCB+11
  838.     MOV    A,M        ; Check for protect attribute
  839.     ANI    7FH        ; Remove CP/M 2.x attributes
  840.      ENDIF
  841. ;
  842.      IF    SNDWILD
  843.     CPI    '#'        ; Wild card designator (? or *)?
  844.     JZ    OPNOT        ; Yes, so tell user
  845.      ENDIF
  846. ;
  847.      IF    SNDCOM
  848.     CPI    'M'        ; M?
  849.     JNZ    OPNOK3        ; If not, ok to send
  850.     DCX    H
  851.     MOV    A,M        ; Check next character
  852.     ANI    7FH        ; Strip attributes
  853.     CPI    'O'        ; O?
  854.     JNZ    OPNOK3        ; If not, ok to send
  855.     DCX    H
  856.     MOV    A,M        ; Now check 1st character
  857.     ANI    7FH        ; Strip attributes
  858.     CPI    'C'        ; C?
  859.     JNZ    OPNOK3        ; If not, continue
  860.     CALL    ERXIT        ; Exit with message
  861.     DB    CR,LF,'++ Can''t download a .COM file ++','$'
  862.      ENDIF
  863. ;
  864. OPNOK3:    LHLD    RCNT        ; Get record count
  865.     MOV    A,H
  866.     ORA    L        ; Are there any?
  867.     JZ    ZEROLN        ; No, else continue
  868.     LDA    MSPEED
  869.     CPI    5        ; <1200?
  870.     JC    TOOSLO        ; Yes, so skip 1k mention
  871.     PUSH    H        ; Save RCNT
  872.     CALL    ILPRT
  873.     DB    CR,LF
  874.     DB    'Ymodem packets total           > ',0
  875.     POP    H        ; Restore RCNT
  876.     CALL    DIVREC        ; Divide number of records by 8
  877.     CALL    DECOUT        ; Show # of kilobytes
  878. ;
  879. TOOSLO:    CALL    ILPRT
  880.     DB    CR,LF
  881.     DB    'Xmodem packets total           > ',0
  882.     LHLD    RCNT        ; Get original count
  883.     CALL    DECOUT
  884.     LDA    SNDFLG        ; Receiving batch mode now?
  885.     ORA    A
  886.     RNZ            ; If yes, all done
  887.     CALL    ILPRT
  888.     DB    CR,LF
  889.     DB    'Disk space you need            > ',0
  890.     LHLD    RCNT        ; Get original count
  891.     CALL    DIVREC        ; Divide by 8 for k
  892.     CALL    DECOUT
  893.     CALL    ILPRT
  894.     DB    'k',0
  895. ;
  896. ; Show transfer time, first for 1k blocks, then for 128 (skip the 1k
  897. ; times for slower than 1200 bps.) for 1200 bps
  898. ;
  899. KSPEED:    LDA    MSPEED
  900.     CPI    5        ; <1200 bps?
  901.     JC    XSPEED        ; Skip 1k display if yes
  902.     CALL    ILPRT
  903.     DB    CR,LF
  904.     DB    'Ymodem time / 1k packets       > ',0
  905.     CALL    KTIM        ; Get file transfer time in BC (minutes)
  906.     CALL    STORTIM        ; Store for comparing time remaining
  907.     CALL    OPNOK4
  908. ;
  909. XSPEED:    CALL    ILPRT
  910.     DB    CR,LF
  911.     DB    'Xmodem time / 128-byte packets > ',0
  912.     LXI    H,XECTBL    ; Use 128  size values
  913.     SHLD    RECTBL+1
  914.     CALL    XTIM        ; Get file transfer time in BC (minutes)
  915.     LDA    KFLG        ; If 'SK' set, 1k time already stored
  916.     ORA    A
  917.     JNZ    $+6
  918.     CALL    STORTIM
  919.     CALL    OPNOK4
  920.     LXI    H,KECTBL    ; Restore to original 1k values
  921.     SHLD    RECTBL+1
  922.     CALL    ILPRT
  923.     DB    CR,LF,0
  924.     LDA    BCHFLG
  925.     ORA    A
  926.     CNZ    CUMSTS        ; Show how many files remain after this
  927.     LDA    FSTFLG
  928.     ORA    A
  929.     RNZ
  930.     LDA    OPTSAV
  931.     CPI    'A'        ; .ARK/.ARC?
  932.     JNZ    SKPARC        ; no, skip rename message
  933. ;
  934. ; If this is an .ARK/.ARC member exctraction, notify user that file must be
  935. ; named as an .ARK or .ARC file type (for NOAH, ARC, PKXARC, etc).
  936. ;
  937.     CALL    ILPRT
  938.     DB    CR,LF
  939.     DB    'You MUST name this file        > ',0
  940.     MVI    D,8        ; Set up for filename count (ignore type)
  941.     LXI    H,MEMFCB    ; Get requested member name
  942. ;
  943. RENARC:    MOV    A,M
  944.     CPI    ' '        ; Short name?
  945.     JZ    RENAR1        ; Yes, fill in type
  946.     CALL    TYPE
  947.     DCR    D        ; One less to go
  948.     INX    H        ; Get next character
  949.     JNZ    RENARC        ; Loop until done
  950. ;
  951. RENAR1:    LDA    FCBEXT-1    ; Get last character of parent file type
  952.     STA    RENAR2        ; Stuff it below to display
  953.     CALL    ILPRT
  954.     DB    '.AR'        ; Common type characters
  955. RENAR2:    DW    0        ; Last will be either 'K' or 'C' (1)
  956. ;
  957. SKPARC:    CALL    ILPRT
  958.     DB    CR,LF,LF,'Your selection ready to Download'
  959.     DB    CR,LF,'  Abort: CTRL-X <pause> CTRL-X'
  960.     DB    CR,LF,LF,0
  961.     CALL    ILPRTL        ; Display local
  962.     DB    ' [ waiting ]',CR,0
  963.     RET
  964. ;
  965. OPNOK4:    PUSH    H        ; Save seconds in 'L'
  966. ;
  967.      IF    ZCPR
  968.     LDA    WHEEL        ; Check wheel status if ZCPR
  969.     ORA    A        ; 0?
  970.     JNZ    SKPTIM        ; NO, so skip the limit
  971.      ENDIF
  972. ;
  973.     LDA    TLIMIT        ; See if special user
  974.     ORA    A
  975.     JZ    SKPTIM        ; Yes, skip this
  976.     MOV    A,C        ; If limiting get length of this program
  977.     INR    A        ; Increment to next full minute
  978. ;
  979.      IF    TIMEON
  980.     LXI    H,TON
  981.     ADD    M        ; Add time on to xfer time, TON will
  982.      ENDIF
  983. ;
  984.     STA    MINUTE        ; Store value for later comparison
  985.     MOV    A,B        ; Get high byte of minute if >255
  986.     JNZ    MXTMC2        ; If no carry from increment/add
  987.     INR    A
  988. ;
  989. MXTMC2:    STA    MINUTE+1
  990. ;
  991. SKPTIM:    MOV    L,C
  992.     MOV    H,B
  993.     CALL    DECOUT        ; Print decimal number of minutes
  994.     CALL    ILPRT
  995.     DB    ':',0
  996.     POP    H        ; Get seconds
  997.     CALL    ZERO        ; See if 10 or more seconds
  998.     CALL    DECOUT        ; Print the seconds portion
  999. ;
  1000.      IF    ZCPR
  1001.     LDA    WHEEL        ; Get WHEEL status
  1002.     ORA    A        ; 0?
  1003.     RNZ            ; Yes, else check time limit
  1004.      ENDIF
  1005. ;
  1006.     LDA    TLIMIT        ; Check user with unlimited time
  1007.     ORA    A        ; 0?
  1008.     RZ            ; Yes
  1009.     LDA    MINUTE+1    ; Get minute count high byte
  1010.     ORA    A        ; Check if zero
  1011.     JNZ    OVERTM        ; If not, is over 255 minutes
  1012.     LDA    MINUTE        ; Get minute count
  1013.     MOV    B,A        ; Into B
  1014.     LDA    TLIMIT        ; Mxtime allowed
  1015.     INR    A        ; Plus 1
  1016.     SBB    B        ; Subtract file time from MXTIME
  1017.     RNC            ; If less, it's ok to continue
  1018. ;
  1019. OVERTM:    CALL    ILPRT
  1020.     DB    CR,LF,LF,'+++ NUKMD ABORTED +++',CR,LF,LF
  1021.     DB    'Required send time exceeds the ',0
  1022.     LXI    H,OVRMSG
  1023.     LDA    TLOS        ; Show minutes remaining
  1024.     CALL    DEC8
  1025.     CALL    ERXIT1
  1026. ;
  1027. OVRMSG:    DB    0,0,0
  1028.     DB    ' minutes remaining.',CR,LF,'$'
  1029. ;
  1030. KTABLE:    DW    5,14,21,27,32,53,101,190,330,525,0
  1031. KECTBL:    DB    192,69,46,36,30,18,10,5,3,2,0
  1032. XTABLE:    DW    5,13,19,25,30,48,85,141,210,280,0
  1033. XECTBL:    DB    192,74,51,38,32,20,11,8,5,3,0
  1034. ;
  1035. ; Pass record count in RCNT: returns file's approximate download/upload
  1036. ; time in minutes in BC, seconds in 'L', also stuffs the # of mins/secs
  1037. ; values in PGSIZE if LOGCAL is YES.
  1038. ;
  1039. KTIM:    LXI    H,KTABLE
  1040.     JMP    FILTIM
  1041. ;
  1042. XTIM:    LXI    H,XTABLE    ; Point to baud factor table
  1043. ;
  1044. FILTIM:    LDA    MSPEED        ; Get speed indicator
  1045.     MVI    D,0
  1046.     MOV    E,A        ; Set up for table access
  1047.     DAD    D        ; Index to proper factor
  1048.     DAD    D
  1049.     MOV    E,M
  1050.     INX    H
  1051.     MOV    D,M
  1052.     LHLD    RCNT        ; Get number of records
  1053. ;
  1054. FILTIM1:CALL    DVHLDE        ; Divide HL by value in DE (records/min)
  1055.     PUSH    H        ; Save remainder
  1056. ;
  1057. RECTBL:    LXI    H,KECTBL    ; Point to divisors for seconds calc.
  1058.     MVI    D,0
  1059.     LDA    MSPEED        ; Get speed indicator
  1060.     MOV    E,A
  1061.     DAD    D        ; Index into table
  1062.     MOV    A,M        ; Get multiplier
  1063.     POP    H        ; Get remainder
  1064.     CALL    MULHLA        ; Multiply 'H' by 'A'
  1065.     CALL    SHFTHL
  1066.     CALL    SHFTHL
  1067.     CALL    SHFTHL
  1068.     CALL    SHFTHL
  1069.     MVI    H,0        ; HL now = seconds (L=secs,H=0)
  1070.     MOV    A,L
  1071.     CPI    60
  1072.     JC    RECTB1
  1073.     SUI    60
  1074.     MOV    L,A
  1075.     INR    C
  1076. ;
  1077. RECTB1:    MOV    A,C        ; See if any minutes
  1078.     ORA    B
  1079.     RNZ            ; If yes, exit
  1080.     MOV    A,L        ; See if any seconds
  1081.     ORA    A
  1082.     RNZ            ; If yes, exit
  1083.     INR    A        ; Else show at least one second
  1084.     MOV    L,A
  1085.     RET
  1086. ;
  1087. STORTIM: IF    LOGCAL
  1088.     MOV    A,C        ; Add minutes of length (to 0 or 1)
  1089.     STA    PGSIZE        ; Save as LSB of minutes
  1090.     MOV    A,B        ; Get MSB of minutes
  1091.     STA    PGSIZE+1    ; Save as MSB of minutes (>255?)
  1092.     MOV    A,L        ; Get LSB of seconds (can't be >59)
  1093.     STA    PGSIZE+2    ; Save for LOGCALL
  1094.      ENDIF
  1095. ;
  1096.     RET            ; End of FILTIM routine
  1097. ;
  1098. ; This routine divides the total number of 1024-byte blocks by 8.
  1099. ;
  1100. DIVREC:    LXI    D,8
  1101.     CALL    DVHLDE        ; To get # of 1024 byte blocks
  1102.     MOV    A,H
  1103.     ORA    L        ; Check if remainder
  1104.     MOV    H,B        ; Get quotient
  1105.     MOV    L,C
  1106.     JZ    $+4        ; If 0 remainder, exact k
  1107.     INX    H        ; Else bump up 1 k
  1108.     RET
  1109. ;
  1110. ; Divides 'HL' by value in 'DE' - upon exit: BC=quotient, HL=remainder
  1111. ;
  1112. DVHLDE:    PUSH    D        ; Save divisor
  1113.     MOV    A,E
  1114.     CMA            ; Negate divisor
  1115.     MOV    E,A
  1116.     MOV    A,D
  1117.     CMA
  1118.     MOV    D,A
  1119.     INX    D        ; 'DE' is now two's complemented
  1120.     LXI    B,0        ; Init quotient
  1121. ;
  1122. DIVL1:    DAD    D        ; Subtract divisor from divident
  1123.     INX    B        ; Bump quotient
  1124.     JC    DIVL1        ; Loop until sign changes
  1125.     DCX    B        ; Adjust quotient
  1126.     POP    D        ; Retrieve divisor
  1127.     DAD    D        ; Readjust remainder
  1128.     RET
  1129. ;
  1130. ; Multiply the value in 'HL' by the value in 'A', return with answer in
  1131. ; 'HL'.
  1132. ;
  1133. MULHLA:    XCHG            ; Multiplicand to 'DE'
  1134.     LXI    H,0        ; Init product
  1135.     INR    A
  1136. ;
  1137. MULLP:    DCR    A
  1138.     RZ
  1139.     DAD    D
  1140.     JMP    MULLP
  1141. ;
  1142. ; Shift the 'HL' register pair one bit to the right
  1143. ;
  1144. SHFTHL:    MOV    A,L
  1145.     RAR
  1146.     MOV    L,A
  1147.     ORA    A        ; Clear the carry bit
  1148.     MOV    A,H
  1149.     RAR
  1150.     MOV    H,A
  1151.     RNC
  1152.     MVI    A,128
  1153.     ORA    L
  1154.     MOV    L,A
  1155.     RET
  1156. ;
  1157. ZERO:    MOV    A,L        ; Get the number of seconds
  1158.     CPI    9+1        ; 10 seconds or more?
  1159.     RNC            ; If yes, disregard
  1160.     CALL    ILPRT
  1161.     DB    '0',0
  1162.     RET
  1163. ;
  1164. ; end of open file, set time routine
  1165. ; ----------------------------------
  1166. ;
  1167. ; Closes the received file
  1168. ;
  1169. CLOSFIL:LXI    D,FCB        ; Point to file
  1170.     CALL    CLOSEF        ; Close it
  1171.     INR    A        ; Close ok?
  1172.     RNZ            ; Yes, return
  1173.     CALL    ERXIT        ; No, abort
  1174.     DB    '++ No file or can''t close it ++','$'
  1175. ;
  1176. ; Decimal output routine - call with decimal value in 'HL'
  1177. ;
  1178. DECOUT:    PUSH    B
  1179.     PUSH    D
  1180.     PUSH    H
  1181.     LXI    B,-10
  1182.     LXI    D,-1
  1183. ;
  1184. DECOU2:    DAD    B
  1185.     INX    D
  1186.     JC    DECOU2
  1187.     LXI    B,10
  1188.     DAD    B
  1189.     XCHG
  1190.     MOV    A,H
  1191.     ORA    L
  1192.     CNZ    DECOUT
  1193.     MOV    A,E
  1194.     ADI    '0'
  1195.     CALL    CTYPE
  1196.     POP    H
  1197.     POP    D
  1198.     POP    B
  1199.     RET
  1200. ;
  1201. ; -----
  1202. ;
  1203. ; Prints a hex value in 'A' on the CRT
  1204. ;
  1205. HEXO:    PUSH    PSW
  1206.     RAR
  1207.     RAR
  1208.     RAR
  1209.     RAR
  1210.     CALL    NIBBL
  1211.     POP    PSW
  1212. ;
  1213. NIBBL:    ANI    0FH
  1214.     CPI    10
  1215.     JC    ISNUM
  1216.     ADI    7
  1217. ;
  1218. ISNUM:    ADI    '0'        ; Add in ASCII bias
  1219.     JMP    CTYPE
  1220. ;
  1221. ; Move (HL) to (DE), length in (B)
  1222. ;
  1223. MOVE:    MOV    A,M        ; Get a byte
  1224.     STAX    D        ; Put at new home
  1225.     INX    D        ; Bump pointers
  1226.     INX    H
  1227.     DCR    B        ; Decrement byte count
  1228.     JNZ    MOVE        ; If more, do it
  1229.     RET            ; If not, return
  1230. ;
  1231. ; -----
  1232. ;
  1233. ; Read a record, refill buffer if empty - update record read
  1234. ;
  1235. RDRECD:    LDA    RECNBF        ; See how many records in the buffer
  1236.     ORA    A
  1237.     JZ    RDBLOCK        ; If none, go get some
  1238.     LDA    KFLG        ; Using 1k blocks?
  1239.     ORA    A
  1240.     JZ    RDREC1        ; If not, exit
  1241. ;
  1242. ; Using 1k blocks, switch to 128 if less than 8 records left
  1243. ;
  1244.     LDA    RECNBF        ; See how many records in buffer
  1245.     CPI    8
  1246.     JNC    RDREC2        ; If 8 or more stay in 1k blocks
  1247.     XRA    A        ; Else there are 1-7 records left
  1248.     STA    KFLG        ; Reset the 1k flag for 128
  1249. ;
  1250. RDREC1:    LDA    RECNBF        ; Get number of records in buffer
  1251.     DCR    A        ; Decrement it for 128 character blocks
  1252.     STA    RECNBF        ; Store the new value
  1253.     RET            ; From 'READRED'
  1254. ;
  1255. ; Using 1k blocks, get set to send another one
  1256. ;
  1257. RDREC2:    SUI    8        ; Subtract 1k worth
  1258.     STA    RECNBF
  1259.     RET
  1260. ;
  1261. ; Buffer is empty - read in another block of 16k
  1262. ;
  1263. RDBLOCK:LDA    EOFLG        ; Get 'EOF' flag
  1264.     CPI    1        ; Is it set?
  1265.     STC            ; To show 'EOF'
  1266.     RZ            ; Got 'EOF'
  1267.     CALL    RDBLK1
  1268.     JMP    RDRECD        ; Pass record to caller
  1269. ;
  1270. ; Read up to 16k from the disk file into the buffer, ready to send
  1271. ;
  1272. RDBLK1:    MVI    C,0        ; Records in block
  1273.     LXI    D,DBUF        ; To disk buffer
  1274. ;
  1275. RDRECLP:PUSH    B
  1276.     PUSH    D
  1277.     LDA    OPTSAV
  1278.     CPI    'A'        ; .ARK/.ARC member extraction?
  1279.     JZ    RDARC        ; Yes, so skip rest
  1280.     CALL    DMASET        ; Set DMA
  1281.     CALL    REDFCB        ; Read FCB
  1282. ;
  1283. RDBLK2:    POP    D
  1284.     POP    B
  1285.     ORA    A        ; Read ok?
  1286.     JNZ    REOF        ; If not, error or end of file
  1287.     LXI    H,128        ; Add length of one record
  1288.     DAD    D        ; To next buffer
  1289.     XCHG            ; Buffer to 'DE'
  1290.     INR    C        ; More records?
  1291.     MOV    A,C        ; Get count
  1292.     CPI    BUFSIZ*8    ; Done?
  1293.     JNZ    RDRECLP        ; Read more
  1294. ;
  1295. ; Buffer is full or got EOF
  1296. ;
  1297. RDBFULL:STA    RECNBF        ; Store record count
  1298.     LXI    H,DBUF        ; Get the beginning buffer address
  1299.     SHLD    RECPTR        ; Save for next record
  1300.     MVI    C,STDMA        ; Reset to default DMA
  1301.     LXI    D,TBUF
  1302.     JMP    BDOS        ; from CALL RDBLK1
  1303. ;
  1304. REOF:    DCR    A        ; 'EOF'?
  1305.     JNZ    RDERR        ; Got 'EOF'
  1306.     MVI    A,1
  1307.     STA    EOFLG        ; Set EOF flag
  1308.     MOV    A,C
  1309.     JMP    RDBFULL
  1310. ;
  1311. ; .ARK/.ARC read file routine
  1312. ;
  1313. RDARC:    LXI    B,32768        ; B=128  C=0
  1314.     LHLD    ARCCNT        ; Get record count
  1315.     DCX    H        ; Bump down one
  1316.     SHLD    ARCCNT        ; Save new count
  1317.     MOV    A,H
  1318.     ORA    L        ; Last record?
  1319.     JNZ    RDARC1        ; No, skip next
  1320.     LDA    ARCLST        ; Get # bytes -1 in last record
  1321.     MOV    C,B
  1322.     MOV    B,A
  1323.     ORA    A
  1324.     JZ    RDARC3
  1325.     XRA    C
  1326.     MOV    C,A
  1327. ;
  1328. RDARC1:    LHLD    ARCPTR        ; Get record pointer
  1329.     LDA    ARCFST
  1330.     ORA    A        ; First record?
  1331.     JNZ    RDARC2        ; No, skip next
  1332.     LXI    D,DBUF+29    ; Skip header
  1333.     MOV    A,B
  1334.     SUI    29
  1335.     MOV    B,A
  1336.     STA    ARCFST        ; Show not first time
  1337. ;
  1338. RDARC2:    INR    L        ; Next byte
  1339.     CZ    ARCRD        ; Fill buffer if end
  1340.     MOV    A,M        ; Get byte
  1341.     STAX    D
  1342.     INX    D
  1343.     DCR    B
  1344.     JNZ    RDARC2        ; Loop until all bytes moved
  1345.     SHLD    ARCPTR        ; Save new pointer
  1346.     XRA    A        ; Clear all
  1347.     CMP    C
  1348.     JZ    RDARC4        ; Skip next
  1349. ;
  1350. RDARC3:    STAX    D        ; Store EOF in buffer
  1351.     INX    D
  1352.     INR    C
  1353.     JNZ    RDARC3        ; Loop to zero final record
  1354. ;
  1355. RDARC4:    LDA    ARCEOF        ; Get flag status
  1356.     JMP    RDBLK2        ; Return for more
  1357. ;
  1358. ; Read error
  1359. ;
  1360. RDERR:    CALL    ERXIT
  1361.     DB    '++ File read error ++','$'
  1362. ;
  1363. ; end of read record routine
  1364. ; --------------------------
  1365. ;
  1366. ; Writes the record into a buffer.  If/when 16k has been written, writes
  1367. ; the block to disk.
  1368. ;
  1369. ; Entry point "WRBLOCK" flushes the buffer at EOF
  1370. ;
  1371. WRRECD:    LHLD    RECPTR        ; Get buffer address
  1372.     LXI    D,128        ; 128 chars/record
  1373.     LDA    KFLG        ; Using 1k blocks?
  1374.     ORA    A
  1375.     JZ    $+6        ; If not, skip next line
  1376.     LXI    D,1024        ; 1k/record
  1377.     DAD    D        ; To next buffer
  1378.     SHLD    RECPTR        ; Save buffer address
  1379.     LDA    KFLG        ; Using 1k blocks?
  1380.     ORA    A
  1381.     JZ    WRREC1        ; If not, exit
  1382.     LDA    RECNBF        ; Get number of records in buffer
  1383.     ADI    8        ; Increment it 8 records for 1k
  1384.     JMP    WRREC2
  1385. ;
  1386. WRREC1:    LDA    RECNBF        ; Get number of records in buffer
  1387.     INR    A        ; increment it for 1 record
  1388. ;
  1389. WRREC2:    STA    RECNBF        ; Store the new value
  1390.     CPI    BUFSIZ*8    ; Is the buffer full, yet?
  1391.     RNZ            ; No, return
  1392. ;
  1393. ; Writes a block to disk
  1394. ;
  1395. WRBLOCK:LDA    RECNBF        ; Number of records in the buffer
  1396.     ORA    A        ; 0 means end of file
  1397.     RZ            ; None to write
  1398.     MOV    C,A        ; Save count
  1399.     LXI    D,DBUF        ; Point to disk buff
  1400. ;
  1401. DKWRLP:    PUSH    H
  1402.     PUSH    D
  1403.     PUSH    B
  1404.     CALL    DMASET        ; Set DMA to buffer
  1405.     MVI    C,WRITE
  1406.     CALL    FCBSET        ; Write block
  1407.     POP    B
  1408.     POP    D
  1409.     POP    H
  1410.     ORA    A
  1411.     JNZ    WRERR        ; Oops, error
  1412.     LXI    H,128        ; Length of 1 record
  1413.     DAD    D        ; 'HL'= next buff
  1414.     XCHG            ; To 'DE' for setdma
  1415.     DCR    C        ; More records?
  1416.     JNZ    DKWRLP        ; Yes, loop
  1417.     XRA    A        ; Get a zero
  1418.     STA    RECNBF        ; Reset number of records
  1419.     LXI    H,DBUF        ; Reset buffer buffer
  1420.     SHLD    RECPTR        ; Save buffer address
  1421. ;
  1422. RSDMA:    MVI    C,STDMA
  1423.     LXI    D,TBUF        ; Reset DMA address
  1424.     JMP    BDOS
  1425. ;
  1426. WRERR:    CALL    RSDMA        ; Reset DMA to normal
  1427.     MVI    C,CANCEL    ; Cancel
  1428.     CALL    SEND        ; Sender
  1429.     CALL    SEND
  1430.     CALL    SEND
  1431.     CALL    RCVSABT        ; Kill receive file
  1432.     CALL    ERXIT        ; Exit with msg:
  1433.     DB    '++ Error writing file ++','$'
  1434. ;
  1435. ; Receive a character - timeout time is in 'B' in seconds.  Entry via
  1436. ; 'RCVDG' deletes garbage characters on the line.  For example, having
  1437. ; just sent a record calling 'RECVDG' will delete any line-noise-induced
  1438. ; characters "long" before the ACK/NAK would be received.
  1439. ;
  1440. RECV:    PUSH    D        ; Save 'DE' regs.
  1441.     MVI    E,MHZ        ; Get the clock speed
  1442.     XRA    A        ; Clear the 'A' reg.
  1443. ;
  1444. MSLOOP:    ADD    B        ; Number of seconds
  1445.     DCR    E        ; One less mhz. to go
  1446.     JNZ    MSLOOP        ; If not zero, continue
  1447.     MOV    B,A        ; Put total value back into 'B'
  1448. ;
  1449. MSEC:    LXI    D,3100        ; 1 second DCR loop count
  1450. ;
  1451. MWTI:    CALL    MDINST        ; Input from modem ready
  1452.     JNZ    MCHAR        ; Yes, get the character
  1453.     DCR    E        ; Count down for timeout
  1454.     JNZ    MWTI
  1455.     DCR    D
  1456.     JNZ    MWTI
  1457.     DCR    B        ; More seconds?
  1458.     JNZ    MSEC        ; Yes, wait
  1459. ;
  1460. ; Test for the presence of carrier - if none, go to 'CARCK' and continue
  1461. ; testing for specified time.  If carrier returns, continue.  If it does
  1462. ; not return, exit.
  1463. ;
  1464.     CALL    MDCARCK        ; Is carrier still on?
  1465.     CZ    CARCK        ; If not, test for 15 seconds
  1466. ;
  1467. ; Modem timed out receiving - but carrier is still on.
  1468. ;
  1469.     POP    D        ; Restore 'DE'
  1470.     STC            ; Carry shows timeout
  1471.     RET
  1472. ;
  1473. ; Get character from modem.
  1474. ;
  1475. MCHAR:    CALL    MDINP        ; Get data byte from modem
  1476.     POP    D        ; Restore 'DE'
  1477. ;
  1478. ; Calculate Checksum and CRC
  1479. ;
  1480.     PUSH    PSW        ; Save the character
  1481.     CALL    UPDCRC        ; Calculate CRC
  1482.     ADD    C        ; Add to checksum
  1483.     MOV    C,A        ; Save checksum
  1484.     POP    PSW        ; Restore the character
  1485.     ORA    A        ; Carry off: no error
  1486.     RET            ; From 'RECV'
  1487. ;
  1488. ; Common carrier test for receive and send.  If carrier returns within
  1489. ; TIMOUT seconds, normal program execution continues.  Else, it will
  1490. ; abort to CP/M via EXIT.
  1491. ;
  1492. CARCK:    MVI    E,TIMOUT*10    ; Value for 15 second delay
  1493. ;
  1494. CARCK1:    CALL    DELAY        ; Kill .1 seconds
  1495.     CALL    MDCARCK        ; Is carrier still on?
  1496.     RNZ            ; Return if carrier on
  1497.     DCR    E        ; Has 15 seconds expired?
  1498.     JNZ    CARCK1        ; If not, continue testing
  1499. ;
  1500. ; Report to local console
  1501. ;
  1502.     CALL    ILPRTL        ; Report loss of carrier locally only
  1503.     DB    CR,LF,LF,0
  1504.     LDA    OPTSAV        ; Get option
  1505.     CPI    'R'        ; If not receive
  1506.     JNZ    EXIT        ; Then abort now, else
  1507.     CALL    ILPRT
  1508.     DB    CR,LF
  1509.     DB    '++ Deleting partial Upload -- Carrier Lost ++'
  1510.     DB    CR,LF,0
  1511.     CALL    DELFILE        ; Delete the file we started
  1512.     JMP    EXIT        ; From CARCK back to CP/M prompt
  1513. ;
  1514. ; Delay - 100 millisecond delay.
  1515. ;
  1516. DELAY:    PUSH    B        ; Save 'BC'
  1517.     LXI    B,MHZ*4167    ; Value for 100 ms. delay
  1518. ;
  1519. DELAY2:    DCX    B        ; Update count
  1520.     MOV    A,B        ; Get MSP byte
  1521.     ORA    C        ; Count = zero?
  1522.     JNZ    DELAY2        ; If not, continue
  1523.     POP    B        ; Restore 'BC'
  1524.     RET            ; Return to CARCK1
  1525. ;
  1526. ; Delay to let all incoming stop for one second
  1527. ;
  1528. WAIT1:    MVI    B,1        ; For 1-second
  1529.     CALL    RECV        ; See if any characters still coming in
  1530.     JNC    WAIT1        ; If yes, keep looping
  1531.     RET            ; If none for 1-second, all done
  1532. ;
  1533. ; -----
  1534. ;
  1535. ; Asks user to add description of an uploaded file
  1536. ;
  1537. ASK:     IF    MSGDSC OR DESCRIB
  1538.     LDA    OPTSAV
  1539.     CPI    'R'        ; Uploading?
  1540.     RNZ            ; No, so return
  1541.     LDA    PRVTFL
  1542.     ORA    A        ; Upload private?
  1543.     RNZ            ; Yes, no descriptions required
  1544.      ENDIF
  1545. ;
  1546.      IF    (MSGDSC    OR DESCRIB) AND    RESUSR AND PUPOPT
  1547.     LDA    PUPFLG        ; Get privileged status
  1548.     ORA    A        ; Privileged xfr request?
  1549.     RNZ            ; Yes, skip description
  1550.      ENDIF
  1551. ;
  1552.      IF    MSGDSC AND (NOT    DESCRIB)
  1553.     MVI    A,1
  1554.     STA    DSCFLG        ; Show description file
  1555.      ENDIF
  1556. ;
  1557.      IF    MSGDSC OR DESCRIB
  1558.     LDA    FILCNT        ; Files received batch?
  1559.     ORA    A
  1560.      ENDIF
  1561. ;
  1562.      IF    MSGDSC AND (NOT    DESCRIB)
  1563.     RZ            ; Single file xfr -- all done here
  1564.      ENDIF
  1565. ;
  1566.      IF    (NOT MSGDSC) AND DESCRIB
  1567.     JZ    ASK1        ; If not, exit
  1568.      ENDIF
  1569. ;
  1570.      IF    MSGDSC OR DESCRIB
  1571.     LXI    H,NAMBUF    ; ...else, get filename
  1572.     SHLD    NBSAVE
  1573.     CALL    BCHDCR
  1574.      ENDIF
  1575. ;
  1576. ASK1:     IF    MSGDSC AND (NOT    DESCRIB)
  1577.     CALL    DILPRT
  1578.     DB    CR,LF,0
  1579.     JMP    ASK2
  1580.      ENDIF
  1581. ;
  1582.      IF    DESCRIB    AND (NOT MSGDSC)
  1583.     CALL    SHONM        ; Show the file name
  1584.     CALL    DILPRT
  1585.     DB    '  <<Description Entry Phase>>',CR,LF,0
  1586.      ENDIF
  1587. ;
  1588.      IF    MSGDSC OR DESCRIB
  1589. ASK2:    LDA    CHKASK
  1590.     ORA    A        ; Already been this way?
  1591.     JNZ    ASK3        ; Yes, so skip rest
  1592.     MVI    A,1
  1593.     STA    CHKASK        ; Show we've been here, now
  1594.     CALL    DILPRT
  1595.     DB    CR,LF,LF
  1596.     DB    'If you have pre-typed your description(s) and '
  1597.     DB    'wish to turn off wrap',CR,LF
  1598.     DB    'during each description text transfer, answer (Y)es.'
  1599.     DB    CR,LF,LF
  1600.     DB    'Turn off automatic end-of-line wrap? <N> ',0
  1601.     CALL    INPUT
  1602.     ANI    5FH        ; Change to upper case
  1603.     CPI    'Y'        ; Turn off wrap mode?
  1604.     JNZ    ASK1A        ; No
  1605.     CALL    DILPRT        ; ...else
  1606.     DB    'YES',CR,LF,'(Automatic wrap mode is now OFF)',0
  1607.     MVI    A,72
  1608.     STA    XWRAP        ; Turn off wrap mode
  1609.     JMP    ASK3        ; Ask for description
  1610. ;
  1611. ASK1A:    CALL    DILPRT
  1612.     DB    'NO',CR,LF,'(Automatic wrap mode is now ON)',0
  1613. ;
  1614. ASK3:    CALL    DILPRT
  1615.     DB    CR,LF,LF
  1616.     DB    'Please describe this file in 7 lines or less.  '
  1617.     DB    'Tell what equipment it is',CR,LF
  1618.     DB    'for and what the program does.  Hit an extra CR'
  1619.     DB    ' on a blank line to quit.',CR,LF,LF,0
  1620. ;
  1621. ; Get the file name from FCB, skip any blanks
  1622. ;
  1623.     LXI    H,HLINE
  1624.      ENDIF
  1625. ;
  1626.      IF    MSGDSC AND (NOT    DESCRIB)
  1627.     CALL    DSTOR2        ; Store initial header info
  1628.     CALL    LSTCLR        ; Get caller's name
  1629.     CALL    DSTOR3        ; Store it in header
  1630.     LXI    H,HLINE1
  1631.     CALL    DSTOR2        ; Store remaining header info
  1632.     LDA    DSKSAV        ; Get upload drive
  1633.     INR    A
  1634.     ADI    'A'-1
  1635.     CALL    OUTCHR
  1636.     LDA    USRSAV        ; Get upload user area
  1637.     CALL    PNDEC        ; Convert and store
  1638.     MVI    A,':'
  1639.     CALL    OUTCHR
  1640.      ENDIF
  1641. ;
  1642.      IF    DESCRIB    AND (NOT MSGDSC)
  1643.     CALL    DSTOR1        ; Store header info
  1644.      ENDIF
  1645. ;
  1646.      IF    MSGDSC OR DESCRIB
  1647.     MVI    B,8        ; Get FILENAME
  1648.     LXI    D,FCB+1
  1649.     LXI    H,OLINE
  1650.     CALL    LOPFCB
  1651.      ENDIF
  1652. ;
  1653.      IF    DESCRIB    AND (NOT MSGDSC)
  1654.     LDAX    D
  1655.     CPI    ' '        ; Any file extent?
  1656.     JZ    AFIND1        ; If not, skip the period and extent
  1657.      ENDIF
  1658. ;
  1659.      IF    MSGDSC OR DESCRIB
  1660.     MVI    A,'.'
  1661.     MOV    M,A        ; Separate FILENAME and EXTENT
  1662.     CALL    TYPE
  1663.     INX    H
  1664.     MVI    B,3        ; Get EXTENT name
  1665.     CALL    LOPFCB
  1666.      ENDIF
  1667. ;
  1668. AFIND1:     IF    DESCRIB    AND (NOT MSGDSC) AND USEMENU AND XTRA1
  1669.     LDA    CHOICE
  1670.     CPI    'B'
  1671.     JZ    AFIND2
  1672.      ENDIF
  1673. ;
  1674.      IF    DESCRIB    AND (NOT MSGDSC) AND USEMENU AND XTRA2
  1675.     CPI    'C'
  1676.     JZ    AFIND3
  1677.      ENDIF
  1678. ;
  1679.      IF    DESCRIB    AND (NOT MSGDSC)
  1680.     LDA    KIND
  1681.     CPI    '0'
  1682.     LXI    D,KIND0+4
  1683.     CZ    DKIND        ; File category 0
  1684.     CPI    '1'
  1685.     LXI    D,KIND1+4
  1686.     CZ    DKIND        ; File category 1
  1687.     CPI    '2'
  1688.     LXI    D,KIND2+4
  1689.     CZ    DKIND        ; File category 2
  1690.     CPI    '3'
  1691.     LXI    D,KIND3+4
  1692.     CZ    DKIND        ; File category 3
  1693.     CPI    '4'
  1694.     LXI    D,KIND4+4
  1695.     CZ    DKIND        ; File category 4
  1696.     CPI    '5'
  1697.     LXI    D,KIND5+4
  1698.     CZ    DKIND        ; File category 5
  1699.     CPI    '6'
  1700.     LXI    D,KIND6+4
  1701.     CZ    DKIND        ; File category 6
  1702.     CPI    '7'
  1703.     LXI    D,KIND7+4
  1704.     CZ    DKIND        ; File category 7
  1705.     CPI    '8'
  1706.     LXI    D,KIND8+4
  1707.     CZ    DKIND        ; File category 8
  1708.     CPI    '9'
  1709.     LXI    D,KIND9+4
  1710.     CZ    DKIND        ; File category 9
  1711.      ENDIF
  1712. ;
  1713.      IF    DESCRIB    AND (NOT MSGDSC) AND USEMENU AND XTRA1
  1714.     JMP    AFIND4        ; Skip next
  1715. AFIND2:    LDA    KIND
  1716.     CPI    '0'
  1717.     LXI    D,KIND0B+4
  1718.     CZ    DKIND        ; File category 0
  1719.     CPI    '1'
  1720.     LXI    D,KIND1B+4
  1721.     CZ    DKIND        ; File category 1
  1722.     CPI    '2'
  1723.     LXI    D,KIND2B+4
  1724.     CZ    DKIND        ; File category 2
  1725.     CPI    '3'
  1726.     LXI    D,KIND3B+4
  1727.     CZ    DKIND        ; File category 3
  1728.     CPI    '4'
  1729.     LXI    D,KIND4B+4
  1730.     CZ    DKIND        ; File category 4
  1731.     CPI    '5'
  1732.     LXI    D,KIND5B+4
  1733.     CZ    DKIND        ; File category 5
  1734.     CPI    '6'
  1735.     LXI    D,KIND6B+4
  1736.     CZ    DKIND        ; File category 6
  1737.     CPI    '7'
  1738.     LXI    D,KIND7B+4
  1739.     CZ    DKIND        ; File category 7
  1740.     CPI    '8'
  1741.     LXI    D,KIND8B+4
  1742.     CZ    DKIND        ; File category 8
  1743.     CPI    '9'
  1744.     LXI    D,KIND9B+4
  1745.     CZ    DKIND        ; File category 9
  1746.      ENDIF
  1747. ;
  1748.      IF    DESCRIB    AND (NOT MSGDSC) AND USEMENU AND XTRA2
  1749.     JMP    AFIND4        ; Skip next
  1750. AFIND3:    LDA    KIND
  1751.     CPI    '0'
  1752.     LXI    D,KIND0C+4
  1753.     CZ    DKIND        ; File category 0
  1754.     CPI    '1'
  1755.     LXI    D,KIND1C+4
  1756.     CZ    DKIND        ; File category 1
  1757.     CPI    '2'
  1758.     LXI    D,KIND2C+4
  1759.     CZ    DKIND        ; File category 2
  1760.     CPI    '3'
  1761.     LXI    D,KIND3C+4
  1762.     CZ    DKIND        ; File category 3
  1763.     CPI    '4'
  1764.     LXI    D,KIND4C+4
  1765.     CZ    DKIND        ; File category 4
  1766.     CPI    '5'
  1767.     LXI    D,KIND5C+4
  1768.     CZ    DKIND        ; File category 5
  1769.     CPI    '6'
  1770.     LXI    D,KIND6C+4
  1771.     CZ    DKIND        ; File category 6
  1772.     CPI    '7'
  1773.     LXI    D,KIND7C+4
  1774.     CZ    DKIND        ; File category 7
  1775.     CPI    '8'
  1776.     LXI    D,KIND8C+4
  1777.     CZ    DKIND        ; File category 8
  1778.     CPI    '9'
  1779.     LXI    D,KIND9C+4
  1780.     CZ    DKIND        ; File category 9
  1781.      ENDIF
  1782. ;
  1783.      IF    MSGDSC AND (NOT    DESCRIB)
  1784.     LXI    D,HLINE3
  1785.     CALL    DKIND
  1786.      ENDIF
  1787. ;
  1788. AFIND4:     IF    MSGDSC OR DESCRIB
  1789.     CALL    DSTOR        ; Put FILENAME line into memory and show
  1790.     LDA    XWRAP
  1791.     CPI    72        ; Word wrap off?
  1792.     JNZ    AFIND5        ; No, so skip typing guide
  1793.     CALL    DILPRT
  1794.     DB    CR,LF,'  >---------1---------2---------3'
  1795.     DB    '---------4---------5---------6---------7<end',CR,LF,0
  1796. ;
  1797. AFIND5:    CALL    DILPRT
  1798.     DB    CR,LF,0
  1799.     XRA    A
  1800.     STA    ANYET        ; Reset the flag for no information yet
  1801.      ENDIF
  1802. ;
  1803.      IF    MSGDSC AND (NOT    DESCRIB)
  1804.     LXI    H,HLINE2    ; Add blank line for MFMSG
  1805.     CALL    DSTOR1
  1806.      ENDIF
  1807. ;
  1808.      IF    MSGDSC OR DESCRIB
  1809.     MVI    C,'0'
  1810. EXPLN:    INR    C        ; Begin line count at '1'
  1811.     MOV    A,C
  1812.     CPI    '7'+1        ; Reached limit?
  1813.     JNC    EXPL1        ; Yes, so finish up
  1814.     CALL    TYPE
  1815.      ENDIF
  1816. ;
  1817.      IF    DESCRIB    AND (NOT MSGDSC)
  1818.     MVI    A,' '        ; Stuff spaces for FOR format
  1819.     CALL    OUTCHR
  1820.     CALL    OUTCHR
  1821.     CALL    OUTCHR
  1822.      ENDIF
  1823. ;
  1824.      IF    MSGDSC OR DESCRIB
  1825.     CALL    DILPRT
  1826.     DB    ': ',0
  1827.     CALL    DESC        ; Get a line of information
  1828.     CALL    DSTOR
  1829.     JMP    EXPLN
  1830. ;
  1831. EXPL1:    LXI    H,HLINE3    ; All done, add CR/LF
  1832. ;
  1833. EXPL1A:    MOV    A,M        ; Get next character
  1834.     ORA    A        ; Finished?
  1835.     CALL    OUTCHR        ; Transfer to buffer regardless
  1836.     INX    H        ; Bump to next character
  1837.     JZ    EXPL1B        ; Yes, all done, else...
  1838.     JMP    EXPL1A        ; Loop
  1839. ;
  1840. EXPL1B:    CALL    DILPRT
  1841.     DB    CR,LF
  1842.     DB    '   Please verify your description:'
  1843.     DB    CR,LF,LF,0
  1844.     LHLD    BUFADR        ; Get starting address of description
  1845. ;
  1846. EXPL1C:    MOV    A,M        ; Put character in A
  1847.     ORA    A        ; Finished?
  1848.     JZ    EXPL1D        ; Yes, else...
  1849.     CALL    TYPE        ; Show it
  1850.     INX    H        ; Bump to next character
  1851.     JMP    EXPL1C        ; Loop until done
  1852. ;
  1853. EXPL1D:    LHLD    OUTPTR
  1854.     DCX    H
  1855.     SHLD    OUTPTR
  1856. ;
  1857. EXPL2:    CALL    DILPRT
  1858.     DB    'Is this correct? (Y/N) ',0
  1859. ;
  1860. EXPL2A:    CALL    INPUT
  1861.     ANI    5FH        ; Change to upper case
  1862.     CPI    'Y'        ; Entry ok?
  1863.     JZ    EXPL4        ; Yes, so check for more and exit
  1864.     CPI    'N'        ; No?
  1865.     JNZ    EXPL2A        ; Must be Y or N for fumble fingers...
  1866.     CALL    TYPE        ; Display answer
  1867. ;
  1868. EXPL3:    LHLD    BCHPTR        ; Else restart at beginning of text
  1869.     SHLD    OUTPTR        ; Start over at this address
  1870.     JMP    ASK3        ; Go do this one again
  1871. ;
  1872. ; See if any more batch files need descriptions
  1873. ;
  1874. EXPL4:    CALL    TYPE        ; Display answer
  1875.     LXI    H,FCB        ; Zero the FCB area for next file
  1876.     CALL    INITFCB1
  1877.     LDA    FILCNT        ; Any more file names left in buffer?
  1878.     ORA    A
  1879.     JZ    EXPL5        ; If not, all finished
  1880.     LHLD    BCHADR        ; Get the current output address
  1881.     SHLD    BUFADR        ; Store for next verify
  1882.     LHLD    OUTPTR        ; Get end of current description
  1883.     SHLD    BCHPTR        ; Store for start of next one
  1884.     JMP    ASK1-3        ; Get the next file description (CALL BCHDCR)
  1885.      ENDIF
  1886. ;
  1887. ; Now open the file and put this at the beginning
  1888. ;
  1889. EXPL5:     IF    MSGDSC OR DESCRIB
  1890.     LDA    4        ; Get current drive/user
  1891.     STA    DRUSER        ; Store
  1892.      ENDIF
  1893. ;
  1894.      IF    MSGDSC AND (NOT    DESCRIB)
  1895.     LDA    XPRUSR
  1896.     MOV    E,A        ; Set user to UPLOADS area - private
  1897.      ENDIF
  1898. ;
  1899.      IF    DESCRIB    AND (NOT MSGDSC)
  1900.     MVI    E,USER        ; Set user to FOR file user area
  1901.      ENDIF
  1902. ;
  1903.      IF    MSGDSC OR DESCRIB
  1904.     CALL    USRSET        ; Set according to E
  1905.      ENDIF
  1906. ;
  1907.      IF    MSGDSC AND (NOT    DESCRIB)
  1908.     LDA    XPRDRV        ; Set drive to UPLOADS drive - private
  1909.      ENDIF
  1910. ;
  1911.      IF    DESCRIB    AND (NOT MSGDSC)
  1912.     MVI    A,DRIVE        ; Set drive to FOR file drive
  1913.      ENDIF
  1914. ;
  1915.      IF    MSGDSC OR DESCRIB
  1916.     SUI    41H
  1917.     MOV    E,A
  1918.     CALL    DRVSET
  1919. ;
  1920. ; Open source file
  1921. ;
  1922.     CALL    DILPRT
  1923.     DB    CR,LF,0
  1924.     LXI    D,FILE        ; Open FOR text file
  1925.     CALL    OPENIT
  1926.      ENDIF
  1927. ;
  1928.      IF    DESCRIB    AND (NOT MSGDSC)
  1929.     INR    A        ; Check for no open
  1930.     JNZ    OFILE        ; File exists, exit
  1931.      ENDIF
  1932. ;
  1933.      IF    MSGDSC OR DESCRIB
  1934.     MVI    C,MAKE        ; None exists, make a new file
  1935.     LXI    D,FILE
  1936.     CALL    BDOS
  1937.     INR    A
  1938.     JZ    NOROOM        ; Exit if cannot open new file
  1939. ;
  1940. OFILE:    LXI    H,FILE        ; Otherwise use same filename
  1941.     LXI    D,DEST        ; With .$$$ extent for now
  1942.     MVI    B,9
  1943.     CALL    MOVE
  1944. ;
  1945. ; Open the destination file
  1946. ;
  1947.     XRA    A
  1948.     STA    DEST+12
  1949.     STA    DEST+32
  1950.     LXI    H,BSIZE        ; Get Buffer allocated size
  1951.     SHLD    OUTSIZ        ; Set for comparison
  1952.     CALL    DELDES        ; Delete any matching file
  1953.     MVI    C,MAKE        ; Now make a new file that name
  1954.     LXI    D,DEST
  1955.     CALL    BDOS
  1956.     INR    A
  1957.     JZ    NOROOM        ; Cannot open file, no directory room
  1958.     CALL    DILPRT
  1959.     DB    CR,LF,'Saving your description, one moment...',0
  1960. ;
  1961. ; Read sector from source file
  1962. ;
  1963. READLP:    CALL    RSTDMA        ; Reset to default DMA
  1964.     MVI    C,READ
  1965.     LXI    D,FILE        ; Read from FOR text file
  1966.     CALL    BDOS
  1967.     ORA    A        ; Read ok?
  1968.     JNZ    RERROR
  1969.     LXI    H,TBUF        ; Read buffer address
  1970. ;
  1971. ; Write sector to output file (with buffering)
  1972. ;
  1973. WRDLOP:    MOV    A,M        ; Get byte from read buffer
  1974.     ANI    7FH        ; Strip parity bit
  1975.     CPI    7FH        ; Del (rubout)?
  1976.     JZ    NEXT        ; Yes, ignore it
  1977.     CPI    EOF        ; End of file marker?
  1978.     JZ    TDONE        ; Transfer done, close, exit
  1979.     CALL    OUTCHR
  1980. ;
  1981. NEXT:    INR    L        ; Done with sector?
  1982.     JZ    READLP        ; If yes get another sector
  1983.     JMP    WRDLOP        ; No, get another byte
  1984. ;
  1985. ; Handle a backspace character while entering a character string
  1986. ;
  1987. BCKSP:    CALL    TYPE
  1988.     MOV    A,B        ; Get position on line
  1989.     ORA    A
  1990.     JNZ    BCKSP1        ; Exit if at initial column
  1991.     CALL    SENBEL        ; Send a bell to the modem
  1992.     MVI    A,' '        ; Delete the character
  1993.     JMP    BCKSP3
  1994. ;
  1995. BCKSP1:    DCR    B        ; Show one less column used
  1996.     DCX    H        ; Decrease buffer location
  1997.     MVI    A,' '
  1998.     MOV    M,A        ; Clear memory at this point
  1999.     CALL    TYPE        ; Backspace the "CRT"
  2000. ;
  2001. BCKSP2:    MVI    A,BS        ; Reset the "CRT" again
  2002. ;
  2003. BCKSP3:    JMP    TYPE        ; Write to the "CRT", done
  2004. ;
  2005. ; Asks for line of information
  2006. ;
  2007. DESC:    MVI    B,0
  2008.     LXI    H,OLINE
  2009. ;
  2010. DESC1:    CALL    INPUT        ; Get keyboard character
  2011.     CPI    CR
  2012.     JZ    DESC4
  2013.     CPI    TAB
  2014.     JZ    DESC6
  2015.     CPI    BS
  2016.     JNZ    DESC2
  2017.     CALL    BCKSP
  2018.     JMP    DESC1        ; Get the next character
  2019. ;
  2020. DESC2:    CPI    ' '
  2021.     JC    DESC1        ; If non-printing character, ignore
  2022.     JZ    DESC3        ; A space, so set skip next, else...
  2023.     STA    ANYET        ; Show a character has been sent now
  2024. ;
  2025. DESC3:    MOV    M,A
  2026.     CALL    TYPE        ; Display the character
  2027.     INX    H
  2028.     INR    B
  2029.     CPI    ' '
  2030.     JZ    DESC3B
  2031. ;
  2032. DESC3A:    MOV    A,B
  2033.     CPI    71        ; Do not exceed line length
  2034.     JC    DESC1
  2035.     CALL    SENBEL        ; Send a bell to the modem
  2036.     CALL    BCKSP2
  2037.     CALL    BCKSP1        ; Do not allow a too-long line
  2038.     JMP    DESC1
  2039. ;
  2040. DESC3B:    LDA    XWRAP
  2041.     CMP    B        ; Time for next line?
  2042.     JC    DESC5        ; Yes, else
  2043.     JMP    DESC3A
  2044. ;
  2045. DESC4:    LDA    ANYET        ; Any text typed on first line yet?
  2046.     ORA    A
  2047.     JNZ    DESC5        ; If yes, exit
  2048.     POP    H
  2049.     JMP    EXPL3        ; Ask again for a description
  2050. ;
  2051. DESC5:    MVI    M,CR
  2052.     MOV    A,M
  2053.     CALL    TYPE
  2054.     INX    H        ; Ready for next character
  2055.     MVI    M,LF
  2056.     MOV    A,M
  2057.     CALL    TYPE        ; Display the line feed
  2058.     INX    H
  2059.     MOV    A,B        ; See if at first of line
  2060.     ORA    A
  2061.     RNZ            ; If not, ask for next line
  2062.     POP    H        ; Clear "CALL" from stack
  2063.     JMP    EXPL1
  2064. ;
  2065. DESC6:    MOV    A,B
  2066.     ADI    8
  2067.     CPI    71        ; Would this be past the limit?
  2068.     JC    DESC7        ; No, so do tab function
  2069.     JMP    DESC5        ; ...else, start a new line
  2070. ;
  2071. DESC7:    MVI    M,' '
  2072.     MOV    A,M
  2073.     CALL    TYPE
  2074.     INX    H
  2075.     INR    B
  2076.     MOV    A,B
  2077.     ANI    7
  2078.     JNZ    DESC7
  2079.     JMP    DESC1        ; Ask for next character
  2080. ;
  2081. ; Print message then exit to CP/M
  2082. ;
  2083. DEXIT:    POP    D        ; Get message address
  2084.     CALL    PRTSET        ; Print message
  2085.     JMP    RESET        ; Reset the drive/user, then finished
  2086. ;
  2087. ; Inline print routine - prints string pointed to by stack until a zero
  2088. ; is found.  Returns to caller at the next address after the zero ter-
  2089. ; minator.
  2090. ;
  2091. DILPRT:    XTHL            ; Save HL, get message address
  2092. ;
  2093. DILPLP:    MOV    A,M        ; Get character
  2094.     INX    H        ; Next character in the string
  2095.     ORA    A
  2096.     JZ    DILPL1
  2097.     CALL    TYPE        ; Output it
  2098.     JMP    DILPLP
  2099. ;
  2100. DILPL1:    XTHL            ; Restore HL, ret address
  2101.     RET            ; Return past the end of the message
  2102. ;
  2103. DKIND:    LDAX    D        ; Get the character from the string
  2104.     CALL    TYPE        ; Otherwise display the character
  2105.     MOV    M,A        ; Put in the buffer
  2106.     CPI    LF        ; Done yet?
  2107.      ENDIF
  2108. ;
  2109.      IF    MSGDSC AND (NOT    DESCRIB)
  2110.     RZ            ; Exit if LF, done
  2111.      ENDIF
  2112. ;
  2113.      IF    DESCRIB    AND (NOT MSGDSC)
  2114.     JZ    DKIND1        ; Exit if a LF, done
  2115.      ENDIF
  2116. ;
  2117.      IF    MSGDSC OR DESCRIB
  2118.     INX    D        ; Next position in the string
  2119.     INX    H        ; Next position in the buffer
  2120.     JMP    DKIND        ; Keep going until a LF
  2121. ;
  2122. DKIND1:    LDA    KIND        ; Get the kind of file back
  2123.     RET            ; Finished
  2124. ;
  2125. DSTOR:    LXI    H,OLINE
  2126. ;
  2127. DSTOR1:    MOV    A,M
  2128.     CALL    OUTCHR
  2129.     CPI    LF
  2130.     RZ
  2131.     INX    H
  2132.     JMP    DSTOR1
  2133. ;
  2134. DSTOR2:    MOV    A,M        ; Get next character
  2135.     ORA    A        ; Finished?
  2136.     RZ            ; Yes, else continue
  2137.     CALL    OUTCHR        ; Transfer to buffer
  2138.     INX    H        ; Bump to next character
  2139.     JMP    DSTOR2        ; Loop until done
  2140.      ENDIF
  2141. ;
  2142.      IF    MSGDSC AND (NOT    DESCRIB)
  2143. DSTOR3:    MVI    B,2        ; Set counter
  2144.     INX    H        ; Skip first character (MSPEED)
  2145.     INX    H        ; And second character (Special)
  2146. ;
  2147. DSTR3:    MOV    A,M        ; Get next character
  2148.     ORA    A        ; Finished?
  2149.     RZ            ; Yes, else continue
  2150.     CPI    ';'        ; Change to space
  2151.     JNZ    DSTR3A        ; ...else no change
  2152.     DCR    B        ; Stop after last name
  2153.     RZ            ; Return after last name stored
  2154.     MVI    A,' '
  2155. ;
  2156. DSTR3A:    CALL    OUTCHR        ; Transfer to buffer
  2157.     INX    H        ; Bump to next character
  2158.     JMP    DSTR3        ; Loop until done
  2159.      ENDIF
  2160. ;
  2161. ; Disk is full, save original file, erase others.
  2162. ;
  2163.      IF    MSGDSC OR DESCRIB
  2164. FULL:    CALL    DELDES        ; Delete new file, restore old
  2165.     CALL    DEXIT
  2166.     DB    CR,LF,'++ Disk full - saving original file ++','$'
  2167.      ENDIF
  2168. ;
  2169. ; Get a character, if none ready wait up to DESWAIT minutes, then exit
  2170. ; program.
  2171. ;
  2172. INPUT:    PUSH    H        ; Save current values
  2173.     PUSH    D
  2174.     PUSH    B
  2175.     LDA    DESWAIT
  2176.     ADD    A
  2177.     MOV    L,A        ; Save it
  2178. ;
  2179. INPUT1:    LXI    D,300        ; Approx 30 sec loop
  2180. ;
  2181. INPUT2:    LXI    B,MHZ*77    ; Gives about 77 ms
  2182. ;
  2183. INPUT3:    PUSH    H
  2184.     PUSH    D        ; Save the outer delay count
  2185.     PUSH    B        ; Save the inner delay count
  2186.     MVI    C,DIRCON    ; Get console status
  2187.     MVI    E,0FFH
  2188.     CALL    BDOS
  2189.     ANI    7FH
  2190.     POP    B        ; Restore the inner delay count
  2191.     POP    D        ; Restore the outer delay count
  2192.     POP    H
  2193.     ORA    A        ; Have a character yet?
  2194.     JNZ    INPUT4        ; If yes, exit and get it
  2195.     DCX    B
  2196.     MOV    A,C        ; See if inner loop is finished
  2197.     ORA    B
  2198.     JNZ    INPUT3        ; If not loop again
  2199.     DCX    D
  2200.     MOV    A,E
  2201.     ORA    D
  2202.     JNZ    INPUT2        ; If not reset inner loop and go again
  2203.     PUSH    H
  2204.     CALL    SENBEL        ; 30 secs passed - no input
  2205.     POP    H
  2206.     DCR    L        ; Countdown DESWAIT period
  2207.     JNZ    INPUT1        ; Start next 30 sec timer
  2208. ;
  2209.      IF    MSGDSC OR DESCRIB
  2210.     MVI    A,CR
  2211.     CALL    OUTCHR
  2212.     MVI    A,LF
  2213.     CALL    OUTCHR
  2214.     LXI    SP,STACK    ; Restore the stack
  2215.     CALL    EXPL5        ; Finish appending previous information
  2216.      ENDIF
  2217. ;
  2218.     JMP    EXIT        ; File is closed, return to CP/M
  2219. ;
  2220. INPUT4:    POP    B
  2221.     POP    D
  2222.     POP    H
  2223.     RET            ; Got a character, return with it
  2224. ;
  2225. ; Stores the Filename/extent in the buffer temporarily
  2226. ;
  2227.      IF    MSGDSC OR DESCRIB
  2228. LOPFCB:    LDAX    D        ; Get FCB FILENAME/EXT character
  2229.     CPI    ' '+1        ; Skip any blanks
  2230.     JC    LOPF1
  2231.     MOV    M,A        ; Store in OLINE area
  2232.     CALL    TYPE        ; Display on CRT
  2233.     INX    H        ; Next OLINE position
  2234. ;
  2235. LOPF1:    INX    D        ; Next FCB position
  2236.     DCR    B        ; One less to go
  2237.     JNZ    LOPFCB        ; If not done, get next one
  2238.     RET
  2239. ;
  2240. ; No room to open a new file
  2241. ;
  2242. NOROOM:    CALL    DEXIT
  2243.     DB    CR,LF,'++ No DIR space ++','$'
  2244. ;
  2245. ; Output error - cannot close destination file
  2246. ;
  2247. OERROR:    CALL    DEXIT
  2248.     DB    CR,LF,'++ Can''t close output file ++','$'
  2249. ;
  2250. ; See if there is room in the buffer for this character
  2251. ;
  2252. OUTCHR:    PUSH    H
  2253.     PUSH    PSW        ; Store the character for now
  2254.     LHLD    OUTSIZ        ; Get buffer size
  2255.     XCHG            ; Put in 'DE'
  2256.     LHLD    OUTPTR        ; Now get the buffer pointers
  2257.     MOV    A,L        ; Check to see if room in buffer
  2258.     SUB    E
  2259.     MOV    A,H
  2260.     SBB    D
  2261.     JC    OUT3        ; If room, go store the character
  2262.     LXI    H,0        ; Otherwise reset the pointers
  2263.     SHLD    OUTPTR        ; Store the new pointer address
  2264. ;
  2265. OUT1:    XCHG            ; Put pointer address into 'DE'
  2266.     LHLD    OUTSIZ        ; Get the buffer size into 'HL'
  2267.     MOV    A,E        ; See if buffer is max. length yet
  2268.     SUB    L        ; By subtracting 'HL' from 'DE'
  2269.     MOV    A,D
  2270.     SBB    H
  2271.     JNC    OUT2        ; If less, exit and keep going
  2272. ;
  2273. ; No more room in buffer, stop and transfer to destination file
  2274. ;
  2275.     LHLD    OUTADR        ; Get the buffer address
  2276.     DAD    D        ; Add pointer value
  2277.     XCHG            ; Put into 'DE'
  2278.     CALL    DMASET        ; Set DMA to buffer
  2279.     MVI    C,WRITE
  2280.     LXI    D,DEST
  2281.     CALL    BDOS
  2282.     ORA    A
  2283.     JNZ    FULL        ; Exit with error, if disk is full now
  2284.     LXI    D,RLEN
  2285.     LHLD    OUTPTR
  2286.     DAD    D
  2287.     SHLD    OUTPTR
  2288.     JMP    OUT1
  2289. ;
  2290. OUT2:    CALL    RSTDMA        ; Reset to default DMA
  2291.     LXI    H,0
  2292.     SHLD    OUTPTR
  2293. ;
  2294. OUT3:    XCHG
  2295.     LHLD    OUTADR
  2296.     DAD    D
  2297.     XCHG
  2298.     POP    PSW        ; Get the character back
  2299.     STAX    D        ; Store the character
  2300.     XCHG
  2301.     SHLD    BCHADR
  2302.     LHLD    OUTPTR        ; Get the buffer pointer
  2303.     INX    H        ; Increment them
  2304.     SHLD    OUTPTR        ; Store the new pointer address
  2305.     POP    H
  2306.     RET
  2307. ;
  2308. RERROR:    CPI    1        ; File finished?
  2309.     JZ    TDONE        ; Exit, then
  2310.     CALL    DELDES        ; Delete new, restore old file
  2311.     CALL    DEXIT
  2312.     DB    '++ Source file read error ++','$'
  2313.      ENDIF            ; MSGDSC OR DESCRIB
  2314. ;
  2315. ; Reset the Drive/User to original
  2316. ;
  2317. RESET:     IF    MSGDSC AND (NOT    DESCRIB)
  2318.     LDA    DSCFLG
  2319.     ORA    A        ; Upload description entered?
  2320.     JZ    RESET1        ; No, so skip next 7 lines, else...
  2321.     MVI    C,CURDRV    ; Get current drive of 'UPLOADS'
  2322.     CALL    BDOS
  2323.     STA    DSKSAV        ; Save for MFMSG
  2324.     MVI    E,0FFH
  2325.     CALL    USRSET        ; Get 'UPLOADS' area
  2326.     STA    USRSAV        ; Save for MFMSG
  2327. RESET1:     ENDIF
  2328. ;
  2329.      IF    MSGDSC OR DESCRIB
  2330.     LDA    DRUSER        ; Get original drive/user area back
  2331.     RAR
  2332.     RAR
  2333.     RAR
  2334.     RAR
  2335.     ANI    0FH        ; Just look at the user area
  2336.     MOV    E,A
  2337.     CALL    USRSET        ; Restore original user area
  2338.     LDA    DRUSER        ; Get the original drive/user back
  2339.     ANI    0FH        ; Just look at the drive for now
  2340.     MOV    E,A
  2341.     CALL    DRVSET        ; Restore original drive
  2342.     CALL    DILPRT        ; Print CRLF before quitting
  2343.     DB    CR,LF,0
  2344.     RET            ; To: CALL  ASK
  2345. ;
  2346. ; Shows the Filename/extent
  2347. ;
  2348. SHONM:    CALL    DILPRT
  2349.     DB    CR,LF,LF,0
  2350.     LXI    H,FCB+1
  2351.     MVI    B,8        ; Maximum size of file name
  2352.     CALL    SHONM1
  2353.     MOV    A,M        ; Get the next character
  2354.     CPI    ' '        ; Any file extent?
  2355.     RZ            ; If not, finished
  2356.     MVI    A,'.'
  2357.     CALL    TYPE
  2358.     MVI    B,3        ; Maximum size of file extent
  2359. ;
  2360. SHONM1:    MOV    A,M        ; Get FCB FILENAME/EXT character
  2361.     CPI    ' '+1        ; Skip any blanks
  2362.     JC    $+6
  2363.     CALL    TYPE        ; Display on CRT
  2364.     INX    H        ; Next FCB position
  2365.     DCR    B        ; One less to go
  2366.     JNZ    SHONM1        ; If not done, get next one
  2367.     RET
  2368. ;
  2369. ; Transfer is done - close destination file
  2370. ;
  2371. TDONE:    LHLD    OUTPTR
  2372.     MOV    A,L
  2373.     ANI    RLEN-1
  2374.     JNZ    TDONE1
  2375.     SHLD    OUTSIZ
  2376. ;
  2377. TDONE1:    MVI    A,EOF        ; Fill remainder of record with ^Z's
  2378.     PUSH    PSW
  2379.     CALL    OUTCHR
  2380.     POP    PSW
  2381.     JNZ    TDONE
  2382.     LXI    D,FILE
  2383.     CALL    CLOSEF        ; Close FOR file
  2384.     LXI    D,DEST
  2385.     CALL    CLOSEF        ; Close FOR.$$$ file
  2386.     INR    A
  2387.     JZ    OERROR
  2388. ;
  2389. ;  Rename both files as no destination file name was specified
  2390. ;
  2391.     LXI    H,FILE+1    ; Prepare to rename old file to new
  2392.     LXI    D,DEST+17
  2393.     MVI    B,16
  2394.     CALL    MOVE
  2395.     LXI    D,FILE
  2396.     CALL    DELFIL        ; Delete original FOR file
  2397.     MVI    C,RENAME
  2398.     LXI    D,DEST        ; Rename FOR.$$$ to FOR text file
  2399.     CALL    BDOS
  2400.     JMP    RESET        ; Reset the drive/user, finished
  2401.      ENDIF            ; MSGDSC OR DESCRIB
  2402. ;
  2403. ; Send a bell just to the modem
  2404. ;
  2405. SENBEL:    CALL    MDOUTST        ; Is modem ready for another character?
  2406.     JZ    SENBEL        ; If not, wait
  2407.     MVI    A,7
  2408.     JMP    MDOUTP        ; Send to the modem only
  2409. ;
  2410. ; Send character in 'A' register to console
  2411. ;
  2412. TYPE:    PUSH    B
  2413.     PUSH    D
  2414.     PUSH    H
  2415.     PUSH    PSW
  2416.     MVI    C,WRCON        ; Write to console
  2417.     MOV    E,A        ; Character to 'E' for CP/M
  2418.     CALL    BDOS
  2419.     POP    PSW
  2420.     POP    H
  2421.     POP    D
  2422.     POP    B
  2423.     RET
  2424. ;
  2425. ; end of file description area
  2426. ; ----------------------------
  2427. ;
  2428. ; Send a character to the modem
  2429. ;
  2430. SEND:    PUSH    PSW        ; Save the character
  2431.     CALL    UPDCRC        ; Calculate CRC
  2432.     ADD    C        ; Calculate checksum
  2433.     MOV    C,A        ; Save cksum
  2434. ;
  2435. SEND1:    CALL    MDOUTST        ; Is transmit ready
  2436.     JZ    SEND2        ; No, check carrier
  2437.     POP    PSW        ; Modem is ready
  2438.     JMP    MDOUTP        ; So send it
  2439. ;
  2440. ; Xmit status not ready, so test for carrier before looping - if lost,
  2441. ; go to CARCK and give it up to 15 seconds to return.  If it doesn't,
  2442. ; return abort via EXIT.
  2443. ;
  2444. SEND2:    PUSH    D        ; Save 'DE'
  2445.     CALL    MDCARCK        ; Is carrier still on?
  2446.     CZ    CARCK        ; If not, continue testing it
  2447.     POP    D        ; Restore 'DE'
  2448.     JMP    SEND1        ; Else, wait for xmit ready
  2449. ;
  2450. ; Waits for initial NAK - to ensure no data is sent until the receiving
  2451. ; program is ready, this routine waits for the first timeout-nak or the
  2452. ; letter 'C' for CRC from the receiver.  If CRC is in effect then Cyclic
  2453. ; Redundancy Checks are used instead of checksums.  'E' contains the
  2454. ; number of seconds to wait.  If the first character received is CANCEL
  2455. ; (^X) then the send will be aborted as though it had timed out.
  2456. ;
  2457. WAITNAK:CALL    FUNCHK        ; Check function keys
  2458.     CALL    SNDABT        ; Check for local abort
  2459.     MVI    B,1        ; Timeout delay
  2460.     STA    CONONL        ; Show future diplays to local CRT only
  2461.     CALL    RECV        ; Wait up to 1 second for character
  2462.     JC    WAITN1        ; No character this time
  2463.     CPI    CRC        ; 'CRC' request?
  2464.     JZ    WAITK
  2465.     CPI    KSND        ; Requesting 1k?
  2466.     JZ    SETK        ; Exit if yes, otherwise set CRC
  2467.     CPI    NAK        ; 'NAK' for checksum?
  2468.     JZ    CHECKY        ; Yes, so check for Ymodem batch request
  2469.     CPI    CANCEL        ; Cancel (^X)?
  2470.     JZ    ABORT        ; Yes, abort
  2471. ;
  2472. WAITN1:    DCR    E        ; Finished yet?
  2473.     JZ    ABORT        ; Yes, abort
  2474.     JMP    WAITNAK        ; No, loop
  2475. ;
  2476. WAITK:    MVI    B,1        ; Got a 'C', wait up to 1 second for 'K'
  2477.     CALL    RECV
  2478.     JC    SETCRC        ; Didn't get anything so not using 1k
  2479.     ANI    7FH
  2480.     CPI    7BH
  2481.     JZ    WAITK        ; Disregard noisy lines
  2482.     CPI    KSND        ; Requesting 1k?
  2483.     JZ    SETK        ; Exit if yes, otherwise set CRC
  2484. ;
  2485. ; Turn on the flag for CRC
  2486. ;
  2487. SETCRC:    LDA    KFLG        ; KFLG manually set from 'SK'?
  2488.     ORA    A
  2489.     JNZ    SETK        ; If yes, keep it set
  2490. ;
  2491. SETC1:    XRA    A
  2492.     STA    KFLG        ; Defaults to 128 character blocks
  2493.     INR    A
  2494.     STA    CRCFLG        ; Insures in CRC mode
  2495.     CALL    ILPRTL
  2496.     DB    'CRC requested',CR,LF,0
  2497.     RET
  2498. ;
  2499. ; Turn on the flag for 1k blocks and insure in CRC mode
  2500. ;
  2501. SETK:    LDA    MSPEED
  2502.     CPI    5        ; 1k request for 1200 bps or more
  2503.     JC    SETC1        ; Don't allow 1k if less than 1200 bps
  2504. SETK1:    STA    KFLG        ; Set the flag for 1k blocks
  2505.     STA    CRCFLG        ; Insures in 'CRC' mode
  2506.     CALL    ILPRTL
  2507.     DB    'Ymodem requested',CR,LF,0
  2508.     RET
  2509. ;
  2510. ; Turn on checksum flag, insure sending 128 character blocks
  2511. ;
  2512. SETNAK:    LDA    BCHFLG        ; In batch mode now?
  2513.     ORA    A
  2514.     JNZ    SETNAK1        ; If yes, exit
  2515.     XRA    A
  2516.     STA    CRCFLG        ; Make sure in checksum mode
  2517.     STA    KFLG        ; Defaults to 128 character blocks
  2518.     CALL    ILPRTL
  2519.     DB    'Checksum requested',CR,LF,0
  2520.     RET            ; From WAITNAK
  2521. ;
  2522. SETNAK1:CALL    ILPRTL
  2523.     DB    CR,LF,'Checksum not used in batch mode',CR,LF,0
  2524.     JMP    WAITNAK        ; Ignore checksum request
  2525. ;
  2526. CHECKY:    LDA    YMODEM        ; Get Ymodem batch status
  2527.     ORA    A        ; Requested?
  2528.     JZ    SETNAK        ; No, put checksum into effect
  2529.     MVI    A,1
  2530.     JMP    SETK1        ; Yes, set CRC and 1k flag
  2531. ;
  2532. ; This routine moves the filename from the default command line buffer
  2533. ; to the file control block (FCB).
  2534. ;
  2535. MOVEFCB:LHLD    SAVEHL        ; Get position on command line
  2536.     CALL    GETB        ; Get numeric position
  2537.     LXI    D,FCB+1
  2538.     CALL    MOVENAM        ; Move name to FCB
  2539.     XRA    A
  2540.     STA    FCBRNO        ; Zero record number
  2541.     STA    FCBEXT        ; Zero extent
  2542.     LDA    OPTSAV
  2543.     CPI    'A'        ; .ARK/.ARC?
  2544.     JZ    MOVEFA
  2545.     CPI    'L'        ; .LBR?
  2546.     RNZ            ; If neither, finished
  2547. ;
  2548. ; Handles library entries, first checks for proper .LBR extent.  If no
  2549. ; extent was included, it adds one itself.
  2550. ;
  2551.     SHLD    SAVEHL
  2552.     LXI    H,FCBTYP    ; 1st extent character
  2553.     MOV    A,M
  2554.     CPI    ' '
  2555.     JZ    NOLEXT        ; No extent, make one
  2556.     CPI    'L'        ; Check 1st character in extent
  2557.     JNZ    LBRERR
  2558.     INX    H
  2559.     MOV    A,M
  2560.     CPI    'B'        ; Check 2nd character in extent
  2561.     JNZ    LBRERR
  2562.     INX    H
  2563.     MOV    A,M
  2564.     CPI    'R'        ; Check 3rd character in extent
  2565.     JNZ    LBRERR
  2566.     JMP    MOVEF1
  2567. ;
  2568. ; Handles .ARK/.ARChive entries - first checks for proper type.  If none
  2569. ; specified, .ARK is forced.
  2570. ;
  2571. MOVEFA:    SHLD    SAVEHL
  2572.     LXI    H,FCBTYP    ; 1st extent character
  2573.     MOV    A,M
  2574.     CPI    ' '
  2575.     JZ    NOAEXT        ; No extent, make one
  2576.     CPI    'A'        ; Check 1st character in extent
  2577.     JNZ    LBRERR
  2578.     INX    H
  2579.     MOV    A,M
  2580.     CPI    'R'        ; Check 2nd character in extent
  2581.     JNZ    LBRERR
  2582.     INX    H
  2583.     MOV    A,M
  2584.     CPI    'K'        ; Check 3rd character in extent
  2585. ;
  2586.      IF    ARCEXT
  2587.     JZ    MOVEF1        ; Was .ARK
  2588.     CPI    'C'        ; May be .ARC?
  2589.      ENDIF
  2590. ;
  2591.     JNZ    LBRERR        ; Neither, abort
  2592. ;
  2593. ; Get the name of the desired file in the library
  2594. ;
  2595. MOVEF1:    LHLD    SAVEHL        ; Get current position on command line
  2596.     CALL    CHKMSP        ; See if valid library member file name
  2597.     INR    B        ; Increment for move name
  2598.     LXI    D,MEMFCB    ; Store member name in special buffer
  2599.     JMP    MOVENAM        ; Move from command line to buffer, done
  2600. ;
  2601. ; Check for any spaces prior to library member file name, if none (or
  2602. ; only spaces remaining), no name.
  2603. ;
  2604. CHKMSP:    DCR    B
  2605.     JZ    MEMERR
  2606.     MOV    A,M
  2607.     CPI    ' '+1
  2608.     RNC
  2609.     INX    H
  2610.     JMP    CHKMSP
  2611. ;
  2612. ; Gets the count of characters remaining on the command line
  2613. ;
  2614. GETB:    MOV    A,L
  2615.     SUI    TBUF+2        ; Start location of 1st command
  2616.     MOV    B,A        ; Store for now
  2617.     LDA    TBUF        ; Find length of command line
  2618.     SUB    B        ; Subtract those already used
  2619.     MOV    B,A        ; Now have number of bytes remaining
  2620.     RET
  2621. ;
  2622. LBRERR:    CALL    ERXIT
  2623.     DB    CR,LF
  2624.     DB    '++ No .LBR/.ARK/.ARC file with that name ++','$'
  2625. ;
  2626. MEMERR:    CALL    ILPRT
  2627.     DB    CR,LF,LF
  2628.     DB    '++ Must request an internal file member ++'
  2629.     DB    CR,LF,0
  2630.     JMP    OPTERR
  2631. ;
  2632. ; Add .LBR extent to the library file name
  2633. ;
  2634. NOLEXT:    LXI    H,FCB+9        ; Location of extent
  2635.     MVI    M,'L'
  2636.     INX    H
  2637.     MVI    M,'B'
  2638.     INX    H
  2639.     MVI    M,'R'
  2640.     JMP    MOVEF1        ; Now get the library member name
  2641. ;
  2642. ; Add .ARK extent to the file name
  2643. ;
  2644. NOAEXT:    LXI    H,FCB+9        ; Location of extent
  2645.     MVI    M,'A'
  2646.     INX    H
  2647.     MVI    M,'R'
  2648.     INX    H
  2649.     MVI    M,'K'
  2650.     JMP    MOVEF1        ; Now get the library member name
  2651. ;
  2652. ; Move a file name from the 'TBUF' command line buffer into FCB
  2653. ;
  2654. MOVENAM:MVI    C,1
  2655. ;
  2656. MOVEN1:    MOV    A,M
  2657.     CPI    ' '+1        ; Name ends with space or return
  2658.     JC    FILLSP        ; Fill with spaces if needed
  2659.     CPI    '.'
  2660.     JZ    CHKFIL        ; File name might be less than 8 chars.
  2661.     STAX    D        ; Store
  2662.     INX    D        ; Next position to store the character
  2663.     INR    C        ; One less to go
  2664.     MOV    A,C
  2665.     CPI    12+1
  2666.     JNC    NONAME        ; 11 chars. maximum filename plus extent
  2667. ;
  2668. MOVEN2:    INX    H        ; Next char. in file name
  2669.     DCR    B
  2670.     JZ    OPTERR        ; End of name, see if done yet
  2671.     JMP    MOVEN1
  2672. ;
  2673. ; See if any spaces needed between file name and .ext
  2674. ;
  2675. CHKFIL:    CALL    FILLSP        ; Fill with spaces
  2676.     JMP    MOVEN2
  2677. ;
  2678. FILLSP:    MOV    A,C
  2679.     CPI    9
  2680.     RNC            ; Up to 1st character in .ext now
  2681.     MVI    A,' '        ; Be sure there is a blank there now
  2682.     STAX    D
  2683.     INR    C
  2684.     INX    D
  2685.     JMP    FILLSP        ; Go do another
  2686. ;
  2687. CTYPE:    PUSH    B        ; Save all registers
  2688.     PUSH    D
  2689.     PUSH    H
  2690.     MOV    E,A        ; Character to 'E' in case BDOS (normal)
  2691.     LDA    CONONL
  2692.     ORA    A        ; Bypass NUBYE output to modem?
  2693.     JNZ    CTYPEL        ; Yes, go directly to CRT, then
  2694.     MVI    C,WRCON        ;  BDOS console output, to CRT and modem,
  2695.     CALL    BDOS        ;  since NUBYE intercepts the char.
  2696.     POP    H        ; Restore all registers
  2697.     POP    D
  2698.     POP    B
  2699.     RET
  2700. ;
  2701. CTYPEL:    MOV    C,E        ; BIOS needs it in 'C'
  2702.     CALL    CONOUT        ; BIOS console output routine, not BDOS
  2703.     POP    H        ; Restore all registers saved by 'CTYPE'
  2704.     POP    D
  2705.     POP    B
  2706.     RET
  2707. ;
  2708. ; Inline print of message, terminates with a 0
  2709. ;
  2710. ILPRTB:    XRA    A        ; Switch on remote display
  2711.     JMP    ILPRTL+2
  2712. ;
  2713. ILPRTL:    MVI    A,1
  2714.     STA    CONONL        ; 1=local only, 0=both local and remote
  2715. ;
  2716. ILPRT:    XTHL            ; Save HL, get HL=message
  2717. ;
  2718. ILPLP:    MOV    A,M        ; Get the character
  2719.     INX    H        ; To next character
  2720.     ORA    A        ; End of message?
  2721.     JZ    ILPRET        ; Yes, return
  2722.     CALL    CTYPE        ; Type the message
  2723.     JMP    ILPLP        ; Loop
  2724. ;
  2725. ILPRET:    XTHL            ; Restore HL
  2726.     RET            ; Past message
  2727. ;
  2728.      IF    RESUSR
  2729. DENIED:    CALL    ERXIT
  2730.     DB    '++ Restricted Function ++','$'
  2731.      ENDIF
  2732. ;
  2733. ; Inline print of message - terminates with '$'
  2734. ;
  2735.      IF    CLRSCR
  2736. PRINTL:    POP    H        ; Get address of next character
  2737.     MOV    A,M        ; Get character
  2738.     INX    H        ; Increment to next character
  2739.     PUSH    H        ; Save address
  2740.     CPI    '$'        ; End of message?
  2741.     RZ            ; If '$' is end of message
  2742.     CALL    CTYPE        ; Else print character on console
  2743.     JMP    PRINTL        ; And repeat until abort/end
  2744.      ENDIF
  2745. ;
  2746. ; Print error message, then exit NUKMD
  2747. ;
  2748. ERXIT:    CALL    ILPRT
  2749.     DB    CR,LF,0
  2750. ;
  2751. ERXIT1:    POP    H        ; Get address of next character
  2752.     MOV    A,M        ; Get character
  2753.     INX    H        ; Increment to next character
  2754.     PUSH    H        ; Save address
  2755.     CPI    '$'        ; End of message?
  2756.     JZ    EXITXL        ; If '$' is end of message
  2757.     CALL    CTYPE        ; Else print character on console
  2758.     JMP    ERXIT1        ; And repeat until abort/end
  2759. ;
  2760. EXITXL:    CALL    ILPRT
  2761.     DB    CR,LF,0
  2762. ;
  2763. ERXITX:    POP    H        ; Restore stack
  2764.     CALL    CATCH        ; Clear the input
  2765.     XRA    A
  2766.     STA    OPTSAV        ; Reset option to zero for TELL
  2767.     STA    MSGFLG        ; Reset the message file upload flag
  2768.     JMP    EXIT        ; Get out of here
  2769. ;
  2770. ; Pause for user input, after displaying text
  2771. ;
  2772. MORE:    CALL    ILPRT
  2773.     DB    CR,LF,LF,'Hit any key to continue...',0
  2774. MORE1:    MVI    C,6        ; Check keyboard status
  2775.     MVI    E,0FFH
  2776.     CALL    BDOS
  2777.     ORA    A        ; Anything?
  2778.     JZ    MORE1        ; No, continue to wait
  2779.     RET
  2780. ;
  2781. ; Restore the old user area and drive from a received file
  2782. ;
  2783. RECAREA:CALL    RECDRV        ; Ok set the drive to its place
  2784.     LDA    PRVTFL        ; Private area wanted?
  2785.     ORA    A
  2786.     LDA    XPRUSR        ; Yes, set to private area
  2787.     JNZ    RECARE
  2788.     LDA    XUSR        ; Ok now set the user area
  2789. ;
  2790. RECARE:    MOV    E,A        ; Stuff it in E
  2791.     MVI    C,SETUSR    ; Tell BDOS what we want to do
  2792.     JMP    BDOS        ; Now do it
  2793. ;
  2794. RECDRV:    LDA    PRVTFL
  2795.     ORA    A
  2796.     LDA    XPRDRV        ; Get private upload drive
  2797.     JNZ    RECDR1
  2798.     LDA    XDRV        ; Or forced upload drive
  2799. ;
  2800. RECDR1:    SUI    'A'        ; Adjust it
  2801. ;
  2802. RECDRX:    MOV    E,A        ; Stuff it in E
  2803.     MVI    C,SELDSK    ; Tell BDOS
  2804.     JMP    BDOS        ; Do it
  2805. ;
  2806. ; ===============
  2807. ; CRC SUBROUTINES
  2808. ; ===============
  2809. ;
  2810. CRCCHK:    PUSH    H        ; Check 'CRC' bytes of received message
  2811.     LHLD    CRCVAL
  2812.     MOV    A,H
  2813.     ORA    L
  2814.     POP    H
  2815.     RZ            ; Return with zero flag set if ok
  2816.     MVI    A,0FFH        ; Else clear the flag to show an error
  2817.     RET
  2818. ;
  2819. FINCRC:    PUSH    PSW        ; Finish 'CRC' calculation for last xmsn
  2820.     XRA    A
  2821.     CALL    UPDCRC
  2822.     CALL    UPDCRC
  2823.     PUSH    H
  2824.     LHLD    CRCVAL
  2825.     MOV    D,H
  2826.     MOV    E,L
  2827.     POP    H
  2828.     POP    PSW
  2829.     RET
  2830. ;
  2831. UPDCRC:    PUSH    PSW        ; Update 'CRC' store  with byte in 'A'
  2832.     PUSH    B
  2833.     PUSH    H
  2834.     MVI    B,8
  2835.     MOV    C,A
  2836.     LHLD    CRCVAL
  2837. ;
  2838. UPDLOOP:MOV    A,C
  2839.     RLC
  2840.     MOV    C,A
  2841.     MOV    A,L
  2842.     RAL
  2843.     MOV    L,A
  2844.     MOV    A,H
  2845.     RAL
  2846.     MOV    H,A
  2847.     JNC    SKIPIT
  2848.     MOV    A,H        ; The generator is x^16 + x^12 + x^5 + 1
  2849.     XRI    10H
  2850.     MOV    H,A
  2851.     MOV    A,L
  2852.     XRI    21H
  2853.     MOV    L,A
  2854. ;
  2855. SKIPIT:    DCR    B
  2856.     JNZ    UPDLOOP
  2857.     SHLD    CRCVAL
  2858.     POP    H
  2859.     POP    B
  2860.     POP    PSW
  2861.     RET
  2862. ;
  2863. ; end of CRC routines
  2864. ; -------------------
  2865. ;
  2866. ; Start of LOGCAL routines
  2867. ;
  2868. ; Main log file routine, adds record to log file
  2869. ;
  2870. LOGCALL: IF    LOGCAL OR MSGFIL OR MSGDSC
  2871.     MVI    C,CURDRV    ; Get current drive
  2872.     CALL    BDOS        ; (where down/upload occurred)
  2873.     STA    DSKSAV
  2874.     MVI    E,0FFH        ; (where down/upload occurred)
  2875.     CALL    USRSET        ; Get current user area
  2876.     STA    USRSAV        ; Save it
  2877.      ENDIF
  2878. ;
  2879.      IF    (NOT LOGCAL) AND (MSGFIL OR MSGDSC)
  2880.     RET
  2881.      ENDIF
  2882. ;
  2883.      IF    LOGCAL
  2884.     XRA    A
  2885.     STA    FCBCALLER+12
  2886.     STA    FCBCALLER+32
  2887.     MVI    A,LASTDRV-'A'
  2888.     STA    DEFAULT$DISK
  2889.     MVI    A,LASTUSR
  2890.     STA    DEFAULT$USER
  2891.     LXI    D,FCBCALLER
  2892.     CALL    OPENF        ; Open LASTCALR file
  2893.     JNZ    LOGC1
  2894.     CALL    ILPRTL
  2895.     DB    '++ No LASTCALR??? file found ++',0
  2896.     RET            ; Show error local and transmit EOT
  2897. ;
  2898. LOGC1:    MVI    C,SETRRD    ; Get random record #
  2899.     LXI    D,FCBCALLER    ; (for first record in file)
  2900.     CALL    BDOS
  2901.     LXI    D,DBUF
  2902.     CALL    DMASET        ; Set DMA to DBUF
  2903.     LXI    D,FCBCALLER    ; Read first (and only) record
  2904.     CALL    RRANDM        ; Read it
  2905.     LXI    H,DBUF        ; Set pointer to beginning of record
  2906.      ENDIF            ; LOGCAL
  2907. ;
  2908.      IF    LOGCAL AND CLOCK
  2909.     LXI    D,0        ; Zero DE
  2910.     MVI    A,LCNAME    ; Offset-1 to start of caller's name
  2911.     DCR    A        ; Now correct offset
  2912.     MOV    E,A        ; To E
  2913.     DAD    D        ; HL now points to start of name
  2914.      ENDIF
  2915. ;
  2916.      IF    LOGCAL
  2917.     SHLD    CALLERPTR
  2918.     LXI    D,LOGBUF
  2919.     CALL    DMASET        ; Set DMA to LOGBUF
  2920.     XRA    A
  2921.     STA    FCBLOG+12
  2922.     STA    FCBLOG+32
  2923.     MVI    A,LOGDRV-'A'
  2924.     STA    DEFAULT$DISK
  2925.     MVI    A,LOGUSR
  2926.     STA    DEFAULT$USER
  2927.     LXI    D,FCBLOG
  2928.     CALL    OPENF        ; Open log file
  2929.     JNZ    LOGC4        ; If file exists, skip create
  2930.     LXI    D,FCBLOG
  2931.     MVI    C,MAKE        ; Create a new file if needed
  2932.     CALL    BDOS
  2933.     INR    A
  2934.     JNZ    LOGC2        ; No error, continue
  2935.     CALL    ILPRTL        ; File create error
  2936.     DB    '++ No DIR space: LOG ++',0
  2937.     RET            ; Go back and send EOT
  2938. ;
  2939. LOGC2:    MVI    C,SETRRD    ; Set random record #
  2940.     LXI    D,FCBLOG    ; (for first record in file)
  2941.     CALL    BDOS
  2942. ;
  2943. LOGC3:    MVI    A,EOF
  2944.     STA    LOGBUF
  2945.     JMP    LOGC4B
  2946. ;
  2947. LOGC4:    LXI    D,LOGBUF
  2948.     CALL    DMASET        ; Set DMA to LOGBUF
  2949.     MVI    C,FILSIZ    ; Get file length
  2950.     LXI    D,FCBLOG
  2951.     CALL    BDOS
  2952.     LHLD    FCBLOG+33    ; Back up to last record
  2953.     MOV    A,L
  2954.     ORA    H
  2955.     JZ    LOGC3        ; Unless zero length file
  2956.     DCX    H
  2957.     SHLD    FCBLOG+33
  2958.     LXI    D,FCBLOG
  2959.     CALL    RRANDM        ; Read it
  2960. ;
  2961. LOGC4B:    CALL    RSTLP        ; Initialize LOGPTR and LOGCNT
  2962. ;
  2963. LOGC6:    CALL    GETLOG        ; Get characters out of last record
  2964.     CPI    EOF
  2965.     JNZ    LOGC6        ; Until EOF
  2966.     LDA    LOGCNT        ; Then backup one character
  2967.     DCR    A
  2968.     STA    LOGCNT
  2969.     LHLD    LOGPTR
  2970.     DCX    H
  2971.     SHLD    LOGPTR
  2972.      ENDIF            ; LOGCAL
  2973. ;
  2974.      IF    LOGCAL AND RESUSR AND PUPOPT
  2975.     LDA    PUPFLG
  2976.     ORA    A        ; Privileged upload option request?
  2977.     JZ    LOGC7        ; No
  2978.     MVI    A,'P'        ; ...else,
  2979.     JMP    LOGC8        ; Show as private upload for log file
  2980.      ENDIF
  2981. ;
  2982.      IF    LOGCAL
  2983. LOGC7:    LDA    LOGOPT        ; Get option back and put in file
  2984.     CPI    'A'
  2985.     JNZ    LOGC8
  2986.     MVI    A,'L'
  2987. ;
  2988. LOGC8:    CALL    PUTLOG
  2989.     LDA    MSPEED        ; Get speed factor
  2990.     ADI    30H
  2991.     CALL    PUTLOG
  2992.     CALL    PUTSP        ; Blank
  2993.     LDA    PGSIZE        ; Now the program size in minutes..
  2994.     CALL    PNDEC        ; Of transfer time (mins)
  2995.     MVI    A,':'
  2996.     CALL    PUTLOG        ; ':'
  2997.     LDA    PGSIZE+2
  2998.     CALL    PNDEC        ; And seconds
  2999.     CALL    PUTSP        ; Blank
  3000. ;
  3001. ; Log the drive and user area as a prompt
  3002. ;
  3003.     LDA    FCB
  3004.     ORA    A
  3005.     JNZ    WDRV
  3006.     LDA    DSKSAV
  3007.     INR    A
  3008. ;
  3009. WDRV:    ADI    'A'-1
  3010.     CALL    PUTLOG
  3011.     LDA    USRSAV
  3012.     CALL    PNDEC
  3013.     MVI    A,'>'        ; Make it look like a prompt
  3014.     CALL    PUTLOG
  3015.     LDA    LBRARC
  3016.     ORA    A        ; Member extraction?
  3017.     JZ    WDRV1        ; No, won't be member name
  3018.     LXI    H,MEMFCB    ; Name of file in library
  3019.     MVI    B,11
  3020.     CALL    PUTSTR
  3021.     CALL    PUTSP        ; ' '
  3022. ;
  3023. WDRV1:    LXI    H,FCB+1        ; Now the name of the file
  3024.     MVI    B,11
  3025.     CALL    PUTSTR
  3026.     LDA    LBRARC
  3027.     ORA    A        ; Member extraction?
  3028.     JZ    WDRV2        ; No, won't be member name
  3029.     MVI    C,1
  3030.     JMP    SPLOOP
  3031. ;
  3032. WDRV2:    MVI    C,13
  3033. ;
  3034. SPLOOP:    PUSH    B
  3035.     CALL    PUTSP        ; Put ' '
  3036.     POP    B
  3037.     DCR    C
  3038.     JNZ    SPLOOP
  3039.     LHLD    RECDNO        ; Get record count
  3040.     CALL    DIVREC        ; Divide record count by 8
  3041. ;
  3042. EXKB2:    CALL    PNDEC3        ; Print to log file (right just xxxk)
  3043.     LXI    H,LOGK        ; 'k '
  3044.     MVI    B,2
  3045.     CALL    PUTSTR
  3046.      ENDIF            ; LOGCAL
  3047. ;
  3048.      IF    LOGCAL AND CLOCK
  3049.     XRA    A
  3050.     STA    COMMA        ; Reset field counter
  3051.     CALL    GETDATE        ; IF RTC, get current date
  3052.     PUSH    B        ; (save DD/YY)
  3053.     CALL    PNDEC        ; Print MM
  3054.     MVI    A,'/'        ; '/'
  3055.     CALL    PUTLOG
  3056.     POP    PSW        ; Get DD/YY
  3057.     PUSH    PSW        ; Save YY
  3058.     CALL    PNDEC        ; Print DD
  3059.     MVI    A,'/'        ; '/'
  3060.     CALL    PUTLOG
  3061.     POP    B        ; Get YY
  3062.     MOV    A,C
  3063.     CALL    PNDEC        ; Print YY
  3064.     CALL    PUTSP        ; ' '
  3065.     CALL    GETTIME        ; IF RTC, get current time
  3066.     STA    MNSAV        ; Save min
  3067.     MOV    A,B        ; Get current hour
  3068.     CALL    PNDEC        ; Print hr to file
  3069.     MVI    A,':'        ; With ':'
  3070.     CALL    PUTLOG        ; Between HH:MM
  3071.     LDA    MNSAV        ; Get min
  3072.     CALL    PNDEC        ; And print min
  3073.     CALL    PUTSP        ; Print a space
  3074.      ENDIF            ; LOGCAL AND CLOCK
  3075. ;
  3076.      IF    LOGCAL
  3077. CLOOP:    CALL    GETCALLER    ; And the caller
  3078.     CPI    EOF
  3079.     JZ    QUIT
  3080.     CPI    CR        ; Do not print 2nd line of 'LASTCALR'
  3081.     JNZ    CLOP1
  3082. ;
  3083. CEND:    CALL    PUTLOG
  3084.     MVI    A,LF
  3085.     CALL    PUTLOG        ; And add a LF
  3086.     JMP    QUIT
  3087.      ENDIF
  3088. ;
  3089. CLOP1:     IF    LOGCAL AND CLOCK
  3090.     CPI    ' '        ; Space?
  3091.     JNZ    CLOP1A        ; No, check for comma
  3092.     MVI    A,','        ; Convert space to comma to check field
  3093.      ENDIF
  3094. ;
  3095.      IF    LOGCAL
  3096. CLOP1A:    CPI    ','        ; Comma?
  3097.     JNZ    CLOP2
  3098.      ENDIF
  3099. ;
  3100.      IF    LOGCAL AND CLOCK
  3101.     LDA    COMMA
  3102.     CPI    1        ; Is this the second comma or space?
  3103.     JNZ    CLOP1B        ; No, bump the counter
  3104.     MVI    A,CR
  3105.     JMP    CEND        ; Yes, stop taking data from LASTCALR
  3106. ;
  3107. CLOP1B:    INR    A        ; Bump it one
  3108.     STA    COMMA
  3109.      ENDIF
  3110. ;
  3111.      IF    LOGCAL
  3112.     MVI    A,' '        ; Instead send a ' '
  3113. ;
  3114. CLOP2:    CALL    PUTLOG
  3115.     JMP    CLOOP
  3116. ;
  3117. QUIT:    MVI    A,EOF        ; Put in EOF
  3118.     CALL    PUTLOG
  3119.     LDA    LOGCNT        ; Check count of chars in buffer
  3120.     CPI    1
  3121.     JNZ    QUIT        ; Fill last buffer & write it
  3122.     LXI    D,FCBCALLER    ; Close lastcaller file
  3123.     CALL    CLOSEF
  3124.     INR    A
  3125.     JZ    QUIT1
  3126.     LHLD    FCBLOG+33    ; Move pointer back to show
  3127.     DCX    H        ; Actual file size
  3128.     SHLD    FCBLOG+33
  3129.     LXI    D,FCBLOG    ; Close log file
  3130.     CALL    CLOSEF
  3131.     INR    A
  3132.     RNZ            ; If OK, return
  3133. ;
  3134. QUIT1:    CALL    ILPRTL        ; If error, oops
  3135.     DB    '++ Can''t close LOG ++',0
  3136.     RET            ; Go back and send EOT
  3137. ;
  3138. ; -----
  3139. ;
  3140. ; LOGXAL support routines
  3141. ;
  3142. ; Gets a single byte from DBUF
  3143. ;
  3144. GETCALLER:
  3145.     LHLD    CALLERPTR
  3146.     MOV    A,M
  3147.     INX    H
  3148.     SHLD    CALLERPTR
  3149.     RET
  3150. ;
  3151. ; Gets a single byte from log file
  3152. ;
  3153. GETLOG:    LDA    LOGCNT
  3154.     INR    A
  3155.     STA    LOGCNT
  3156.     CPI    129
  3157.     JZ    EOLF
  3158.     LHLD    LOGPTR
  3159.     MOV    A,M
  3160.     INX    H
  3161.     SHLD    LOGPTR
  3162.     RET
  3163. ;
  3164. EOLF:    LHLD    FCBLOG+33
  3165.     INX    H
  3166.     SHLD    FCBLOG+33
  3167.     LXI    H,LOGBUF+1
  3168.     SHLD    LOGPTR
  3169.     MVI    A,1
  3170.     STA    LOGCNT
  3171.     MVI    A,EOF
  3172.     RET
  3173. ;
  3174. ; Open file with FCB pointed to by DE (disk/user passed in DEFAULT$DISK
  3175. ; and DEFAULT$USER)
  3176. ;
  3177. OPENF:    PUSH    D        ; Save FCB address
  3178.     LDA    DEFAULT$DISK    ; Get disk for file
  3179.     CALL    RECDRX        ; Log into it
  3180.     LDA    DEFAULT$USER    ; Get default user
  3181.     CALL    RECARE        ; Log into it
  3182.     POP    D        ; Get FCB address
  3183.      ENDIF            ; LOGCAL
  3184. ;
  3185.      IF    CPM3 AND LOGCAL
  3186.     PUSH    D        ; Save FCB address
  3187.     CALL    RSTDMA        ; Reset to default DMA
  3188.     POP    D        ; Get back pointer to FCB
  3189.     PUSH    D        ; Save FCB pointer again
  3190.     MVI    C,SRCHF        ; Search for first match
  3191.     CALL    BDOS
  3192.     INR    A        ; Did a file match?
  3193.     POP    D
  3194.     RZ            ; No, return
  3195.     PUSH    D
  3196.     DCR    A        ; A=directory code (0-3)
  3197.     ADD    A        ; *2
  3198.     ADD    A        ; *4
  3199.     ADD    A        ; *8
  3200.     ADD    A        ; *16
  3201.     ADD    A        ; *32
  3202.     MOV    E,A
  3203.     MVI    D,0
  3204.     LXI    H,TBUF        ; Add (32*dir code) to default DMA
  3205.     DAD    D        ; to find first match filename
  3206.     POP    D        ; DE=FCB
  3207.     PUSH    D        ; Save DE again
  3208.     INX    H        ; Move HL past user # byte in buffer
  3209.     INX    D        ; Move DE past drive # in FCB
  3210.     MVI    B,11
  3211.     CALL    MOVE        ; Move name found to FCB
  3212.     POP    D        ; And continue with the open
  3213.      ENDIF
  3214. ;
  3215.      IF    LOGCAL
  3216.     CALL    OPENIT        ; Open file
  3217.     CPI    0FFH        ; Not present?
  3218.     RET            ; Return to caller
  3219.      ENDIF
  3220. ;
  3221. ; Write character to log file
  3222. ;
  3223. PUTLOG:     IF    LOGCAL
  3224.     LHLD    LOGPTR        ; Get pointer
  3225.     ANI    7FH        ; Mask off any high bits
  3226.     MOV    M,A        ; Put data
  3227.     INX    H        ; Increment pointer
  3228.     SHLD    LOGPTR        ; Update pointer
  3229.     MOV    B,A        ; Save character in B
  3230.     LDA    LOGCNT        ; Get count
  3231.     INR    A        ; Increment it
  3232.     STA    LOGCNT        ; Update count
  3233.     CPI    129        ; Check it
  3234.     RNZ            ; If not EOB, return
  3235.     PUSH    B        ; Save character
  3236.     LXI    D,FCBLOG    ; Else, write this sector
  3237.     MVI    C,WRDM
  3238.     CALL    BDOS
  3239.     ORA    A
  3240.     JZ    ADVRCP        ; If ok, cont.
  3241.     CALL    ILPRTL
  3242.     DB    '++ Disk full - can''t add to LOG ++',0
  3243.     RET
  3244. ;
  3245. ADVRCP:    LHLD    FCBLOG+33    ; Advance record number
  3246.     INX    H
  3247.     SHLD    FCBLOG+33
  3248.     CALL    RSTLP        ; Reset buffer pointers
  3249.     POP    PSW        ; Get saved character
  3250.     JMP    PUTLOG        ; Put it in buffer and return
  3251. ;
  3252. RSTLP:    LXI    H,LOGBUF    ; Reset pointers
  3253.     SHLD    LOGPTR        ; And return
  3254.     MVI    A,0
  3255.     STA    LOGCNT
  3256.     RET
  3257. ;
  3258. ; Print number in decimal format (into log file)  IN: HL=binary number
  3259. ; OUT: nnn=right justified with spaces
  3260. ;
  3261. PNDEC3:    MOV    A,H        ; Check high byte
  3262.     ORA    A
  3263.     JNZ    DECOT        ; If on, is at least 3 digits
  3264.     MOV    A,L        ; Else, check low byte
  3265.     CPI    100
  3266.     JNC    TEN
  3267.     CALL    PUTSP
  3268. ;
  3269. TEN:    CPI    10
  3270.     JNC    DECOT
  3271.     CALL    PUTSP
  3272.     JMP    DECOT
  3273. ;
  3274. ; Puts a single space in log file, saves PSW/HL
  3275. ;
  3276. PUTSP:    PUSH    PSW
  3277.     PUSH    H
  3278.     MVI    A,' '
  3279.     CALL    PUTLOG
  3280.     POP    H
  3281.     POP    PSW
  3282.     RET
  3283.      ENDIF            ; LOGCAL
  3284. ;
  3285. ; Store decimal format number into log file and/or upload descriptions file.
  3286. ;
  3287. PNDEC:     IF    LOGCAL
  3288.     CPI    10        ; Two column decimal format routine
  3289.     JC    ONE        ; One or two digits to area number?
  3290.     JMP    TWO
  3291. ;
  3292. ONE:    PUSH    PSW
  3293.     MVI    A,'0'
  3294.     CALL    PUTLOG
  3295.     POP    PSW
  3296.      ENDIF
  3297. ;
  3298.      IF    LOGCAL OR MSGDSC
  3299. TWO:    MVI    H,0
  3300.     MOV    L,A
  3301. ;
  3302. DECOT:    PUSH    B
  3303.     PUSH    D
  3304.     PUSH    H
  3305.     LXI    B,-10
  3306.     LXI    D,-1
  3307. ;
  3308. DECOT2:    DAD    B
  3309.     INX    D
  3310.     JC    DECOT2
  3311.     LXI    B,10
  3312.     DAD    B
  3313.     XCHG
  3314.     MOV    A,H
  3315.     ORA    L
  3316.     CNZ    DECOT
  3317.      ENDIF
  3318. ;
  3319.      IF    MSGDSC
  3320.     LDA    DSCFLG
  3321.     ORA    A        ; Description entry?
  3322.      ENDIF
  3323. ;
  3324.      IF    LOGCAL OR MSGDSC
  3325.     MOV    A,E
  3326.      ENDIF
  3327. ;
  3328.      IF    MSGDSC
  3329.     JZ    DECOT3        ; No, so skip next 3 lines
  3330.     ADI    '0'
  3331.     CALL    OUTCHR        ; ...else store it
  3332.     JMP    DECOT4        ; And skip next 2 lines
  3333.      ENDIF
  3334. ;
  3335.      IF    LOGCAL OR MSGDSC
  3336. DECOT3:    ADI    '0'
  3337.     CALL    PUTLOG
  3338. ;
  3339. DECOT4:    POP    H
  3340.     POP    D
  3341.     POP    B
  3342.     RET
  3343.      ENDIF
  3344. ;
  3345. ; Put string to log file
  3346. ;
  3347.      IF    LOGCAL
  3348. PUTSTR:    MOV    A,M
  3349.     PUSH    H
  3350.     PUSH    B
  3351.     CALL    PUTLOG
  3352.     POP    B
  3353.     POP    H
  3354.     INX    H
  3355.     DCR    B
  3356.     JNZ    PUTSTR
  3357.     RET
  3358.      ENDIF            ; LOGCAL
  3359. ;
  3360. ; end of LOGCAL routine
  3361. ; ---------------------
  3362. ;
  3363. ; Start of TIMEON routine
  3364. ;
  3365. ; Calculate time on system and inform user.  NUBYE will handle logoff if
  3366. ; MXTIME is exceeded.
  3367. ;
  3368. TIME:     IF    TIMEON
  3369.     MVI    E,0FFH
  3370.     MVI    C,81        ; Ask for MXTIME
  3371.     CALL    BDOS
  3372.     STA    TLIMIT        ; And save it
  3373.     MVI    E,0
  3374.     MVI    C,81        ; Stop NUBYE from checking time just now
  3375.     CALL    BDOS
  3376.     MVI    C,79        ; Ask for TON and RTC address
  3377.     CALL    BDOS
  3378.     STA    TON        ; Save TON
  3379.      ENDIF
  3380. ;
  3381.      IF    CLOCK AND TIMEON AND DTOS
  3382.     MVI    C,83
  3383.     CALL    BDOS        ; Have NUBYE print time-on system
  3384.      ENDIF
  3385. ;
  3386.      IF    TIMEON
  3387.     PUSH    B
  3388.     LDA    TON        ; Get time-on-system
  3389.     MOV    B,A        ; Save it
  3390.     LDA    TLIMIT        ; Get MXTIME
  3391.     SUB    B        ; MXTIME-TOS=TLOS (Time-Left-On-System)
  3392.     STA    TLOS        ; And store it
  3393.     POP    B
  3394.      ENDIF
  3395. ;
  3396.     RET            ; End of routine in any case
  3397. ;
  3398. TON:    DB    0        ; Storage for time-on-system
  3399. TLIMIT:    DB    0        ; Storage for MXTIME and status
  3400. TLOS:    DB    0        ; Storage for time-left-on-system
  3401. ;
  3402. ; DEC8 will convert an 8 bit binary number in A to 3 ASCII bytes.  HL
  3403. ; points to the MSB location where the ASCII bytes will be stored.  Any
  3404. ; leading zeros are suppressed, so store spaces in your buffer before
  3405. ; calling.
  3406. ;
  3407. DEC8:    PUSH    B
  3408.     PUSH    D
  3409.     MVI    E,0        ; Leading zero flag
  3410.     MVI    D,100
  3411. ;
  3412. DEC81:    MVI    C,'0'-1
  3413. ;
  3414. DEC82:    INR    C
  3415.     SUB    D        ; 100 or 10
  3416.     JNC    DEC82        ; Still +
  3417.     ADD    D        ; Now add it back
  3418.     MOV    B,A        ; Remainder
  3419.     MOV    A,C        ; Get 100/10
  3420.     CPI    '1'        ; Zero?
  3421.     JNC    DEC84        ; Yes
  3422.     MOV    A,E        ; Check flag
  3423.     ORA    A        ; Reset?
  3424.     MOV    A,C        ; Restore byte
  3425.     JZ    DEC85        ; Leading zeros are skipped
  3426. ;
  3427. DEC84:    MOV    M,A        ; Store it in buffer pointed at by HL
  3428.     INX    H        ; Increment storage location
  3429.     MVI    E,0FFH        ; Set zero flag
  3430. ;
  3431. DEC85:    MOV    A,D
  3432.     SUI    90        ; 100 to 10
  3433.     MOV    D,A
  3434.     MOV    A,B        ; Remainder
  3435.     JNC    DEC81        ; Do it again
  3436.     ADI    '0'        ; Make ASCII
  3437.     MOV    M,A        ; And store it
  3438.     POP    D
  3439.     POP    B
  3440.     RET
  3441. ;
  3442. ; end of TIMEON routine
  3443. ; ---------------------
  3444. ;
  3445. ; The routine here should read your real-time clock
  3446. ; and return with the following information:
  3447. ;
  3448. ;   register: A - current minute (0-59)
  3449. ;          B - current hour     (0-23)
  3450. ;
  3451.      IF    TIMEON OR CLOCK
  3452. GETTIME:MVI    C,79        ; Ask for TON and RTC address
  3453.     CALL    BDOS
  3454.     MOV    A,M        ; Get hours on system
  3455.     CALL    BCDBIN        ; Convert BCD value to binary
  3456.     PUSH    PSW        ; Save hours on stack
  3457.     INX    H        ; Point to minutes
  3458.     MOV    A,M        ; Get minutes
  3459.     CALL    BCDBIN        ; Convert BCD to binary
  3460.     POP    B        ; Get hours in B (minuntes in A)
  3461.     RET
  3462.      ENDIF
  3463. ;
  3464.      IF    LOGCAL AND CLOCK
  3465. GETDATE:MVI    C,79        ; Get RTC address
  3466.     CALL    BDOS
  3467.     LXI    D,4        ; Offset to YY
  3468.     DAD    D        ; HL=YY Address
  3469.     MOV    A,M        ; Get YY
  3470.     CALL    BCDBIN        ; Convert to binary
  3471.     STA    YYSAV        ; Save YY
  3472.     INX    H        ; Point to MM
  3473.     MOV    A,M        ; Get MM
  3474.     CALL    BCDBIN        ; Convert BCD to binary
  3475.     STA    MMSAV        ; Save it
  3476.     INX    H        ; Point to DD
  3477.     MOV    A,M        ; Get it
  3478.     CALL    BCDBIN        ; Convert it to binary
  3479.     MOV    B,A        ; Stuff DD in B
  3480.     LDA    YYSAV        ; Get YY
  3481.     MOV    C,A        ; Put YY in C
  3482.     LDA    MMSAV        ; Get MM in A
  3483.      ENDIF
  3484. ;
  3485.      IF    EDATE
  3486.     MOV    D,B
  3487.     MOV    B,A
  3488.     MOV    A,D        ; Return with dd/mm/yy vice mm/dd/yy
  3489.      ENDIF
  3490. ;
  3491.      IF    LOGCAL AND CLOCK
  3492.     RET            ; And return
  3493.      ENDIF
  3494. ;
  3495. ; Convert BCD value in A to binary in A
  3496. ;
  3497.      IF    CLOCK OR TIMEON
  3498. BCDBIN:    PUSH    PSW        ; Save A
  3499.     ANI    0F0H        ; Mask high nibble
  3500.     RRC            ; Move to low nibble
  3501.     RRC
  3502.     RRC
  3503.     RRC
  3504.     MOV    C,A        ; And stuff in C (C=A)
  3505.     MVI    B,9        ; X10 (*9)
  3506. ;
  3507. BCDBL:    ADD    C        ; Add original value to A
  3508.     DCR    B        ; Decrement B
  3509.     JNZ    BCDBL        ; Loop nine times (A+(C*9)=A*10)
  3510.     MOV    B,A        ; Save result in B
  3511.     POP    PSW        ; Get original value
  3512.     ANI    0FH        ; Mask low nibble
  3513.     ADD    B        ; +B gives binary value of BCD digit A
  3514.     RET            ; Return
  3515.      ENDIF
  3516. ;
  3517. ; -----
  3518. ;
  3519. ; The following allocations are used by the LOGCALL routines
  3520. ;
  3521.            IF    LOGCAL
  3522. PGSIZE:          DB    0,0,0          ; Program length in mins and secs
  3523. LOGOPT:          DB    '?'          ; Primary option stored here
  3524. DEFAULT$DISK: DB    0          ; Disk for open stored here
  3525. DEFAULT$USER: DB    0          ; User for open stored here
  3526. FCBCALLER:    DB    0,'LASTCALR???'      ; Last caller file FCB
  3527.           DB    0,0,0,0,0,0,0,0
  3528.           DB    0,0,0,0,0,0,0,0
  3529.           DB    0,0,0,0,0,0,0,0
  3530. ;
  3531. CALLERPTR:    DW    LOGBUF
  3532.            ENDIF
  3533. ;
  3534. FCBLOG:     IF    LOGCAL AND (NOT    MBBS)
  3535.     DB    0,'NUKMD   LOG'    ; Log file FCB
  3536.      ENDIF
  3537. ;
  3538.      IF    LOGCAL AND MBBS
  3539.     DB    0,'LOG     SYS'    ; Log file FCB
  3540.      ENDIF
  3541. ;
  3542.      IF    LOGCAL
  3543.     DB    0,0,0,0,0,0,0,0
  3544.     DB    0,0,0,0,0,0,0,0
  3545.     DB    0,0,0,0,0,0,0,0    ; Rest of FCB
  3546. LOGPTR:    DW    DBUF
  3547. LOGCNT:    DB    0
  3548.      ENDIF
  3549. ;
  3550.      IF    LOGCAL OR RESUSR OR MSGFIL OR MSGDSC
  3551. DSKSAV:    DB    0        ; Up/download disk saved here
  3552. USRSAV:    DB    0        ; Up/download user saved here
  3553.      ENDIF
  3554. ;
  3555.      IF    LOGCAL
  3556. LOGK:    DB    'k '
  3557.      ENDIF
  3558. ;
  3559.      IF    LOGCAL AND (TIMEON OR CLOCK)
  3560. YYSAV:    DB    0
  3561. MMSAV:    DB    0
  3562. DDSAV:    DB    0
  3563. MNSAV:    DB    0
  3564.      ENDIF
  3565. ;
  3566. ; -----
  3567. ;
  3568. ; Batch stuff
  3569. ;
  3570. BCHADR:    DW    DBUF        ; For multiple descriptions
  3571. BCHPTR:    DW    0
  3572. BGNMS:    DW    0        ; Start address of filenames in TBUFF
  3573. BLOKK:    DW    0        ; # of 2k blocks required by remote
  3574. BUFADR:    DW    DBUF        ; For multiple file display
  3575. ;
  3576. BCHFLG:    DB    0        ; Batch mode flag
  3577. DISKNO:    DB    0
  3578. FCBBUF:    DB    0,0,0,0,0    ; Batch filename from command line
  3579.     DB    0,0,0,0,0
  3580.     DB    0,0,0,0,0
  3581. FILCNT:    DB    0        ; # of files in batch mode
  3582. FSTFLG:    DB    0        ; Set to 1 when command line scan done
  3583. FTYCNT:    DB    0
  3584. MFFLG1:    DB    0
  3585. MFNAM5:    DB    0,0,0,0,0,0
  3586.     DB    0,0,0,0,0,0
  3587. MFNAM6:    DB    0,0,0,0,0,0
  3588.     DB    0,0,0,0,0,0
  3589. NAMECT:    DB    0        ; # of names on command line
  3590. NBSAVE:    DB    0,0        ; Start address in NAMBUF for next file
  3591. SHOCNT:    DB    0        ; Counter to show files left
  3592. SNDFLG:    DB    0        ;
  3593. TOTREC:    DB    0,0        ; Total records to be sent
  3594. ;
  3595. ; Temporary storage area
  3596. ;
  3597. FILE:     IF    MSGDSC AND (NOT    DESCRIB)
  3598.     DB    0,'UPLOADS    '
  3599.      ENDIF
  3600. ;
  3601.      IF    DESCRIB    AND (NOT MSGDSC)
  3602.     DB    0,'FOR        '
  3603.      ENDIF
  3604. ;
  3605.     DB    0,0,0,0,0,0,0
  3606.     DB    0,0,0,0,0,0,0
  3607.     DB    0,0,0,0,0,0,0
  3608. ;
  3609. DEST:    DB    0,'        $$$'
  3610.     DB    0,0,0,0,0,0,0
  3611.     DB    0,0,0,0,0,0,0
  3612.     DB    0,0,0,0,0,0,0
  3613. ;
  3614. DUSAVE:    DB    0,0,0,0           ; Buffer for drive/user
  3615. MEMFCB:    DB    '                ' ; Library name (16 bytes required)
  3616. ;
  3617. AFBYTE:    DB    0        ; Access flags byte storage
  3618. ANYET:    DB    0        ; Any description typed yet?
  3619. ARCEOF:    DB    0        ; EOF flag for .ARK/.ARC
  3620. ARCFST:    DB    0        ; First record flag
  3621. ARCLST:    DB    0        ; Last record byte count -1
  3622. ARCVER:    DB    0        ; Flag for compression type (1-8)
  3623. BLKSHF:    DB    0
  3624. CHKASK:    DB    0        ; First time wrap mode prompt
  3625. CHKEOT:    DB    0        ; Prevents locking up after an EOT
  3626. CHOICE:    DB    0        ; User choice flag
  3627. CHRCNT:    DB    0,0,0        ; 24-bit counter
  3628. CRCFLG:    DB    0        ; For sending checksum rather than CRC
  3629. CONONL:    DB    0        ; CTYPE console-only flag
  3630. COMMA:    DB    0        ; Field counter for logcal
  3631. DRUSER:    DB    0        ; Original drive/user, for return
  3632. DSCFLG:    DB    0        ; Special description file flag
  3633. DUD:    DB    0        ; Specified disk
  3634. DUU:    DB    0        ; Specified user
  3635. EOFLG:    DB    0        ; 'EOF' flag (1=yes)
  3636. EOTFLG:    DB    0        ; EOT (End Of Transmission) status flag
  3637. ERRCT:    DB    0        ; Error count
  3638. FRSTIM:    DB    0        ; Turned on after first 'SOH' received
  3639. GOTONE:    DB    0        ; Prevents asking for a description
  3640. INBTCH:    DB    0        ; For batch uploads and MSGDSC
  3641. KIND:    DB    0        ; Asks what kind of file this is
  3642. KFLG:    DB    0        ; For sending 1k blocks
  3643. LBRARC:    DB    0        ; .LBR/.ARK/.ARK request flag
  3644. MSGFLG:    DB    0        ; Special flag for message file uploads
  3645. NOISY:    DB    0        ; 1 indicates noisy line switch to Xmodem
  3646. OLDDRV:    DB    0        ; Save the original drive number
  3647. OLDUSR:    DB    0        ; Save the original user number
  3648. OPTSAV:    DB    0        ; Save option here for carrier loss
  3649. PUPFLG:    DB    0        ; Special flag for privileged option uploads
  3650. PRVTFL:    DB    0        ; Private user area option flag
  3651. RCVCNT:    DB    0        ; Record number received
  3652. RCVDRV:    DB    0        ; Requested drive number
  3653. RCVTRY:    DB    0        ; Keeps track of number of attempts
  3654. RCVUSR:    DB    0        ; Requested user number
  3655. RWHEEL:    DB    0        ; Shows wheel byte is set
  3656. SPLFL:    DB    0        ; Special flag for private downloads
  3657. SPLFL1:    DB    0        ; Special flag for alternate section downloads
  3658. SYSABT:    DB    0        ; Local sysop xfr abort with ^X
  3659. YMODEM:    DB    0        ; Special flag for Ymodem batch xfr (CRC-1k)
  3660. ;
  3661. ACCERR:    DW    0        ; No 'ACK' error count for 1k ratio
  3662. ARCCNT:    DW    0        ; .ARK/.ARC record count
  3663. ARCPTR:    DW    0        ; Record pointer
  3664. ARCREC:    DW    0        ; Record number
  3665. BLKMAX:    DW    0
  3666. CRCVAL:    DW    0        ; Current CRC value
  3667. DIRSIZ:    DW    0        ; Directory size
  3668. HDRADR:    DW    0        ; Header address
  3669. INDEX:    DW    0        ; Index into directory
  3670. MINUTE:    DW    0        ; Transfer time in mins for MAXTIM
  3671. OUTADR:    DW    DBUF
  3672. OUTPTR:    DW    0
  3673. OUTSIZ:    DW    BSIZE
  3674. RCNT:    DW    0        ; Record count
  3675. RECDNO:    DW    0        ; Current record number
  3676. RCDCNT:    DW    0        ; Used in sending the record header
  3677. RECPTR:    DW    DBUF
  3678. RECNBF:    DW    0        ; Number of records in the buffer
  3679. SAVEHL:    DW    0        ; Saves TBUF command line address
  3680. ;
  3681. HLINE:     IF    DESCRIB    AND (NOT MSGDSC)
  3682.     DB    '----',CR,LF
  3683.      ENDIF
  3684. ;
  3685.      IF    MSGDSC AND (NOT    DESCRIB)
  3686.     DB    'MSG#: ????',CR,LF
  3687.     DB    'FROM: ',0
  3688. HLINE1:    DB    ' (PRIVATE)',CR,LF
  3689.     DB    '  TO: SYSOP',CR,LF
  3690.     DB    '  RE: '
  3691. MBDSH:    DB    'NEW UPLOAD: ',0
  3692. HLINE2:    DB    ' ',CR,LF
  3693.      ENDIF
  3694. ;
  3695.      IF    MSGDSC OR DESCRIB
  3696. HLINE3:    DB    CR,LF,0        ; End of description
  3697.      ENDIF
  3698. ;
  3699. OLINE:    DS    80        ; Temporary storage buffer
  3700.     DS    60        ; Area for stack
  3701. ;
  3702. ; BDOS equates
  3703. ;
  3704. WRCON    EQU    2        ; Output to console
  3705. DIRCON    EQU    6        ; Direct console output
  3706. PRINT    EQU    9        ; Print string function
  3707. SELDSK    EQU    14        ; Select drive
  3708. OPEN    EQU    15        ; 0FFH = not found
  3709. CLOSE    EQU    16        ; "      "
  3710. SRCHF    EQU    17        ; "      "
  3711. SRCHN    EQU    18        ; "      "
  3712. DELET    EQU    19        ; Delete file
  3713. READ    EQU    20        ; 0=OK, 1=EOF
  3714. WRITE    EQU    21        ; 0=OK, 1=ERR, 2=?, 0FFH=no dir. space
  3715. MAKE    EQU    22        ; 0FFH=bad
  3716. RENAME    EQU    23        ; Rename a file
  3717. CURDRV    EQU    25        ; Get current drive
  3718. STDMA    EQU    26        ; Set DMA
  3719. SETUSR    EQU    32        ; Set user area to receive file
  3720. RRDM    EQU    33        ; Read random
  3721. WRDM    EQU    34        ; Write random
  3722. FILSIZ    EQU    35        ; Compute file size
  3723. SETRRD    EQU    36        ; Set random record
  3724. BDOS    EQU    5        ; Address for BDOS jump vectors
  3725. TBUF    EQU    80H        ; Default DMA address
  3726. FCB    EQU    5CH        ; System FCB
  3727. FCB1    EQU    6CH        ; Secondary FCB area
  3728. FCBEXT    EQU    FCB+12        ; File extent
  3729. FCBRNO    EQU    FCB+32        ; Record number
  3730. FCBTYP    EQU    FCB+9        ; File type
  3731. RANDOM    EQU    FCB+33        ; Random record field
  3732. ;
  3733. MAIN    EQU    1
  3734. VERS    EQU    11
  3735. MONTH    EQU    2
  3736. DAY    EQU    4
  3737. YEAR    EQU    87
  3738. ;
  3739. BELL    EQU    7    ; Bell
  3740. BS    EQU    8    ; Backspace character
  3741. ACK    EQU    6    ; Acknowledge
  3742. CANCEL    EQU    18H    ; ^X for cancel
  3743. CR    EQU    0DH    ; Carriage return
  3744. CRC    EQU    'C'    ; CRC request character
  3745. KSND    EQU    'K'    ; 1k block request character
  3746. EOF    EQU    1AH    ; End of file - ^Z
  3747. EOT    EQU    4    ; End of transmission
  3748. LF    EQU    0AH    ; Line feed
  3749. NAK    EQU    15H    ; Negative acknowledge
  3750. RLEN    EQU    128    ; Record length
  3751. TAB    EQU    9    ; Horizontal tab
  3752. SOH    EQU    1    ; Start of header
  3753. STX    EQU    2    ; Start of 1k header
  3754. ;
  3755. MSPEED    EQU    3CH    ; Location of NUBYE's modem speed indicator
  3756. DRIVMAX    EQU    3DH    ; Location of MAXDRIV byte
  3757. USRMAX    EQU    3FH    ; Location of MAXUSER byte
  3758. ;
  3759. ARCMRK    EQU    26    ; Header mark
  3760. HDRSIZ    EQU    28    ; Header size (version 1 = HDRSIZ-4)
  3761. ;
  3762. ; 16k disk buffer
  3763. ;
  3764.     ORG    ($+127)/128*128
  3765. ;
  3766. CMDBUF:    DS    128        ; Store TBUFF here in batch mode
  3767. STACK    EQU    CMDBUF-2
  3768. NAMBUF:    DS    24*128        ; Allow room for 256 batch filenames
  3769. DBUF:    DS    128*128        ; 16k disk buffer
  3770. BUFSTR    EQU    DBUF+126    ; For file length in batch mode
  3771. LOGBUF    EQU    DBUF+128    ; For use with LOGCAL
  3772. BSIZE    EQU    24*1024        ; Set for 24k for the DESCRIB/MSGDSC buffer
  3773. ;
  3774.     END
  3775. ;
  3776. ; 1) Irvin M. Hoff et al, "KMD22.ASM" (1986)
  3777.