home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / simtel / sigm / vols000 / vol008 / xmodem38.asm < prev   
Encoding:
Assembly Source File  |  1984-04-29  |  23.2 KB  |  1,016 lines

  1. ;
  2. ;    XMODEM.ASM V3.8, by Keith Petersen, W8SDZ
  3. ;          revised 12/06/80
  4. ;
  5. ;    REMOTE CP/M - CP/M FILE TRANSFER PROGRAM
  6. ;
  7. ;Based on MODEM.ASM V2.0, by Ward Christensen.
  8. ;This program is intended for use on remote CP/M
  9. ;systems where it is important that the initialization
  10. ;of the modem not be changed, such as when using
  11. ;the PMMIBYE program. The baud rate and number of bits
  12. ;remains the same as whatever was set previously.
  13. ;There is no disconnect, terminal or echo option.
  14. ;
  15. ;NOTE: This file will assemble, without need for
  16. ;editing, to work with a PMMI MM-103 modem and 2 Mhz
  17. ;system clock.    See equates for options including
  18. ;other modems and 4 Mhz system clock frequency.
  19. ;
  20. ;Program updates/fixes (these are written in reverse
  21. ;order to minimize reading time to find latest update):
  22. ;
  23. ;12/06/80 Re-wrote routine to calculate file size,
  24. ;      added decimal print of file size. (KBP)
  25. ;
  26. ;12/05/80 Corrected error in use of ext byte that pre-
  27. ;      vented files greater than one extent from being
  28. ;      sent.     Ron Fowler
  29. ;
  30. ;12/03/80 Corrected file extent length display. Now
  31. ;      reports correct number of records for files
  32. ;      longer than one extent. Display is now
  33. ;      double precision (xxxxH). Also made some
  34. ;      cosmetic changes by re-arranging the equates.
  35. ;      By Tim Nicholas
  36. ;
  37. ;10/28/80 Cleaned up file. (KBP)
  38. ;
  39. ;10/23/80 Expanded conditional assembly of NOCOM routines
  40. ;      into NOCOMS, NOLBS, and NOCOMR equates, to allow
  41. ;      separate conditional assembly of tests for sending
  42. ;      .COM files, sending .??# files, and receiving .COM
  43. ;      files, respectively.    (Dave Hardy)
  44. ;
  45. ;10/15/80 Added traps for ambiguous file name or
  46. ;      none at all. (KBP)
  47. ;
  48. ;09/09/80 Added conditional assembly to prevent filetypes
  49. ;      '.COM' or '.??#' from being sent to distant end
  50. ;      and added conditional assembly of test for '.COM'
  51. ;      filetype on receive as well. See 'NOCOM' below.
  52. ;      Any filetype ending in '#' will not be sent by
  53. ;      this program if 'NOCOM' is set to TRUE.  J.SEYMOUR
  54. ;
  55. ;NOTE: If you add improvements or otherwise update
  56. ;this program, please modem a copy of the new file
  57. ;to "TECHNICAL CBBS" in Dearborn, Michigan - phone
  58. ;313-846-6127 (110, 300, 450 or 600 baud).  Use the
  59. ;filename XMODEM.NEW.    (KBP)
  60. ;
  61. FALSE    EQU    0
  62. TRUE    EQU    NOT FALSE
  63. ;
  64. ;------------------------------------------------------
  65. ;     --- Conditional Assembly Options ---          ;
  66. ;------------------------------------------------------
  67. ;
  68. STDCPM    EQU    TRUE    ;TRUE, IS STANDARD CP/M
  69. ALTCPM    EQU    FALSE    ;TRUE, IS H8 OR TRS-80 CP/M
  70. ;
  71. PMMI    EQU    TRUE    ;TRUE, IS PMMI
  72. DCH    EQU    FALSE    ;TRUE, IS D.C. HAYES
  73. ;
  74. NOCOMS    EQU    FALSE    ;TRUE, NO .COM FILES SENT
  75. NOLBS    EQU    TRUE    ;TRUE, NO .??# FILES SENT
  76. NOCOMR    EQU    TRUE    ;TRUE, NO .COM FILES RECEIVED
  77. ;
  78. FASTCLK EQU    FALSE    ;PUT TRUE HERE FOR 4 MHZ CLOCK
  79. ;
  80. ;------------------------------------------------------
  81. ;         --- Modem Port Equates ---           ;
  82. ;------------------------------------------------------
  83. ;
  84.     IF    PMMI
  85. MODCTLP EQU    0C0H    ;PMMI VALUES
  86. MODSNDB EQU    1    ;BIT TO TEST FOR SEND
  87. MODSNDR EQU    1    ;VALUE WHEN READY
  88. MODRCVB EQU    2    ;BIT TO TEST FOR RECEIVE
  89. MODRCVR EQU    2    ;VALUE WHEN READY
  90. MODDATP EQU    0C1H    ;DATA PORT
  91. BAUDRP    EQU    0C2H    ;BAUD RATE OUTPUT
  92. MODCTL2 EQU    0C3H    ;SECOND CTL PORT
  93.     ENDIF
  94. ;
  95.     IF    DCH
  96. MODCTLP EQU    82H    ;D. C. HAYES VALUES
  97. MODSNDB EQU    2    ;BIT TO TEST FOR SEND
  98. MODSNDR EQU    2    ;VALUE WHEN READY
  99. MODRCVB EQU    1    ;BIT TO TEST FOR RECEIVE
  100. MODRCVR EQU    1    ;VALUE WHEN READY
  101. MODDATP EQU    80H    ;DATA PORT
  102. MODCTL2 EQU    81H    ;SECOND CTL PORT
  103.     ENDIF
  104. ;
  105. ;If you are using an external modem (not S-100 plug-in)
  106. ;change these equates for your modem port requirements
  107. ;
  108.     IF    NOT PMMI AND NOT DCH
  109. MODCTLP EQU    02H    ;PUT YOUR MODEM STATUS PORT HERE
  110. MODSNDB EQU    80H    ;YOUR BIT TO TEST FOR SEND
  111. MODSNDR EQU    80H    ;YOUR VALUE WHEN READY
  112. MODRCVB EQU    40H    ;YOUR BIT TO TEST FOR RECEIVE
  113. MODRCVR EQU    40H    ;YOUR VALUE WHEN READY
  114. MODDATP EQU    03H    ;YOUR MODEM DATA PORT
  115.     ENDIF        ;END OF EXTERNAL MODEM EQUATES
  116. ;
  117. ;        --- End of Options ---
  118. ;------------------------------------------------------
  119. ;
  120. ;
  121. ERRLIM    EQU    10    ;MAX ALLOWABLE ERRORS (10 STANDARD)
  122. ;
  123. ;Define ASCII characters used
  124. ;
  125. SOH    EQU    1    ;START OF HEADER
  126. EOT    EQU    4    ;END OF TRANSMISSION
  127. ACK    EQU    6    ;ACKNOWLEDGE
  128. NAK    EQU    15H    ;NEG ACKNOWLEDGE
  129. CAN    EQU    18H    ;CONTROL-X FOR CANCEL
  130. LF    EQU    10    ;LINEFEED
  131. CR    EQU    13    ;CARRIAGE RETURN
  132.     IF    STDCPM
  133. BASE    EQU    0    ;CP/M BASE ADDRESS
  134.     ENDIF
  135. ;
  136.     IF    ALTCPM
  137. BASE    EQU    4200H    ;ALTERNATE CP/M BASE ADDRESS
  138.     ENDIF
  139. ;
  140.     ORG    BASE+100H
  141. ;
  142. ;Init private stack
  143.     LXI    H,0    ;HL=0
  144.     DAD    SP    ;HL=STACK FROM CP/M
  145.     SHLD    STACK    ;..SAVE IT
  146.     LXI    SP,STACK ;SP=MY STACK
  147.     CALL    ILPRT    ;PRINT:
  148.     DB    CR,LF
  149.     DB    'XMODEM ver 3.8',CR,LF,0
  150. ;
  151. ;Get option
  152. ;
  153.     LDA    FCB+1    ;GET OPTION (S or R)
  154.     PUSH    PSW    ;SAVE OPTION
  155. ;
  156. ;Move the filename from FCB2 to FCB1
  157. ;
  158.     CALL    MOVEFCB
  159. ;
  160. ;Gobble up garbage chars from the line
  161. ;prior to receive or send
  162. ;
  163.     IN    MODDATP
  164.     IN    MODDATP
  165. ;
  166. ;Jump to appropriate function
  167. ;
  168.     POP    PSW    ;GET OPTION
  169. ;
  170.     CPI    'S'    ;SEND..
  171.     JZ    SENDFIL ;..A FILE?
  172. ;
  173.     CPI    'R'    ;RECEIVE..
  174.     JZ    RCVFIL    ;..A FILE?
  175. ;
  176. ;Invalid option
  177. ;
  178.     CALL    ERXIT    ;EXIT W/ERROR
  179.     DB    '++INVALID OPTION ON XMODEM '
  180.     DB    'COMMAND++',CR,LF
  181.     DB    'Must be S for SEND or R for '
  182.     DB    'RECEIVE',CR,LF,'$'
  183. ;
  184. * * * * * * * * * * * * * * * * * * * * *
  185. *                    *
  186. *    SENDFIL: SENDS A CP/M FILE    *
  187. *                    *
  188. * * * * * * * * * * * * * * * * * * * * *
  189. ;
  190. ;The CP/M file specified in the XMODEM command
  191. ;is transferred over the phone to another
  192. ;computer running MODEM with the "R" (receive)
  193. ;option.  The data is sent one sector at a
  194. ;time with headers and checksums, and re-
  195. ;transmission on errors.  
  196. ;
  197. SENDFIL CALL    TRAP    ;CHECK FOR NO NAME OR AMBIG. NAME
  198.     CALL    CNREC    ;COMPUTE # OF RECORDS.
  199.     CALL    OPENFIL ;OPEN THE FILE
  200.     MVI    E,80    ;WAIT 80 SEC..
  201.     CALL    WAITNAK ;..FOR INITIAL NAK
  202. ;
  203. SENDLP    CALL    RDSECT    ;READ A SECTOR
  204.     JC    SENDEOF ;SEND EOF IF DONE
  205.     CALL    INCRSNO ;BUMP SECTOR #
  206.     XRA    A    ;ZERO ERROR..
  207.     STA    ERRCT    ;..COUNT
  208. ;
  209. SENDRPT CALL    SENDHDR ;SEND A HEADER
  210.     CALL    SENDSEC ;SEND DATA SECTOR
  211.     CALL    SENDCKS ;SEND CKSUM
  212.     CALL    GETACK    ;GET THE ACK
  213.     JC    SENDRPT ;REPEAT IF NO ACK
  214.     JMP    SENDLP    ;LOOP UNTIL EOF
  215. ;
  216. ;File sent, send EOT's
  217. ;
  218. SENDEOF MVI    A,EOT    ;SEND..
  219.     CALL    SEND    ;..AN EOT
  220.     CALL    GETACK    ;GET THE ACK
  221.     JC    SENDEOF ;LOOP IF NO ACK
  222.     JMP    EXIT    ;ALL DONE
  223. ;
  224. * * * * * * * * * * * * * * * * * * * * *
  225. *                    *
  226. *    RCVFIL: RECEIVE A FILE        *
  227. *                    *
  228. * * * * * * * * * * * * * * * * * * * * *
  229. ;
  230. ;Receives a file in block format as sent
  231. ;by another person doing "MODEM S FN.FT".
  232. ;
  233. RCVFIL    CALL    TRAP    ;CHECK FOR NO NAME OR AMBIG. NAME
  234. ;
  235.     IF    NOCOMR
  236.     LXI    H,FCB+9 ;POINT TO FILETYPE
  237.     MVI    A,'C'    ;1ST LETTER
  238.     CMP    M    ;IS IT C ?
  239.     JNZ    CONTINU ;IF NOT, CONTINUE NORMALLY
  240.     INX    H    ;GET 2ND LETTER
  241.     MVI    A,'O'    ;2ND LETTER
  242.     CMP    M    ;IS IT O ?
  243.     JNZ    CONTINU ;IF NOT, CONTINUE NORMALLY
  244.     INX    H    ;GET 3RD LETTER
  245.     MVI    A,'M'    ;3RD LETTER
  246.     CMP    M    ;IS IT M ?
  247.     JNZ    CONTINU ;IF NOT, CONTINUE NORMALLY
  248.     CALL    ERXIT    ;EXIT, PRINT ERROR MESSAGE
  249.     DB    '++CAN''T RECEIVE A .COM FILE++'
  250.     DB    CR,LF,CR,LF
  251.     DB    'Rename filetype ".OBJ" and try again'
  252.     DB    CR,LF,'$'
  253.     ENDIF
  254. ;
  255. CONTINU CALL    CHEKFIL ;SEE IF FILE EXISTS
  256.     CALL    MAKEFIL ;..THEN MAKE NEW
  257.     CALL    ILPRT    ;PRINT:
  258.     DB    'FILE OPEN, READY TO RECEIVE',CR,LF,0
  259. ;
  260. RCVLP    CALL    RCVSECT ;GET A SECTOR
  261.     JC    RCVEOT    ;GOT EOT
  262.     CALL    WRSECT    ;WRITE THE SECTOR
  263.     CALL    INCRSNO ;BUMP SECTOR #
  264.     CALL    SENDACK ;ACK THE SECTOR
  265.     JMP    RCVLP    ;LOOP UNTIL EOF
  266. ;
  267. ;Got EOT on sector - flush buffers, end
  268. ;
  269. RCVEOT    CALL    WRBLOCK ;WRITE THE LAST BLOCK
  270.     CALL    SENDACK ;ACK THE SECTOR
  271.     CALL    CLOSFIL ;CLOSE THE FILE
  272.     JMP    EXIT    ;ALL DONE
  273. ;
  274. * * * * * * * * * * * * * * * * * * * * *
  275. *                    *
  276. *        SUBROUTINES        *
  277. *                    *
  278. * * * * * * * * * * * * * * * * * * * * *
  279. ;
  280. ;---->    TRAP: Check for no file name or ambiguous name
  281. ;
  282. TRAP    LXI    H,FCB+1 ;POINT TO FILE NAME
  283.     MOV    A,M    ;GET FIRST CHAR OF FILE NAME
  284.     CPI    ' '    ;ANY THERE?
  285.     JNZ    ATRAP    ;YES, CHECK FOR AMBIGOUS FILE NAME
  286.     CALL    ERXIT    ;PRINT MSG, EXIT
  287.     DB    '++NO FILE NAME SPECIFIED++',CR,LF,'$'
  288. ;
  289. ATRAP    MVI    B,11    ;11 CHARS TO CHECK
  290. ;
  291. TRLOOP    MOV    A,M    ;GET CHAR FROM FCB
  292.     CPI    '?'    ;AMBIGUOUS?
  293.     JZ    TRERR    ;YES, EXIT WITH ERROR MSG
  294.     INX    H    ;POINT TO NEXT CHAR
  295.     DCR    B    ;ONE LESS TO GO
  296.     JNZ    TRLOOP    ;NOT DONE, CHECK SOME MORE
  297.     RET        ;NO AMBIGUOUS NAME, RETURN
  298. ;
  299. TRERR    CALL    ERXIT    ;PRINT MSG, EXIT
  300.     DB    '++CAN''T USE WILD CARD OPTIONS',CR,LF,'$'
  301. ;
  302. ;---->    RCVSECT: Receive a sector
  303. ;
  304. ;Returns with carry set if EOT received.
  305. ;
  306. RCVSECT XRA    A    ;GET 0
  307.     STA    ERRCT    ;INIT ERROR COUNT
  308. ;
  309. RCVRPT    MVI    B,10    ;10 SEC TIMEOUT
  310.     CALL    RECV    ;GET SOH/EOT
  311.     JC    RCVSTOT ;TIMEOUT
  312.     CPI    SOH    ;GET SOH?
  313.     JZ    RCVSOH    ;..YES
  314. ;
  315. ;Earlier versions of MODEM program send some nulls -
  316. ;ignore them
  317. ;
  318.     ORA    A    ;00 FROM SPEED CHECK?
  319.     JZ    RCVRPT    ;YES, IGNORE IT
  320.     CPI    EOT    ;END OF TRANSFER?
  321.     STC        ;RETURN WITH CARRY..
  322.     RZ        ;..SET IF EOT
  323. ;
  324. ;Didn't get SOH or EOT - 
  325. ;    -or-
  326. ;Did'nt get valid header - purge the line,
  327. ;then send NAK.
  328. ;
  329. RCVSERR MVI    B,1    ;WAIT FOR 1 SEC..
  330.     CALL    RECV    ;..WITH NO CHARS
  331.     JNC    RCVSERR ;LOOP UNTIL SENDER DONE
  332.     MVI    A,NAK    ;SEND..
  333.     CALL    SEND    ;..THE NAK
  334.     LDA    ERRCT    ;ABORT IF..
  335.     INR    A    ;..WE HAVE REACHED..
  336.     STA    ERRCT    ;..THE ERROR..
  337.     CPI    ERRLIM    ;..LIMIT?
  338.     JC    RCVRPT    ;..NO, TRY AGAIN
  339. ;
  340. ;10 errors in a row -
  341. ;
  342. RCVSABT CALL    CLOSFIL ;KEEP WHATEVER WE GOT
  343.     CALL    ERXIT
  344.     DB    '++UNABLE TO RECEIVE BLOCK '
  345.     DB    '- ABORTING++',CR,LF,'$'
  346. ;
  347. ;Timed out on receive
  348. ;
  349. RCVSTOT JMP    RCVSERR ;BUMP ERR CT, ETC.
  350. ;
  351. ;Got SOH - get block #, block # complemented
  352. ;
  353. RCVSOH    MVI    B,1    ;TIMEOUT = 1 SEC
  354.     CALL    RECV    ;GET SECTOR
  355.     JC    RCVSTOT ;GOT TIMEOUT
  356.     MOV    D,A    ;D=BLK #
  357.     MVI    B,1    ;TIMEOUT = 1 SEC
  358.     CALL    RECV    ;GET CMA'D SECT #
  359.     JC    RCVSTOT ;TIMEOUT
  360.     CMA        ;CALC COMPLEMENT
  361.     CMP    D    ;GOOD SECTOR #?
  362.     JZ    RCVDATA ;YES, GET DATA
  363. ;
  364. ;Got bad sector #
  365. ;
  366.     JMP    RCVSERR ;BUMP ERROR CT.
  367. ;
  368. RCVDATA MOV    A,D    ;GET SECTOR #
  369.     STA    RCVSNO    ;SAVE IT
  370.     MVI    C,0    ;INIT CKSUM
  371.     LXI    H,BASE+80H ;POINT TO BUFFER
  372. ;
  373. RCVCHR    MVI    B,1    ;1 SEC TIMEOUT
  374.     CALL    RECV    ;GET CHAR
  375.     JC    RCVSTOT ;TIMEOUT
  376.     MOV    M,A    ;STORE CHAR
  377.     INR    L    ;DONE?
  378.     JNZ    RCVCHR    ;NO, LOOP
  379. ;
  380. ;Verify checksum
  381. ;
  382.     MOV    D,C    ;SAVE CHECKSUM
  383.     MVI    B,1    ;TIMEOUT LEN.
  384.     CALL    RECV    ;GET CHECKSUM
  385.     JC    RCVSTOT ;TIMEOUT
  386.     CMP    D    ;CHECKSUM OK?
  387.     JNZ    RCVSERR ;NO, ERROR
  388. ;
  389. ;Got a sector, it's a duplicate if = previous,
  390. ;    or OK if = 1 + previous sector
  391. ;
  392.     LDA    RCVSNO    ;GET RECEIVED
  393.     MOV    B,A    ;SAVE IT
  394.     LDA    SECTNO    ;GET PREV
  395.     CMP    B    ;PREV REPEATED?
  396.     JZ    RECVACK ;ACK TO CATCH UP
  397.     INR    A    ;CALC NEXT SECTOR #
  398.     CMP    B    ;MATCH?
  399.     JNZ    ABORT    ;NO MATCH - STOP SENDER, EXIT
  400.     RET        ;CARRY OFF - NO ERRORS
  401. ;
  402. ;Previous sector repeated, due to the last ACK
  403. ;being garbaged.  ACK it so sender will catch up 
  404. ;
  405. RECVACK CALL    SENDACK ;SEND THE ACK,
  406.     JMP    RCVSECT ;GET NEXT BLOCK
  407. ;
  408. ;Send an ACK for the sector
  409. ;
  410. SENDACK MVI    A,ACK    ;GET ACK
  411.     CALL    SEND    ;..AND SEND IT
  412.     RET
  413. ;
  414. ;---->    SENDHDR: Send the sector header
  415. ;
  416. ;SEND: (SOH) (block #) (complemented block #)
  417. ;
  418. SENDHDR MVI    A,SOH    ;SEND..
  419.     CALL    SEND    ;..SOH,
  420.     LDA    SECTNO    ;THEN SEND..
  421.     CALL    SEND    ;..SECTOR #
  422.     LDA    SECTNO    ;THEN SECTOR #
  423.     CMA        ;..COMPLEMENTED..
  424.     CALL    SEND    ;..SECTOR #
  425.     RET        ;FROM SENDHDR
  426. ;
  427. ;---->    SENDSEC: Send the data sector
  428. ;
  429. SENDSEC MVI    C,0    ;INIT CKSUM
  430.     LXI    H,BASE+80H ;POINT TO BUFFER
  431. SENDC    MOV    A,M    ;GET A CHAR
  432.     CALL    SEND    ;SEND IT
  433.     INR    L    ;POINT TO NEXT CHAR
  434.     JNZ    SENDC    ;LOOP IF <100H
  435.     RET        ;FROM SENDSEC
  436. ;
  437. ;---->    SENDCKS: Send the checksum
  438. ;
  439. SENDCKS MOV    A,C    ;SEND THE..
  440.     CALL    SEND    ;..CHECKSUM
  441.     RET        ;FROM SENDCKS
  442. ;
  443. ;---->    GETACK: Get the ACK on the sector
  444. ;
  445. ;Returns with carry clear if ACK received.
  446. ;If an ACK is not received, the error count
  447. ;is incremented, and if less than "ERRLIM",
  448. ;carry is set and control returns.  If the
  449. ;error count is at "ERRLIM", the program
  450. ;aborts.
  451. ;
  452. GETACK    MVI    B,10    ;WAIT 10 SECONDS MAX
  453.     CALL    RECVDG    ;RECV W/GARBAGE COLLECT
  454.     JC    GETATOT ;TIMED OUT
  455.     CPI    ACK    ;OK? (CARRY OFF IF =)
  456.     RZ        ;YES, RET FROM GETACK
  457. ;
  458. ;Timeout or error on ACK - bump error count
  459. ;
  460. ACKERR    LDA    ERRCT    ;GET COUNT
  461.     INR    A    ;BUMP IT
  462.     STA    ERRCT    ;SAVE BACK
  463.     CPI    ERRLIM    ;AT LIMIT?
  464.     RC        ;NOT AT LIMIT
  465. ;
  466. ;Reached error limit
  467. ;
  468. CSABORT CALL    ERXIT
  469.     DB    '++CAN''T SEND SECTOR '
  470.     DB    '- ABORTING++',CR,LF,'$'
  471. ;
  472. ;Timeout getting ACK
  473. ;
  474. GETATOT JMP    ACKERR    ;NO MSG
  475. ABORT    LXI    SP,STACK
  476. ;
  477. ABORTL    MVI    B,1    ;1 SEC. W/O CHARS.
  478.     CALL    RECV
  479.     JNC    ABORTL    ;LOOP UNTIL SENDER DONE
  480.     MVI    A,CAN    ;CONTROL X
  481.     CALL    SEND    ;STOP SENDING END
  482. ;
  483. ABORTW    MVI    B,1    ;1 SEC W/O CHARS.
  484.     CALL    RECV
  485.     JNC    ABORTW    ;LOOP UNTIL SENDER DONE
  486.     MVI    A,' '    ;GET A SPACE...
  487.     CALL    SEND    ;TO CLEAR OUT CONTROL X
  488.     CALL    ERXIT    ;EXIT WITH ABORT MSG
  489.     DB    'XMODEM PROGRAM CANCELLED',CR,LF,'$'
  490. ;
  491. ;---->    INCRSNO: Increment sector #
  492. ;
  493. INCRSNO LDA    SECTNO    ;INCR..
  494.     INR    A    ;..SECT..
  495.     STA    SECTNO    ;..NUMBER
  496.     RET
  497. ;
  498. ;---->    CHEKFIL: See if file exists
  499. ;
  500. ;If it exists, say use a different name.
  501. ;
  502. CHEKFIL LXI    D,FCB    ;POINT TO CTL BLOCK
  503.     MVI    C,SRCHF ;SEE IF IT..
  504.     CALL    BDOS    ;..EXISTS
  505.     INR    A    ;FOUND?
  506.     RZ        ;..NO, RETURN
  507.     CALL    ERXIT    ;EXIT, PRINT ERROR MESSAGE
  508.     DB    '++FILE EXISTS, USE A DIFFERENT NAME++'
  509.     DB    CR,LF,'$'
  510. ;
  511. ;---->    MAKEFIL: Makes the file to be received
  512. ;
  513. MAKEFIL    XRA    A    ;SET EXT & REC # TO 0
  514.     STA    FCBEXT
  515.     STA    FCBSNO
  516.     LXI    D,FCB    ;POINT TO FCB
  517.     MVI    C,MAKE    ;GET BDOS FNC
  518.     CALL    BDOS    ;TO THE MAKE
  519.     INR    A    ;FF=BAD?
  520.     RNZ        ;OPEN OK
  521. ;Directory full - can't make file
  522.     CALL    ERXIT
  523.     DB    '++ERROR - CAN''T MAKE FILE++',CR,LF
  524.     DB    'Directory must be full',CR,LF,'$'
  525. ;
  526. ;---->    CNREC: Computes record count, and saves it
  527. ;           until successful file OPEN.
  528. ;
  529. ;LOOK UP THE FCB IN THE DIRECTORY
  530. CNREC    MVI    A,'?'    ;MATCH ALL EXTENTS
  531.     STA    FCBEXT
  532.     MVI    C,SRCHF ;GET 'SEARCH FIRST' FNC
  533.     LXI    D,FCB
  534.     CALL    BDOS    ;READ FIRST
  535.     INR    A    ;WERE THERE ANY?
  536.     JNZ    SOME    ;GOT SOME
  537.     CALL    ERXIT
  538.     DB    '++FILE NOT FOUND++$'
  539. ;
  540. ;READ MORE DIRECTORY ENTRIES
  541. MOREDIR    MVI    C,SRCHN ;SEARCH NEXT
  542.     LXI    D,FCB
  543.     CALL    BDOS    ;READ DIR ENTRY
  544.     INR    A    ;CHECK FOR END (0FFH)
  545.     RZ        ;NO MORE - RETURN
  546. ;POINT TO DIRECTORY ENTRY 
  547. SOME    DCR    A    ;UNDO PREV 'INR A'
  548.     ANI    3    ;MAKE MODULUS 4
  549.     ADD    A    ;MULTIPLY...
  550.     ADD    A    ;..BY 32 BECAUSE
  551.     ADD    A    ;..EACH DIRECTORY
  552.     ADD    A    ;..ENTRY IS 32
  553.     ADD    A    ;..BYTES LONG
  554.     LXI    H,BASE+80H ;POINT TO BUFFER
  555.     ADD    L    ;POINT TO ENTRY
  556.     ADI    15    ;OFFSET TO RECORD COUNT
  557.     MOV    L,A    ;HL NOW POINTS TO REC COUNT
  558.     MOV    L,M    ;GET RECORD COUNT
  559.     MVI    H,0
  560.     XCHG        ;SAVE COUNT IN DE
  561.     LHLD    RCNT    ;GET OLD COUNT
  562.     DAD    D    ;ADD IN NEW COUNT
  563.     SHLD    RCNT    ;SAVE NEW RECORD COUNT
  564.     JMP    MOREDIR    ;GO SEE IF MORE EXTENTS
  565. ;
  566. ;---->    OPENFIL: Opens the file to be sent
  567. ;
  568. OPENFIL    XRA    A    ;SET EXT & REC # TO 0 FOR PROPER OPEN
  569.     STA    FCBEXT
  570.     STA    FCBSNO
  571.     LXI    D,FCB    ;POINT TO FILE
  572.     MVI    C,OPEN    ;GET FUNCTION
  573.     CALL    BDOS    ;OPEN IT
  574.     INR    A    ;OPEN OK?
  575.     JNZ    OPENOK    ;..YES
  576.     CALL    ERXIT    ;..NO, ABORT
  577.     DB    '++OPEN ERROR++',CR,LF,'$'
  578. ;
  579. ;Check for distribution-protected file
  580. ;
  581. OPENOK    LDA    FCB+1    ;FIRST CHAR OF FILE NAME
  582.     ANI    80H    ;CHECK BIT 7
  583.     JZ    OPENOK2 ;IT WAS OFF, FILE CAN BE SENT
  584. ;
  585. OPENOT    CALL    ERXIT    ;EXIT W/MESSAGE
  586.     DB    '++THIS FILE IS NOT FOR DISTRIBUTION, SORRY++'
  587.     DB    CR,LF,'$'
  588. ;
  589. OPENOK2 EQU    $
  590. ;
  591.     IF    NOLBS OR NOCOMS ;CHECK FOR SEND RESTRICTIONS
  592.     LXI    H,FCB+11
  593.     MOV    A,M    ;CHECK FOR PROTECT ATTR
  594.     ANI    7FH    ;REMOVE CP/M 2.x ATTRS
  595.     ENDIF        ;NOLBS OR NOCOMS
  596. ;
  597.     IF    NOLBS    ;DON'T ALLOW '#' TO BE SENT.
  598.     CPI    '#'    ;CHK FOR '#' AS LAST FIRST
  599.     JZ    OPENOT    ;IF '#', CAN'T SEND, SHOW WHY
  600.     ENDIF        ;NOLBS
  601. ;
  602.     IF    NOCOMS    ;DON'T ALLOW .COM TO BE SENT
  603.     CPI    'M'    ;IF NOT, CHK FOR '.COM'
  604.     JNZ    OPENOK3 ;IF NOT, OK TO SEND
  605.     DCX    H
  606.     MOV    A,M    ;CHK NEXT CHAR
  607.     ANI    7FH    ;STRIP ATTRIBUTES
  608.     CPI    'O'    ; 'O'?
  609.     JNZ    OPENOK3 ;IF NOT, OK TO SEND
  610.     DCX    H
  611.     MOV    A,M    ;NOW CHK FIRST CHAR
  612.     ANI    7FH    ;STRIP ATTRIBUTES
  613.     CPI    'C'    ; 'C' AS IN '.COM'?
  614.     JNZ    OPENOK3 ;IF NOT, CONTINUE
  615.     CALL    ERXIT    ;EXIT W/MESSAGE
  616.     DB    '++CAN''T SEND A .COM FILE++'
  617.     DB    CR,LF,'$'
  618.     ENDIF        ;NOCOMS
  619. ;
  620. OPENOK3 CALL    ILPRT    ;PRINT:
  621.     DB    'FILE OPEN, SIZE: ',0
  622.     LHLD    RCNT    ; Get record count.
  623.     CALL    DECOUT    ;PRINT DECIMAL NUMBER OF SECTORS
  624.     CALL    ILPRT    ;PRINT:
  625.     DB    ' SECTORS',CR,LF,0
  626.     RET
  627. ;
  628. ;---->    CLOSFIL: Closes the received file
  629. ;
  630. CLOSFIL LXI    D,FCB    ;POINT TO FILE
  631.     MVI    C,CLOSE ;GET FUNCTION
  632.     CALL    BDOS    ;CLOSE IT
  633.     INR    A    ;CLOSE OK?
  634.     RNZ        ;..YES, RETURN
  635.     CALL    ERXIT    ;..NO, ABORT
  636.     DB    '++CAN''T CLOSE FILE++',CR,LF,'$'
  637. ;
  638. ;
  639. ;----> DECOUT: Decimal output routine
  640. ;
  641. DECOUT:    PUSH    B
  642.     PUSH    D
  643.     PUSH    H
  644.     LXI    B,-10
  645.     LXI    D,-1
  646. ;
  647. DECOU2:    DAD    B
  648.     INX    D
  649.     JC    DECOU2
  650.     LXI    B,10
  651.     DAD    B
  652.     XCHG
  653.     MOV    A,H
  654.     ORA    L
  655.     CNZ    DECOUT
  656.     MOV    A,E
  657.     ADI    '0'
  658.     CALL    CTYPE
  659.     POP    H
  660.     POP    D
  661.     POP    B
  662.     RET
  663. ;
  664. ;---->    RDSECT: Reads a sector
  665. ;
  666. ;For speed, this routine buffers up 16
  667. ;sectors at a time.
  668. ;
  669. RDSECT    LDA    SECINBF ;GET # SECT IN BUFF.
  670.     DCR    A    ;DECREMENT..
  671.     STA    SECINBF ;..IT
  672.     JM    RDBLOCK ;EXHAUSTED?  NEED MORE.
  673.     LHLD    SECPTR    ;GET POINTER
  674.     LXI    D,BASE+80H ;TO DATA
  675.     CALL    MOVE128 ;MOVE TO BUFFER
  676.     SHLD    SECPTR    ;SAVE BUFFER POINTER
  677.     RET        ;FROM "READSEC"
  678. ;
  679. ;Buffer is empty - read in another block of 16
  680. ;
  681. RDBLOCK LDA    EOFLG    ;GED EOF FLAG
  682.     CPI    1    ;IS IT SET?
  683.     STC        ;TO SHOW EOF
  684.     RZ        ;GOT EOF
  685.     MVI    C,0    ;SECTORS IN BLOCK
  686.     LXI    D,DBUF    ;TO DISK BUFFER
  687. ;
  688. RDSECLP PUSH    B
  689.     PUSH    D
  690.     MVI    C,STDMA ;SET DMA..
  691.     CALL    BDOS    ;..ADDR
  692.     LXI    D,FCB
  693.     MVI    C,READ
  694.     CALL    BDOS
  695.     POP    D
  696.     POP    B
  697.     ORA    A    ;READ OK?
  698.     JZ    RDSECOK ;YES
  699.     DCR    A    ;EOF?
  700.     JZ    REOF    ;GOT EOF
  701. ;
  702. ;Read error
  703. ;
  704.     CALL    ERXIT
  705.     DB    '++FILE READ ERROR++',CR,LF,'$'
  706. ;
  707. RDSECOK LXI    H,80H    ;ADD LENGTH OF ONE SECTOR...
  708.     DAD    D    ;...TO NEXT BUFF
  709.     XCHG        ;BUFF TO DE
  710.     INR    C    ;MORE SECTORS?
  711.     MOV    A,C    ;GET COUNT
  712.     CPI    16    ;DONE?
  713.     JZ    RDBFULL ;..YES, BUFF IS FULL
  714.     JMP    RDSECLP ;READ MORE
  715. ;
  716. REOF    MVI    A,1
  717.     STA    EOFLG    ;SET EOF FLAG
  718.     MOV    A,C
  719. ;
  720. ;Buffer is full, or got EOF
  721. ;
  722. RDBFULL STA    SECINBF ;STORE SECTOR COUNT
  723.     LXI    H,DBUF    ;INIT BUFFER..
  724.     SHLD    SECPTR    ;..POINTER
  725.     LXI    D,BASE+80H ;RESET..
  726.     MVI    C,STDMA ;..DMA..
  727.     CALL    BDOS    ;..ADDR
  728.     JMP    RDSECT    ;PASS SECT TO CALLER
  729. ;
  730. ;---->    WRSECT: Write a sector
  731. ;
  732. ;Writes the sector into a buffer.  When 16
  733. ;have been written, writes the block to disk.
  734. ;
  735. ;Entry point "WRBLOCK" flushes the buffer at EOF.
  736. ;
  737. WRSECT    LHLD    SECPTR    ;GET BUFF ADDR
  738.     XCHG        ;TO DE FOR MOVE
  739.     LXI    H,BASE+80H    ;FROM HERE
  740.     CALL    MOVE128 ;MOVE TO BUFFER
  741.     XCHG        ;SAVE NEXT..
  742.     SHLD    SECPTR    ;..BLOCK POINTER
  743.     LDA    SECINBF ;BUMP THE..
  744.     INR    A    ;..SECTOR #..
  745.     STA    SECINBF ;..IN THE BUFF
  746.     CPI    16    ;HAVE WE 16?
  747.     RNZ        ;NO, RETURN
  748. ;
  749. ;---->    WRBLOCK: Writes a block to disk
  750. ;
  751. WRBLOCK LDA    SECINBF ;# SECT IN BUFFER
  752.     ORA    A    ;0 MEANS END OF FILE
  753.     RZ        ;NONE TO WRITE
  754.     MOV    C,A    ;SAVE COUNT
  755.     LXI    D,DBUF    ;POINT TO DISK BUFF
  756. ;
  757. DKWRLP    PUSH    H
  758.     PUSH    D
  759.     PUSH    B
  760.     MVI    C,STDMA ;SET DMA
  761.     CALL    BDOS    ;TO BUFFER
  762.     LXI    D,FCB    ;THEN WRITE
  763.     MVI    C,WRITE ;..THE..
  764.     CALL    BDOS    ;..BLOCK
  765.     POP    B
  766.     POP    D
  767.     POP    H
  768.     ORA    A
  769.     JNZ    WRERR    ;OOPS, ERROR
  770.     LXI    H,80H    ;LENGTH OF 1 SECT
  771.     DAD    D    ;HL= NEXT BUFF
  772.     XCHG        ;TO DE FOR SETDMA
  773.     DCR    C    ;MORE SECTORS?
  774.     JNZ    DKWRLP    ;..YES, LOOP
  775.     XRA    A    ;GET A ZERO
  776.     STA    SECINBF ;RESET # OF SECTORS
  777.     LXI    H,DBUF    ;RESET BUFFER..
  778.     SHLD    SECPTR    ;..POINTER
  779. ;
  780. RSDMA    LXI    D,BASE+80H ;RESET..
  781.     MVI    C,STDMA ;..DMA..
  782.     CALL    BDOS    ;..ADDR
  783.     RET
  784. ;
  785. WRERR    CALL    RSDMA    ;RESET DMA TO NORM.
  786.     MVI    C,CAN    ;CANCEL..
  787.     CALL    SEND    ;..SENDER
  788.     CALL    ERXIT    ;EXIT W/MSG:
  789.     DB    '++ERROR WRITING FILE++',CR,LF,'$'
  790. ;
  791. ;---->    RECV: Receive a character
  792. ;
  793. ;Timeout time is in B, in seconds.  Entry via
  794. ;"RECVDG" deletes garbage characters on the
  795. ;line.    For example, having just sent a sector,
  796. ;calling RECVDG will delete any line-noise-induced
  797. ;characters "long" before the ACK/NAK would
  798. ;be received.
  799. ;
  800. RECVDG    EQU    $    ;RECEIVE W/GARBAGE DELETE
  801.     IN    MODDATP ;GET A CHAR
  802.     IN    MODDATP ;..TOTALLY PURGE UART
  803. ;
  804. RECV    PUSH    D    ;SAVE
  805. ;
  806.     IF    FASTCLK ;4MHZ?
  807.     MOV    A,B    ;GET TIME REQUEST
  808.     ADD    A    ;DOUBLE IT
  809.     MOV    B,A    ;NEW TIME IN B
  810.     ENDIF
  811. ;
  812. MSEC    LXI    D,50000 ;1 SEC DCR COUNT
  813. ;
  814.     IF    NOT DCH
  815. MWTI    IN    MODCTLP ;CHECK STATUS
  816.     ENDIF
  817. ;
  818.     IF    DCH
  819. MWTI    IN    MODCTL2 ;CHECK STATUS
  820.     ENDIF
  821. ;
  822.     ANI    MODRCVB ;ISOLATE BIT
  823.     CPI    MODRCVR ;READY?
  824.     JZ    MCHAR    ;GOT CHAR
  825.     DCR    E    ;COUNT..
  826.     JNZ    MWTI    ;..DOWN..
  827.     DCR    D    ;..FOR..
  828.     JNZ    MWTI    ;..TIMEOUT
  829.     DCR    B    ;MORE SECONDS?
  830.     JNZ    MSEC    ;YES, WAIT
  831. ;
  832. ;Modem timed out receiving
  833. ;
  834.     POP    D    ;RESTORE D,E
  835.     STC        ;CARRY SHOWS TIMEOUT
  836.     RET
  837. ;
  838. ;Got character from modem
  839. ;
  840. MCHAR    IN    MODDATP ;READ THE CHAR
  841.     POP    D    ;RESTORE DE
  842. ;
  843. ;Calc checksum
  844. ;
  845.     PUSH    PSW    ;SAVE THE CHAR
  846.     ADD    C    ;ADD TO CHECKSUM
  847.     MOV    C,A    ;SAVE CHECKSUM
  848.     POP    PSW    ;RESTORE CHAR
  849.     ORA    A    ;CARRY OFF: NO ERROR
  850.     RET        ;FROM "RECV"
  851. ;
  852. ;---->    SEND: Send a character to the modem
  853. ;
  854. SEND    PUSH    PSW    ;SAVE THE CHAR
  855.     ADD    C    ;CALC CKSUM
  856.     MOV    C,A    ;SAVE CKSUM
  857. ;
  858.     IF    NOT DCH
  859. SENDW    IN    MODCTLP ;GET STATUS
  860.     ENDIF
  861. ;
  862.     IF    DCH
  863. SENDW    IN    MODCTL2 ;GET STATUS
  864.     ENDIF
  865. ;
  866.     ANI    MODSNDB ;ISOLATE READY BIT
  867.     CPI    MODSNDR ;READY?
  868.     JNZ    SENDW    ;..NO, WAIT
  869.     POP    PSW    ;GET CHAR
  870.     OUT    MODDATP ;OUTPUT IT
  871.     RET        ;FROM "SEND"
  872. ;
  873. ;---->    WAITNAK: Waits for initial NAK
  874. ;
  875. ;To ensure no data is sent until the receiving
  876. ;program is ready, this routine waits for the
  877. ;first timeout-NAK from the receiver.
  878. ;(E) contains the # of seconds to wait.
  879. ;
  880. WAITNAK MVI    B,1    ;TIMEOUT DELAY
  881.     CALL    RECV    ;DID WE GET..
  882.     CPI    NAK    ;..A NAK?
  883.     RZ        ;YES, SEND BLOCK
  884.     DCR    E    ;80 TRIES?
  885.     JZ    ABORT    ;YES, ABORT
  886.     JMP    WAITNAK ;NO, LOOP
  887. ;
  888. ;---->    MOVEFCB: Moves FCB(2) to FCB
  889. ;
  890. ;In order to make the XMODEM command 'natural',
  891. ;i.e. XMODEM SEND FILENAME (MODEM S FN.FT) rather
  892. ;than XMODEM FILENAME SEND (MODEM FN.FT S), this
  893. ;routine moves the filename from the second FCB
  894. ;to the first.
  895. ;
  896. MOVEFCB LXI    H,FCB+16 ;FROM
  897.     LXI    D,FCB    ;TO
  898.     MVI    B,16    ;LEN
  899.     CALL    MOVE    ;DO THE MOVE
  900.     XRA    A    ;GET 0
  901.     STA    FCBSNO    ;ZERO SECTOR #
  902.     STA    FCBEXT    ;..AND EXTENT
  903.     RET
  904. ;
  905. CTYPE    PUSH    B    ;SAVE..
  906.     PUSH    D    ;..ALL..
  907.     PUSH    H    ;..REGS
  908.     MOV    E,A    ;CHAR TO E
  909.     MVI    C,WRCON ;GET BDOS FNC
  910.     CALL    BDOS    ;PRIN THE CHR
  911.     POP    H    ;RESTORE..
  912.     POP    D    ;..ALL..
  913.     POP    B    ;..REGS
  914.     RET        ;FROM "CTYPE"
  915. ;
  916. HEXO    PUSH    PSW    ;SAVE FOR RIGHT DIGIT
  917.     RAR        ;RIGHT..
  918.     RAR        ;..JUSTIFY..
  919.     RAR        ;..LEFT..
  920.     RAR        ;..DIGIT..
  921.     CALL    NIBBL    ;PRINT LEFT DIGIT
  922.     POP    PSW    ;RESTORE RIGHT
  923. ;
  924. NIBBL    ANI    0FH    ;ISOLATE DIGIT
  925.     CPI    10    ;IS IT <10?
  926.     JC    ISNUM    ;YES, NOT ALPHA
  927.     ADI    7    ;ADD ALPHA BIAS
  928. ;
  929. ISNUM    ADI    '0'    ;MAKE PRINTABLE
  930.     JMP    CTYPE    ;..THEN TYPE IT
  931. ;
  932. ;---->    ILPRT: Inline print of message
  933. ;
  934. ;The call to ILPRT is followed by a message,
  935. ;binary 0 as the end.
  936. ;
  937. ILPRT    XTHL        ;SAVE HL, GET HL=MSG
  938. ;
  939. ILPLP    MOV    A,M    ;GET CHAR
  940.     ORA    A    ;END OF MSG?
  941.     JZ    ILPRET    ;..YES, RETURN
  942.     CALL    CTYPE    ;TYPE THE MSG
  943.     INX    H    ;TO NEXT CHAR
  944.     JMP    ILPLP    ;LOOP
  945. ;
  946. ILPRET    XTHL        ;RESTORE HL
  947.     RET        ;PAST MSG
  948. ;
  949. ;---->    ERXIT: Exit printing message following call
  950. ;
  951. ERXIT    POP    D    ;GET MESSAGE
  952.     MVI    C,PRINT ;GET BDOS FNC
  953.     CALL    BDOS    ;PRINT MESSAGE
  954. ;
  955. EXIT    LHLD    STACK    ;GET ORIGINAL STACK
  956.     SPHL        ;RESTORE IT
  957.     RET        ;--EXIT-- TO CP/M
  958. ;
  959. ;Move 128 characters
  960. ;
  961. MOVE128 MVI    B,128    ;SET MOVE COUNT
  962. ;
  963. ;Move from (HL) to (DE) length in (B)
  964. ;
  965. MOVE    MOV    A,M    ;GET A CHAR
  966.     STAX    D    ;STORE IT
  967.     INX    H    ;TO NEXT "FROM"
  968.     INX    D    ;TO NEXT "TO"
  969.     DCR    B    ;MORE?
  970.     JNZ    MOVE    ;..YES, LOOP
  971.     RET        ;..NO, RETURN
  972. ;
  973. ;Temporary storage area
  974. ;
  975. RCNT    DW    0    ;RECORD COUNT
  976. RCVSNO    DB    0    ;SECT # RECEIVED
  977. SECTNO    DB    0    ;CURRENT SECTOR NUMBER 
  978. ERRCT    DB    0    ;ERROR COUNT
  979. ;Following 3 used by disk buffering routines
  980. EOFLG    DB    0    ;EOF FLAG (1=TRUE)
  981. SECPTR    DW    DBUF
  982. SECINBF DB    0    ;# OF SECTORS IN BUFFER
  983.     DS    60    ;STACK AREA
  984. STACK    DS    2    ;STACK POINTER
  985. ;
  986. ;16 sector disk buffer
  987. ;
  988. DBUF    EQU    $    ;16 SECTOR DISK BUFFER
  989. ;
  990. ;BDOS equates
  991. ;
  992. RDCON    EQU    1
  993. WRCON    EQU    2
  994. PRINT    EQU    9
  995. CONST    EQU    11    ;CONSOLE STAT
  996. OPEN    EQU    15    ;0FFH = NOT FOUND
  997. CLOSE    EQU    16    ;    "       "
  998. SRCHF    EQU    17    ;    "       "
  999. SRCHN    EQU    18    ;    "       "
  1000. ERASE    EQU    19    ;NO RET CODE
  1001. READ    EQU    20    ;0=OK, 1=EOF
  1002. WRITE    EQU    21    ;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC
  1003. MAKE    EQU    22    ;0FFH=BAD
  1004. REN    EQU    23    ;0FFH=BAD
  1005. STDMA    EQU    26    ;SET DMA
  1006. BDOS    EQU    BASE+5
  1007. FCB    EQU    BASE+5CH ;SYSTEM FCB
  1008. FCBEXT    EQU    FCB+12    ;FILE EXTENT
  1009. FCBSNO    EQU    FCB+32    ;SECTOR #
  1010. FCB2    EQU    BASE+6CH ;SECOND FCB
  1011. ;
  1012.     END
  1013.  
  1014.