; TITLE: 'XFER' ; ; JULY 23, 2023 ; ; Copyright (c) 2013 Martin Eberhard ; Copyright (c) 2015 Mike Douglas ; Copyright (c) 2020 The Glitch Works ; Copyright (c) 2023 Solid State Machines ; ; Simple file transfer program. Some code ; taken from M Eberhard XMODEM 1.01 and ; Glitch Works PCGET and PCPUT. ; ; GPL3 LICENSE included in the project root ; for licensing information. ;------------------------------------------- O 0000 00 00 00 .PROJECT xfer.com 0003 CTRLC EQU 3 000a LF EQU 10 000d CR EQU 13 001a EOF EQU 1AH 000f TOFAST EQU 15 ;2 SECONDS 00e1 TOSLOW EQU 225 ;30 SECONDS 05dd CMDSND EQU 05DDH ;SLAVE: SEND COMMAND ;------------------------------------------- ;CP/M 2 BDOS Equates ;------------------------------------------- fa00 BIOS EQU 0FA00H fa03 WBOOT EQU BIOS+3 ;Warm Boot fa09 CONIN EQU BIOS+9 ;Console In fa12 PUNCH EQU BIOS+18 ;Serial Send fa15 READER EQU BIOS+21 ;Serial Recv 0007 IOBYTE EQU 7 0009 PRINT EQU 9 000f OPEN EQU 15 ;0FFH=NOT FOUND 0010 CLOSE EQU 16 ; " " 0011 SRCHF EQU 17 ; " " 0012 SRCHN EQU 18 ; " " 0013 ERASE EQU 19 ;NO RET CODE 0014 READ EQU 20 ;0=OK, 1=EOF 0015 WRITE EQU 21 ;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC 0016 MAKE EQU 22 ;0FFH=BAD 0017 REN EQU 23 ;0FFH=BAD 001a STDMA EQU 26 0005 BDOS EQU 5 005c FCB EQU 5CH ;DEFAULT FCB 005d PARAM EQU FCB+1 ;COMMAND LINE PARAMETER 1 IN FCB 0080 CMDLN EQU 80H 0100 ORG 100H ; ; Get ready and begin the transfer ; ; This routine checks for the presence of a filename. If no ; filename is supplied, a help message is printed and we ; exit. ; 0100 3a 5d 00 START: LDA PARAM ;A=1st character of parameter 1 0103 fe 20 CPI ' ' ;make sure file name present 0105 ca 06 03 JZ HELP ;no filename? 0108 21 80 00 lxi h,CMDLN ;CP/M put cmd line here 010b 46 mov b,m ;1st byte is byte count 010c 23 inx h ;point to the string 010d cd d4 02 call SSKIP ;skip initial spaces 0110 ca 06 03 jz HELP ;no parameters? ; ; Skip past the file name, which CP/M already ; put in the FCB for us ; b = bytes remaining to see in COMBUF ; hl points to next chr in COMBUF ; 0113 cd de 02 SKPFIL: call CMDCHR 0116 ca 06 03 jz HELP ;no /R or /S 0119 fe 20 cpi ' ' ;hunt for a space 011b c2 13 01 jnz SKPFIL ; ; Parse all command line options and set variables ; accordingly. Each option must be preceeded by a ; '/' Options may be preceeded by any reasonable ; number of spaces. ; 011e cd d4 02 OPTLUP: call SSKIP ;skip spaces 0121 ca 64 01 jz OPTDONE ;end of input line? 0124 fe 2f cpi '/' ;all start with / 0126 c2 06 03 jnz HELP ;error:no slash 0129 cd de 02 call CMDCHR ;Get an option chr 012c ca 06 03 jz HELP ;Error: nothing after / 012f 32 5c 01 sta PARERR ;put it in error msg ; ; Got a command line option in a. Loop through ; table of options, looking for a match. Update ; the appropriate option variable with the table ; value. Error exit if not in table. ; TRASH a,c,de ; 0132 e5 push h ;Save COMBUF pointer 0133 21 e7 02 lxi h,OPTTAB 0136 be CHKLUP: cmp m ;Match? (alpha order) 0137 23 inx h ;get var value 0138 4e mov c,m 0139 23 inx h ;get var address 013a 5e mov e,m 013b 23 inx h 013c 56 mov d,m 013d 23 inx h 013e ca 5e 01 jz OPMTCH ;matched an option 0141 d2 36 01 jnc CHKLUP ;No match: keep looking ; ; Illegal option. Print message and return to CP/M ; 0144 cd a9 02 call EXIT ;Exit with this message 0147 45 72 72 6f db 'Error Invalid Param /' 014b 72 20 49 6e 014f 76 61 6c 69 0153 64 20 50 61 0157 72 61 6d 20 015b 2f 015c 26 PARERR: db '&' ;parameter goes here 015d 24 db '$' 015e 79 OPMTCH: mov a,c ;value from table 015f 12 stax d ;save value 0160 e1 pop h 0161 c3 1e 01 jmp OPTLUP ;look for more ; Done parsing command line. ; ; Did we get a direction? return with ; a=(XMODE) + 1 if so, error if not ; 0164 3a 71 03 OPTDONE: lda XMODE ;did /R or /S get set? 0167 b7 ora a ;0 means uninit'ed 0168 ca 06 03 jz HELP 016b 3d dcr a ;0 means rx 016c ca fb 01 jz RECV_FILE ; fall into SEND_FILE ; ; Send FIle ; 016f 0e 07 MVI C,IOBYTE 0171 cd 05 00 CALL BDOS ;GET IO BYTE 0174 e6 01 ANI 1 ;TTY=0 0176 ca 87 01 JZ TX_TTY 0179 11 d7 03 LXI D,ANYKEY 017c 0e 09 MVI C,PRINT ;PRINT SEND MSG 017e cd 05 00 CALL BDOS 0181 cd 09 fa CALL CONIN 0184 c3 92 01 JMP TX_CRT 0187 11 72 03 TX_TTY: LXI D,SENDMSG 018a 0e 09 MVI C,PRINT ;PRINT SEND MSG 2 018c cd 05 00 CALL BDOS 018f cd cc 02 CALL SLEEP 0192 11 5c 00 TX_CRT: LXI D,FCB 0195 0e 0f MVI C,OPEN ;OPEN FILE 0197 cd 05 00 CALL BDOS 019a 3c INR A ;OPEN OK? 019b c2 b5 01 JNZ READ_SECTOR ;GOOD OPEN 019e cd a9 02 CALL EXIT 01a1 45 72 72 6f DB 'Error Opening File.$' 01a5 72 20 4f 70 01a9 65 6e 69 6e 01ad 67 20 46 69 01b1 6c 65 2e 24 01b5 READ_SECTOR: 01b5 11 5c 00 LXI D,FCB 01b8 0e 14 MVI C,READ ;READ SECTOR 01ba cd 05 00 CALL BDOS 01bd b7 ORA A 01be ca e9 01 JZ SEND_LOOP 01c1 3d DCR A ;EOF? 01c2 ca dc 01 JZ SEND_DONE ;DONE 01c5 cd a9 02 CALL EXIT 01c8 45 72 72 6f DB 'Error Reading File.$' 01cc 72 20 52 65 01d0 61 64 69 6e 01d4 67 20 46 69 01d8 6c 65 2e 24 01dc SEND_DONE: 01dc 0e 07 MVI C,IOBYTE 01de cd 05 00 CALL BDOS ;GET IO BYTE 01e1 e6 01 ANI 1 ;TTY=0 01e3 cc cc 02 CZ SLEEP ;WAIT ON TTY 01e6 c3 f0 02 JMP XFER_DONE 01e9 SEND_LOOP: 01e9 21 80 00 LXI H,80H 01ec SEND_CHAR: 01ec 3e 80 MVI A,80H ;SEND WHEN BUFF FULL 01ee 4e MOV C,M ;READ CHAR 01ef e5 PUSH H 01f0 cd 12 fa CALL PUNCH ;TX CHAR 01f3 e1 POP H 01f4 2c INR L ;DONE? 01f5 c2 ec 01 JNZ SEND_CHAR 01f8 c3 b5 01 JMP READ_SECTOR ; ; Receive File ; 01fb RECV_FILE: 01fb 11 a7 03 LXI D,RECVMSG 01fe 0e 09 MVI C,PRINT ;PRINT RECV MSG 0200 cd 05 00 CALL BDOS 0203 11 5c 00 LXI D,FCB 0206 0e 11 MVI C,SRCHF ;FILE EXISTS? 0208 cd 05 00 CALL BDOS 020b 3c INR A ;FOUND? 020c ca 17 02 JZ NEW_FILE ;NO, SKIP ERASE 020f 11 5c 00 LXI D,FCB 0212 0e 13 MVI C,ERASE ;ERASE FILE 0214 cd 05 00 CALL BDOS 0217 NEW_FILE: 0217 11 5c 00 LXI D,FCB 021a 0e 16 MVI C,MAKE ;MAKE NEW FILE 021c cd 05 00 CALL BDOS 021f 3c INR A ;FF=BAD 0220 c2 3c 02 JNZ RECV_START ;OPEN OK 0223 cd a9 02 CALL EXIT 0226 45 72 72 6f DB 'Error Directory Full.$' 022a 72 20 44 69 022e 72 65 63 74 0232 6f 72 79 20 0236 46 75 6c 6c 023a 2e 24 023c RECV_START: 023c 06 e1 MVI B,TOSLOW 023e RECV_LOOP: 023e 21 80 00 LXI H,80H ;POINT TO BUFFER 0241 RECV_CHAR: 0241 cd ba 02 CALL RECV ;GET CHAR 0244 da 77 02 JC RECV_DONE ;RECEIVE DONE 0247 77 MOV M,A ;STORE CHAR 0248 2c INR L ;DONE? 0249 06 0f MVI B,TOFAST 024b c2 41 02 JNZ RECV_CHAR 024e cd 56 02 CALL WR_SEC ;GOT NEW SECTOR - WRITE IT 0251 06 0f MVI B,TOFAST 0253 c3 3e 02 JMP RECV_LOOP 0256 WR_SEC: 0256 11 5c 00 LXI D,FCB 0259 0e 15 MVI C,WRITE 025b cd 05 00 CALL BDOS 025e b7 ORA A 025f c8 RZ 0260 cd a9 02 CALL EXIT 0263 45 72 72 6f DB 'Error Writing File.$' 0267 72 20 57 72 026b 69 74 69 6e 026f 67 20 46 69 0273 6c 65 2e 24 ; ; Handle end-of-transfer ; 0277 RECV_DONE: 0277 3e 7f MVI A,7FH 0279 a5 ANA L 027a ca 86 02 JZ FCLOSE 027d 36 1a MVI M,EOF ;EOF CHAR 027f 2c INR L ;DONE? 0280 c2 77 02 JNZ RECV_DONE ;PAD FILE 0283 cd 56 02 CALL WR_SEC 0286 11 5c 00 FCLOSE: LXI D,FCB 0289 0e 10 MVI C,CLOSE 028b cd 05 00 CALL BDOS 028e 3c INR A 028f c2 f0 02 JNZ XFER_DONE 0292 cd a9 02 CALL EXIT 0295 45 72 72 6f DB 'Error Closing File.$' 0299 72 20 43 6c 029d 6f 73 69 6e 02a1 67 20 46 69 02a5 6c 65 2e 24 ; ; Exit and print a message ; 02a9 d1 EXIT: POP D ;GET MESSAGE 02aa 0e 09 MVI C,PRINT 02ac cd 05 00 CALL BDOS ;PRINT MESSAGE 02af 11 f9 03 LXI D,NEWLINE 02b2 0e 09 MVI C,PRINT 02b4 cd 05 00 CALL BDOS ;PRINT NEWLINE 02b7 c3 03 fa JMP WBOOT ;Warm boot CP/M ; ; Receive byte via READER ; 02ba e5 RECV: PUSH H 02bb c5 PUSH B 02bc cd 15 fa CALL READER ;RX CHAR 02bf c1 POP B 02c0 e1 POP H 02c1 ca c6 02 JZ TICKR ;GOT CHAR? 02c4 b7 ORA A ;TURN OFF CARRY TO SHOW NO TIMEOUT 02c5 c9 RET ;RET CHAR IN A 02c6 05 TICKR: DCR B ;DEC # OF TICKS 02c7 c2 ba 02 JNZ RECV 02ca 37 STC ;Set carry flag to show timeout 02cb c9 RET ; ; Sleep for long timeout ; 02cc 1e e1 SLEEP: MVI E,TOSLOW ;SLOW TIMEOUT 02ce 01 0a 01 LXI B,010AH ;SEQ 1, SLEEP COMMAND 02d1 dd 05 DW CMDSND ;CALL KERNEL 02d3 c9 RET ; ; Skip spaces in command line buffer ; On Entry: ; b has remaining COMBUF byte count ; hl points to the next chr in COMBUF ; On Exit: ; a = chr from COMBUF ; b has been decremented ; hl has been advanced ; Z=1 means end of buffer (and a is not valid) ;* 02d4 cd de 02 SSKIP: call CMDCHR 02d7 c8 rz ;Z set for nothing left 02d8 fe 20 cpi ' ' ;white space? 02da ca d4 02 jz SSKIP 02dd c9 ret ; ; Get next chr from command line buffer ; On Entry: ; b has remaining COMBUF byte count ; hl points to the next chr in COMBUF ; On Exit: ; a = chr from COMBUF, parity stripped ; b has been decremented ; hl has been advanced ; Z=1 means end of buffer, and a=0 ; Carry = 0 ; 02de 78 CMDCHR: mov a,b ;End of buffer already? 02df b7 ora a ;also clears carry 02e0 c8 rz ;and clears a 02e1 7e mov a,m ;get buffer chr 02e2 23 inx h ;bump buffer pointers 02e3 05 dcr b 02e4 e6 7f ani 7FH ;Strip parity, clear Z 02e6 c9 ret ; ; Command Line Options Table ; Table entries must be in alphabetical order, and ; terminated with 0FFh ; ; Each entry is 4 bytes long: ; byte1 = uppercase legal option letter ; byte2 = value for variable ; bytes3-4 = variable address ; 02e7 OPTTAB: ;Select receive mode: puts 1 in XMODE 02e7 52 01 db 'R',1 02e9 71 03 dw XMODE ;Select send mode: puts 2 in XMODE 02eb 53 02 db 'S',2 02ed 71 03 dw XMODE ;end of table marker 02ef ff db 0FFh ; ; Transfer done, clean up ; 02f0 XFER_DONE: 02f0 cd a9 02 CALL EXIT 02f3 54 72 61 6e DB 'Transfer Complete.$' 02f7 73 66 65 72 02fb 20 43 6f 6d 02ff 70 6c 65 74 0303 65 2e 24 ; ; Exit with usage message ; 0306 cd a9 02 HELP: call EXIT 0309 58 46 45 52 db 'XFER {/R or /S}',CR,LF 030d 20 3c 64 72 0311 69 76 65 3a 0315 66 69 6c 65 0319 6e 61 6d 65 031d 2e 65 78 74 0321 3e 20 7b 2f 0325 52 20 6f 72 0329 20 2f 53 7d 032d 0d 0a 032f 20 20 2f 52 db ' /R Receive file over serial.',CR,LF 0333 20 20 20 20 0337 52 65 63 65 033b 69 76 65 20 033f 66 69 6c 65 0343 20 6f 76 65 0347 72 20 73 65 034b 72 69 61 6c 034f 2e 0d 0a 0352 20 20 2f 53 db ' /S Send file over serial.$' 0356 20 20 20 20 035a 53 65 6e 64 035e 20 66 69 6c 0362 65 20 6f 76 0366 65 72 20 73 036a 65 72 69 61 036e 6c 2e 24 ; ; Variables and Storage Defines ; 0371 00 XMODE DB 0 ; ; Message Strings ; 0372 57 61 69 74 SENDMSG: db 'Waiting 15 seconds at start ' 0376 69 6e 67 20 037a 31 35 20 73 037e 65 63 6f 6e 0382 64 73 20 61 0386 74 20 73 74 038a 61 72 74 20 038e 61 6e 64 20 db 'and end of transfer...',CR,LF,'$' 0392 65 6e 64 20 0396 6f 66 20 74 039a 72 61 6e 73 039e 66 65 72 2e 03a2 2e 2e 0d 0a 03a6 24 03a7 57 61 69 74 RECVMSG: db 'Waiting 30 seconds to start ' 03ab 69 6e 67 20 03af 33 30 20 73 03b3 65 63 6f 6e 03b7 64 73 20 74 03bb 6f 20 73 74 03bf 61 72 74 20 03c3 72 65 63 65 db 'receiving file...',CR,LF,'$' 03c7 69 76 69 6e 03cb 67 20 66 69 03cf 6c 65 2e 2e 03d3 2e 0d 0a 24 03d7 50 72 65 73 ANYKEY: db 'Press any key to start transfer...' 03db 73 20 61 6e 03df 79 20 6b 65 03e3 79 20 74 6f 03e7 20 73 74 61 03eb 72 74 20 74 03ef 72 61 6e 73 03f3 66 65 72 2e 03f7 2e 2e 03f9 0d 0a 24 NEWLINE: db CR,LF,'$' 1 Error(s) 03fc END 03d7 ANYKEY 0005 BDOS fa00 BIOS 0136 CHKLUP 0010 CLOSE 02de CMDCHR 0080 CMDLN 05dd CMDSND fa09 CONIN 000d CR 0003 CTRLC 001a EOF 0013 ERASE 02a9 EXIT 005c FCB 0286 FCLOSE 0306 HELP 0007 IOBYTE 000a LF 0016 MAKE 03f9 NEWLINE 0217 NEW_FILE 000f OPEN 015e OPMTCH 0164 OPTDONE 011e OPTLUP 02e7 OPTTAB 005d PARAM 015c PARERR 0009 PRINT fa12 PUNCH 0014 READ fa15 READER 01b5 READ_SECTOR 02ba RECV 03a7 RECVMSG 0241 RECV_CHAR 0277 RECV_DONE 01fb RECV_FILE 023e RECV_LOOP 023c RECV_START 0017 REN 0372 SENDMSG 01ec SEND_CHAR 01dc SEND_DONE 01e9 SEND_LOOP 0113 SKPFIL 02cc SLEEP 0011 SRCHF 0012 SRCHN 02d4 SSKIP 0100 START 001a STDMA 02c6 TICKR 000f TOFAST 00e1 TOSLOW 0192 TX_CRT 0187 TX_TTY fa03 WBOOT 0015 WRITE 0256 WR_SEC 02f0 XFER_DONE 0371 XMODE