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
/
CIS
/
BUFEXC.ASM
< prev
next >
Wrap
Assembly Source File
|
2000-06-30
|
28KB
|
1,213 lines
; *** CompuServe Information Service Executive for CP/M (R)
; (CP/M is a trademark of Digital Research)
; Copyright (C) 1980, 1981 CompuServe Incorporated
; Version 2.3
; Written by:
; Russ Ranshaw
false: equ 0
true: equ not false
usebios: equ false ; true to use direct BIOS calls for
; console output
;*** Note: If you choose to use BDOS call for console
; write, AND have "shoxfr" true, you may not be able
; to do file transfers if your communication channel
; is operating at greater than 300 baud!
shoxfr: equ true ; true to show data during file transfer
sholcc: equ 09h ; lowest displayable control character for console
shohcc: equ 0dh ; highest "" (used in PRODSP to map ctl characters)
paglen: equ 60 ; printer page length
other: equ false ; true if the computer is NOT one of the special ones
hz89: equ false ; true if the computer is a Heath/Zenith 88, 89, 8
hz19: equ false ; Console is a Heath/Zenith -19
pmmi equ true ; true if PMMI modem board
if other
BBASE: EQU 0000H ; "PAGE 0" ADDRESS
CTL EQU 03H ; CONTROL PORT
SIO EQU 01H ; SIO PORT
SIOIR EQU 40H ; SIO PORT DATA INPUT READY FLAG
SIOTR EQU 80H ; SIO PORT TRANSMITTER READY FLAG
hitrue: equ false ; SIO flags are "hi" (1) when true
endif
if hz89
bbase: equ 4200h ; "Page 0" Address - Set to 0 if you have 0 based CP/M
ctl: equ 0ddh ; Line Control Register
sio: equ 0d8h ; Receive/Transmit Data Register
sioir: equ 01h ; Receive Data Ready flag
siotr: equ 20h ; Transmitter Buffer Ready flag
hitrue: equ true
endif
if pmmi
bbase: equ 0h ; "Page 0" address
basprt: equ 0c0h ; base i/o port address for pmmi board
ctl: equ basprt ; primary control port
sio: equ basprt+1 ; serial data port
sioir: equ 02h ; data input ready flag
siotr: equ 01h ; transmitter ready flag
hitrue: equ true ; flags are high when true
endif
; SPECIAL CHARACTERS FOR DATA TRANSMISSION PROTOCOL
SOH: EQU 01H ; START OF TEXT
ETX: EQU 03H ; END OF TEXT
EOT: EQU 04H ; END OF TRANSMISSION
ENQ: EQU 05H ; ^E, USED FOR PMMI EXIT W/O DISCONNECT
SI: EQU 0FH ; <SI> = SHIFT INTO PROTOCOL MODE
SO: EQU 0EH ; <SO> = SHIFT OUT OF PROTOCOL MODE]
; PROTOCOL MODE IMPLIES THAT <ESC> SEQUENCES
; ARE NOT SENT TO CONSOLE BUT ARE USED TO CONTROL
; THE UP/DOWN LOAD PROTOCOL
DC1: EQU 11H ; <DC1> CONTROL-Q: RESUME TRANSMISSION
DC2: EQU 12H ; <DC2> CONTROL-R: PRINTER ON
DC3: EQU 13H ; <DC3> CONTROL-S: STOP TRANSMISSION
DC4: EQU 14H ; <DC4> CONTROL-T: PRINTER OFF
KNAK: EQU 15H ; <NAK>
DLE: EQU 10H ; <DLE> (TRANSPARACY FLAG)
ESC: EQU 1BH ; ESCAPE
EOF: EQU 1AH ; ^Z (CP/M END OF FILE)
CR: EQU 0DH ; <RET>
LF: EQU 0AH ; <LF>
FF: EQU 0CH ; <FF>
MON: EQU 18H ; ^X (RETURN TO CP/M) (& DISCONNECT PMMI)
; CP/M EQUATES
BDOS: EQU BBASE+0005H ; MAIN ENTRY POINT FOR CP/M
TFCB: EQU BBASE+005CH ; DEFAULT FILE CONTROL BLOCK
TBUFF: EQU BBASE+0080H ; DEFAULT FILE BUFFER
TBASE: EQU BBASE+0100H ; TRANSIENT BASE
; DEFINE OFFSETS INTO FILE CONTROL BLOCK (FCB)
FCB$ET: EQU 0 ; ENTRY TYPE
FCB$FN: EQU 1 ; FILE NAME (8 BYTES)
FCB$FT: EQU 9 ; FILE TYPE (3 BYTES)
FCB$RC: EQU 15 ; RECORD COUNT (CURRENT EXTENT)
FCB$DM: EQU 16 ; DISK MAP
FCB$NR: EQU 32 ; NEXT RECORD NUMBER TO READ OR WRITE
; BDOS FUNCTIONS:
FN$SR: EQU 0 ; SYSTEM RESET
FN$RC: EQU 1 ; READ CONSOLE
FN$WC: EQU 2 ; WRITE CONSOLE
FN$RR: EQU 3 ; READ READER
FN$WP: EQU 4 ; WRITE PUNCH
FN$WL: EQU 5 ; WRITE LIST
FN$IS: EQU 7 ; INTERROGATE I/O STATUS
FN$AS: EQU 8 ; ALTER I/O STATUS
FN$PCB: EQU 9 ; PRINT CONSOLE BUFFER
FN$RCB: EQU 10 ; READ CONSOLE BUFFER
FN$CCS: EQU 11 ; CHECK CONSOLE STATUS
FN$LDH: EQU 12 ; LIFT DISK HEAD
FN$RDS: EQU 13 ; RESET DISK SYSTEM
FN$SD: EQU 14 ; SELECT DISK
FN$OPN: EQU 15 ; OPEN FILE
FN$CLS: EQU 16 ; CLOSE FILE
FN$SF: EQU 17 ; SEARCH FIRST
FN$SN: EQU 18 ; SEARCH NEXT
FN$DEL: EQU 19 ; DELETE FILE
FN$RDR: EQU 20 ; READ DISK RECORD
FN$WDR: EQU 21 ; WRITE DISK RECORD
FN$CRE: EQU 22 ; CREATE FILE
FN$REN: EQU 23 ; RENAME FILE
FN$IL: EQU 24 ; INTERROGATE LOGIN
FN$ID: EQU 25 ; INTERROGATE DISK
FN$SDA: EQU 26 ; SET DMA ADDRESS
FN$IA: EQU 27 ; INTERROGATE ALLOCATION
ORG TBASE
START: JMP START0 ; NORMAL START
INISIO: JMP SIOINI ; INITIALIZE MODEM UART
GETSIO: JMP SIOGET ; GET CHAR FROM MODEM UART
PUTSIO: JMP SIOPUT ; PUT A CHAR TO MODEM UART
; <ESC><I> response for this executive:
if other
SYSID: DB '#CPMTarbell,CC,HC,PA,PL',CR,00
endif
if hz89
sysid: db '#CPMHeath/Zenith,CC,HC,PA,PL',cr,00
endif
if pmmi
sysid: db '#CPMPMMI,CC,HC,PA,PL',CR,00
endif
; CC = Cursor Control
; Implies following cursor controls:
; <ESC><A> cursor up
; <ESC><B> cursor down
; <ESC><C> cursor right
; <ESC><D> cursor left
; <ESC><H> cursor home (line 1, column 1)
; <ESC><J> erase to end of screen
; <ESC><K> erase to end of line
; <ESC><j> erase screen and home cursor (also <FF>)
; <ESC><Y><L+31><C+31> position cursor to line L column C
; HC = Hard Copy
; Implies following:
; <DC2> (^R, 022 octal, 12 hex) enable printer; subsequent data
; will be copied to local printer
; <DC4> (^T, 024 octal, 14 hex) disable printer
; <ESC><e> disable terminal display
; <ESC><f> enable terminal display
; PA = A Protocol
; Implies file transfer capability using the CompuServe A protocol
; PL = Load Protocol
; Implies ability to load code segments under the CompuServe L protocol
banner: db 'CompuServe CP/M (R) Executive Version 2.3',cr,lf
if other
db '**** Tarbell/Z-80 ****',cr,lf,0
endif
if hz89
db '**** Heath/Zenith ****',cr,lf,0
endif
if pmmi
db '**** PMMI Modem ****',cr,lf
db '^E exit WITHOUT disconnect, ^X exit WITH disconnect'
db cr,lf,lf,'$'
endif
DB 'Copyright (C) 1980, 1981 CompuServe Incorporated',CR,LF
DB CR,LF,'$'
START0: LXI SP,STACK ; SET UP OUR OWN STACK
MVI C,FN$ID ; GET CURRENT CP/M DISK DRIVE
CALL BDOS
STA CPMDEF
LHLD BBASE+1 ; GET START OF BIOS
XCHG
LXI H,3 ; OFFSET TO CONSOLE CHECK
DAD D
SHLD TRMGET+1 ; STORE ADDRESS
LXI H,6 ; OFFSET TO CONSOLE READ
DAD D
SHLD TERMRD+1
if usebios
LXI H,9 ; OFFSET TO CONSOLE WRITE
DAD D
SHLD TERMWR+1
endif
LXI H,2AH ; OFFSET TO LIST STATUS
DAD D ; MUST BE IMPLEMENTED IN BIOS
SHLD LSTST+1 ; FOR PRINT BUFFERING TO WORK
XRA A ; DISABLE PRINTER OUTPUT
STA PRTFLG
STA SIFLAG ; NO <SI> SEEN
if shoxfr
sta shoflg ; Don't come up in "show transfer"
endif ; mode!
CALL INISIO ; INITIALIZE MODEM UART
LXI D,BANNER ; ANNOUNCE OURSELVES
MVI C,FN$PCB
CALL BDOS
; TERMINAL EMULATOR LOOP
TERM: CALL TRMGET
JZ TSTSIN
CPI MON ; IF USER WANTS TO RETURN TO CP/M
IF NOT PMMI
JZ BBASE ; THEN DO IT!
ENDIF ;NOT PMMI
IF PMMI
JZ DISCON ; DISCONNECT MODEM
ENDIF ;PMMI
CPI ENQ ; IF ^E
JZ BBASE ; THEN JUST EXIT
CPI DC2 ; IF ^R
JZ PRTON ; THEN TURN PRINTER ON
CPI DC4 ; IF ^T
JZ PRTOFF ; THEN TURN PRINTER OFF
CALL PUTSIO ; ELSE PUT CHAR OUT TO SIO
TSTSIN: CALL GETSIN ; ELSE GET A CHAR FROM SIO
JZ TSTPRT
CPI DLE ; IF <DLE>
JZ ISDLE ; THEN PROCESS <DLE>
CPI SI ; IF <SI>
JZ ISSI ; THEN PROCESS <SI>
CPI SO ; IF <SO>
JZ ISSO ; THEN PROCESS <SO>
CPI ESC ; IF <ESC>
JZ ISESC ; THEN GO PROCESS ESCAPE SEQUENCE
CPI DC2 ; IF ^R
JZ PRTON ; THEN TURN PRINTER ON
CPI DC4 ; ELSE IF ^T
JZ PRTOFF ; THEN TURN PRINTER OFF
IF HZ19 ; IF RUNNING A H/Z-19 CONSOL
CPI FF ; THEN IF CHAR IS <FF>
JZ FFHZ19 ; THEN MAP IT TO <ESC><E>
ENDIF
NOTSIM: CALL TERDSP ; DISPLAY/PRINT CHARACTER
TSTPRT:
LSTST: CALL 0000 ; MODIFIED TO LIST STATUS
ORA A
JZ TERM ; IF PRINTER IS BUSY
; THEN CONTINUE TO SERVICE THE
; KEYBOARD & MODEM AT HIGH POLLING RATE
LHLD HEAD ; GET HEAD POINTER
XCHG
LHLD TAIL ; AND TAIL POINTER
CALL DEHLCMP ; SEE IF THEY ARE EQUAL
JZ TERM ; IF SO, BUFFER IS EMPTY, SO EXIT
PUSH H ; SAVE TAIL POINTER
MOV A,M ; GET THE CHAR
ANI 7FH ; STRIP PARITY
LXI H,LINCNT ; POINT TO LINE COUNTER
CPI FF ; IF FF
JZ NEWPAGE ; RESET LINE COUNTER
CPI LF ; IF LF
JNZ OUTP ; DECREMENT LINE COUNTER
DCR M ; IF ROOM LEFT ON PAGE
JNZ OUTP ; OUTPUT THE LF
NEWPAGE MVI M,PAGLEN ; ELSE A FF
MVI A,FF
OUTP: MOV E,A
MVI C,FN$WL ; CHAR TO LIST DEVICE
CALL BDOS
POP H ; RESTORE TAIL PTR
INX H
LDA BDOS+2 ; CHECK FOR MEMORY TOP
DCR A
CMP H
JNC OUTP1
LXI H,PRTBUF ; WRAPAROUND
OUTP1: SHLD TAIL ; UPDATE TAIL PTR
JMP TERM
DEHLCMP: ; TEST (DE)-(HL) COMPARISON
MOV A,D
CMP H
RNZ
MOV A,E
CMP L
RET
; GET A CHARACTER FROM LOCAL TERMINAL
; RETURN Z FLAG IF LOCAL NOT READY
TRMGET: CALL 0000H ; **** MODIFIED ADDRESS!!!
ANI 01
RZ ; RETURN IF NO LOCAL INPUT
TERMRD: CALL 0000H ; **** MODIFIED ADDRESS!!!
ANI 7FH ; RETURN WITH PARITY STRIPPED
RET
; HERE IF <DC2> (^R) RECEIVED FROM HOST OR CONSOLE
PRTON: MVI A,0FFH ; SET PRINTER FLAG
STA PRTFLG
JMP TERM
; HERE IF <DC4> (^T) RECEIVED FROM HOST OR CONSOLE
PRTOFF: XRA A ; CLEAR PRINTER FLAG
STA PRTFLG
JMP TERM
; HERE IF <DLE> RECEIVED FROM HOST
ISDLE: CALL GETSIX ; GET CHARACTER FOLLOWING <DLE>
CALL TERDSP ; DISPLAY IT ON CONSOLE/PRINTER
JMP TERM
ISSO: XRA A ; <SO> DISABLES PROTOCOL MODE
if shoxfr
sta shoflg
endif
LXI SP,STACK ; RESTORE STACK INCASE OF ABORT
ISSI: STA SIFLAG ; <SI> ENABLES PROTOCOL MODE
JMP TERM
; HERE ON <ESC>
ISESC: LDA SIFLAG ; IF <SI> NOT RECEIVED
ORA A
MVI A,ESC
IF NOT HZ19
JZ NOTSIM ; THEN JUST DISPLAY IT
ENDIF
IF HZ19
JZ CK1061 ; THEN CHECK FOR SPECIAL MAPPINGS
ENDIF
ISESCN: CALL GETSIX ; ELSE GET CHARACTER FROLLOWING <ESC>
CPI 'I' ; IF <ESC><I>
JNZ ESC0 ; THEN
LXI H,SYSID ; SEND THE ID STRING TO HOST
SNDID: MOV A,M ; GET NEXT ID BYTE
ORA A ; IF NULL
JZ TERM ; THEN FINISHED
CALL PUTSIO ; ELSE SEND TO HOST
INX H
JMP SNDID
IF HZ19 ; IF WE HAVE A H/Z-19 AS CONSOLE
CK1061: CALL GETSIX ; GET CHAR FOLLOWING <ESC>
CPI 'j' ; <ESC><j> = <ESC><E>
JNZ NT1061
FFHZ19: MVI A,'E'
NT1061: PUSH PSW
MVI A,ESC
CALL TERDSP
POP PSW
JMP NOTSIM
ENDIF ; H/Z-19 MAPPING
ESC0: CPI 'L' ; IF <ESC><L>
JNZ ESC1 ; THEN
MVI E,0 ; PERFORM SYSTEM LOAD FUNCTION
CALL GETCKS ; GET BYTE COUNT
MOV B,A
CALL GETCKS ; GET LOW ADDRESS BYTE
MOV L,A
CALL GETCKS
MOV H,A ; AND HIGH-ORDER
ESCL0: CALL GETCKS ; GET NEXT DATA BYTE
MOV M,A ; SAVE IT
INX H ; BUMP MEMORY ADDRESS
DCR B ; COUNT BYTES RECEIVED
JNZ ESCL0 ; & LOOOP TILL ZERO
MOV C,E ; SAVE CHECKSUM
CALL GETCKS ; GET NEXT BYTE
CMP C ; IF MATCH
MVI A,'.' ; THEN SEND .
JZ ESCL1 ; ELSE
MVI A,'/' ; SEND /
ESCL1: CALL PUTSIO
if not shoxfr
call viomrk ; display protocol mark
endif
JMP TERM
if not shoxfr
viomrk:
push psw
mvi a,cr
call viodsp
mvi a,lf
call viodsp
mvi a,32
sta xfrctr
pop psw
cpi '.'
cnz viodsp
ret
ctxfr: push h
push psw
lxi h,xfrctr ; decrement count of xfr'd characters
dcr m
jnz ctxfr0
mvi m,32
mvi a,'+' ; display '+' every 32 bytes
call viodsp
ctxfr0: pop psw
pop h
ret
endif
ESC1: CPI 'A' ; IF <ESC><A>
JNZ TERM ; THEN
; Initialize for data transmission using the CompuServe A-protocol
; The protocol begins with the following being sent from the host:
; <ESC><A><SOH><U | D><A | B><FILESPEC><ETX><CKSUM>
; where:
; U = upload, D = download
; A = ASCII (file ends in 1Ah), B = binary
; FILESPEC = standard CP/M file specification, including optional drive
; CKSUM = checksum for the record
MVI A,'0' ; INIT RECORD NUMBER
STA APNXT
CALL APRCVX ; GET COMMAND LINE FROM HOST
LXI H,APBUF+2 ; POINT TO FILE SPEC FROM USER
LXI D,TFCB ; POINT TO FILE CONTROL BLOCK
MOV B,M ; GET POSSIBLE DISK DRIVE NAME
INX H ; IF : NEXT
MOV A,M
CPI ':'
JNZ NOCOL ; THEN
INX H ; SKIP THE COLON
MVI A,7 ; MASK OFF DRIVE NUMBER
ANA B
JMP FIRSTB
NOCOL: DCX H ; POINT BACK TO FIRST FILE BYTE
XRA A ; USE DEFAULT DRIVE NUMBER
FIRSTB: STAX D ; STORE DRIVE NUMBER
INX D ; POINT TO FIRST FILE NAME BYTE
MVI B,8 ; MAX LENGTH OF NAME
CALL NAAME ; GET FILE NAME
MOV A,M ; GET NEXT BYTE
CPI '.' ; IF . PRESENT
JNZ EXT ; THEN
INX H ; SKIP OVER IT
EXT: MVI B,3 ; LENGTH OF EXTENSION
CALL NAAME ; GET EXTENSION
XRA A ; ZERO FILE EXTENT
STAX D
LDA TFCB ; SELECT THE DISK
ORA A ; IF 0 THEN USE DEFAULT DISK
JZ NODISK
SUI 1 ; MAP A INTO 0, B INTO 1, ETC.
MOV E,A
MVI D,0
MVI C,FN$SD
CALL BDOS
NODISK:
lda apbuf+1 ; store transfer type
sta xfrtyp
LDA APBUF ; CHECK DIRECTION
CPI 'D' ; IF DOWN LOAD
JNZ CHKUPL ; THEN
lxi d,dnload
mvi c,fn$pcb
call bdos
MVI C,FN$OPN ; IF THE FILE EXISTS
CALL DSKOP
CPI 0FFH
JZ DLOKAY ; THEN
LXI D,DLBOMB ; TELL THE USER ABOUT IT
MVI C,FN$PCB
CALL BDOS
MVI C,FN$RC ; GET USER'S RESPONSE
CALL BDOS
ani 7fh
PUSH PSW
MVI A,CR
CALL VIODSP
MVI A,LF
CALL VIODSP
POP PSW
CPI 'Y' ; IF NOT 'Y'
JZ DLDEL
CPI 'y'
JNZ ABORT ; THEN ABORT THE DOWNLOAD ATTEMPT
DLDEL: MVI C,FN$DEL ; ELSE DELETE THE FILE
CALL DSKOP
DLOKAY: CALL OPNOUT ; OPEN FOR OUTPUT
if shoxfr
mvi a,0ffh
sta shoflg
endif
; THE FOLLOWING LOOP DOES THE DOWNLOAD FUNCTION
DL0: CALL APRCV ; GET NEXT LINE OF DATA
JNZ DLEOT ; HANDLE END OF TRANSMISSION
LXI H,APBUF ; POINT TO BUFFER
DL1: MOV A,M ; GET NEXT BYTE
INX H ; POINT TO NEXT BYTE
CALL PUTBYT ; PUT IT INTO OUTPUT BUFFER
DCR B ; COUNT THE BYTE
JNZ DL1
JMP DL0 ; GET NEXT RECORD FROM HOST
; HERE WHEN THE HOST'S <EOT> MESSAGE HAS BEEN RECEIVED
DLEOT:
lda xfrtyp ; if binary transfer
cpi 'B'
jz dleotb ; then don't insert ^Z
MVI A,EOF ; PUT ^Z (END OF FILE MARK)
CALL PUTBYT
dleotb: MVI C,FN$WDR
CALL DSKOP
DLEOT0: MVI C,FN$CLS
CALL DSKOP
CALL RSTDEF
MVI A,'.' ; TELL HOST WE GOT IT
CALL PUTSIO
if not shoxfr
call viomrk
endif
if shoxfr
xra a
sta shoflg
endif
JMP TERM ; BACK TO TERMINAL MODE
; HERE IF NOT A DOWN LOAD - BETTER BE UP LOAD!
CHKUPL: CPI 'U'
JNZ ABORT ; SEND NAK TO HOST IF NOT .
lxi d,upload
mvi c,fn$pcb
call bdos
CALL OPNINP ; OPEN THE FILE FOR INPUT
MVI A,'.' ; TELL HOST WE'RE READY TO SEND DATA
CALL PUTSIO
if not shoxfr
call viomrk
endif
; THE UPLOAD FUNCTION IS DONE IN THE FOLLOWING LOOP:
CALL GETSIX ; GET HOST'S PROMPT
CPI '.' ; ABORT IF NOT '.'
JNZ ABORT
if shoxfr
mvi a,0ffh
sta shoflg
endif
UPL1: MVI B,0 ; INIT COUNT
LXI H,APBUF
UPL2: CALL GETBYT ; GET DATA FROM FILE
jp upl3 ; jump if <EOF> occured
MOV M,A ; THEN PUT INTO BUFFER
INX H ; BUMP POINTER
INR B ; AND BYTE COUNT
JNZ UPL2 ; GET NEXT BYTE IF BUFFER NOT FILLED
UPL4: MOV A,B ; SAVE COUNT
STA APLEN
CALL APSND ; SEND THE DATA
JMP UPL1 ; GO DO NEXT LINE
UPL3: MOV A,B ; WRITE FINAL DATA BLOCK IF THERE IS ONE
STA APLEN
ORA A
CNZ APSND
MVI A,0FFH ; SEND <EOT> MESSAGE WITHOUT MASKING
LXI H,EOTMSG
CALL APSND0
CALL RSTDEF ; RESTORE CP/M'S DEFAULT DISK DRIVE
if shoxfr
xra a
sta shoflg
endif
JMP TERM ; RETURN TO TERMINAL MODE
EOTMSG: DB 1,EOT
;***
; ROUTINE TO INTERFACE TO CP/M'S CONSOLE OUTPUT DRIVER
viodsp: push b ; save register
push d
push h
push psw
if usebios
mov c,a
TERMWR: CALL 0000H ; ***** MODIFIED TO CONOT IN BIOS
endif
if not usebios
mov e,a ; call BDOS to write char on console
mvi c,fn$wc
call bdos
endif
pop psw
pop h
pop d
pop b
RET
; ROUTINE TO DISPLAY C(A) ON CP/M CONSOLE AND PRINTER IF NECESSARY
TERDSP: PUSH PSW ; SAVE C(A)
CALL VIODSP ; DISPLAY ON CONSOLE
POP PSW ; GET CHARACTER BACK
MOV E,A ; SAVE CHARACTER
LDA PRTFLG ; IF ^R RECEIVED
ORA A
RZ ; THEN
; STORE THE CHAR IN THE PRINTER BUFFER
LHLD HEAD ; GET BUFFER PTR
MOV M,E ; STORE THE CHAR
INX H ; BUMP PTR
LDA BDOS+2 ; CHECK FOR TOP OF MEMORY
DCR A
CMP H
JNZ TERDS1 ; IF REACHED, WRAPAROUND
LXI H,PRTBUF
TERDS1: SHLD HEAD ; UPDATE PTR
RET
; ROUTINE TO OPEN A FILE FOR OUTPUT
OPNOUT: MVI C,FN$CRE ; CREATE FILE
CALL DSKOP ; CALL CP/M
CPI 0FFH ; IF OKAY
JZ ERRCRE ; ERROR DURING CREATE (DIRECTOR FULL?)
XRA A ; CLEAR NEXT RECORD COUNT
STA TFCB+FCB$NR
STA IBP ; INIT BUFFER POINTER
RET
; ROUTINE TO OPEN FILE FOR INPUT
OPNINP: MVI C,FN$OPN
CALL DSKOP
CPI 0FFH ; IF FILE NOT FOUND
JZ ERROPN ; THEN ERROR MESSAGE TIME!
XRA A
STA TFCB+FCB$NR ; INIT TO FIRST RECORD
MVI A,80H ; "EMPTY BUFFER"
STA IBP
RET
; ROUTINE TO PUT C(A) INTO DISK BUFFER
PUTBYT: PUSH B ; SAVE REGS
PUSH D
PUSH H
PUSH PSW ; SAVE BYTE
LDA IBP ; IF BUFFER IS FULL
CPI 80H
JNZ PUT0 ; THEN
MVI C,FN$WDR
CALL DSKOP
ORA A
JNZ ERRWDR ; WRITE ERROR???
XRA A ; INIT IBP TO 0
PUT0: MOV E,A ; SAVE CUR BYTE POSITION
MVI D,0
INR A ; BUMP POINTER
STA IBP
LXI H,TBUFF ; POINT TO BUFFER
DAD D ; NOW POINT TO BYTE
POP PSW ; GET BYTE
MOV M,A ; STORE BYTE
POP H ; RESTORE REGS
POP D
POP B
RET
; ROUTINE TO GET NEXT BYTE FROM A DISK RECORD
GETBYT: PUSH B ; SAVE REGS
PUSH D
PUSH H
mvi b,0 ; assume not <EOF>
LDA IBP ; IF BUFFER IS EMPTY
CPI 80H
JNZ GET0
MVI C,FN$RDR
CALL DSKOP
mov b,a ; save return code (0 implies okay)
XRA A ; RESET BYTE POINTER
GET0: MOV E,A ; SAVE BYTE POS
MVI D,0
INR A ; BUMP BYTE POS
STA IBP
LXI H,TBUFF
DAD D
lda xfrtyp ; if binary transfer
cpi 'B'
jz gtbtbn ; then don't check for ^Z
MOV A,M ; GET THE BYTE
cpi eof ; if ^Z
jnz gtrstr ; then
mvi b,1 ; we will exit with N cleared
gtrstr: dcr b ; set N if NOT eof
POP H ; RESTORE REGS
POP D
POP B
RET
gtbtbn: mov a,m ; get binary byte
jmp gtrstr ; set N flag and exit
; FATAL CP/M ERROR CONDITIONS PRINT A LOCAL MESSAGE
; THEN SEND A <NAK> TO HOST
ERRCRE: LXI D,CREMSG
JMP DFATAL
ERROPN: LXI D,OPNMSG
JMP DFATAL
ERRWDR: LXI D,WDRMSG
JMP DFATAL
DFATAL:
FATAL: MVI C,FN$PCB ; WRITE ERROR MESSAGE
CALL BDOS
ABORT:
lxi d,abload ; tell user we are aborting
mvi c,fn$pcb
call bdos
MVI C,FN$CLS
CALL DSKOP
CALL RSTDEF ; RESTORE DEFAULD DISK
MVI A,KNAK
CALL PUTSIO ; TELL HOST WE HAVE BOMBED
JMP ISSO ; DISABLE PROTOCOL MODE
; HERE TO DO A CP/M DISK OPERATION; CALLED WITH DESIRED FUNCTION CODE IN C
DSKOP: LXI D,TFCB
CALL BDOS
PUSH PSW ; SAVE RETURN CODE
XRA A ; OUTPUT A NULL TO CONSOLE
CALL VIODSP ; TO FLUSH DISK BUFFER
POP PSW ; RESTORE DSK RETURN CODE
RET
; ROUTINE TO RESTORE CP/M'S DEFAULT DISK DRIVE
RSTDEF: LDA CPMDEF
MOV E,A
MVI D,0
MVI C,FN$SD
CALL BDOS
RET
; ROUTINE TO EXTRACT FILE NAME AND EXTENSION
NAAME: MOV A,M ; GET NEXT BYTE
CPI CR ; <RET> ENDS NAME
JZ FILL ; FILL IF END OF STRING
CPI '.' ; IF EXTENSION
JZ FILL ; THEN FILL OUT WITH SPACES
INX H ; SKIP THIS BYTE
CPI 60H ; LOWER CASE A
JC NAME1 ; JUMP IF NOT LOWER CASE
SBI 20H ; CONVERT LOWER CASE TO UPPER
NAME1: STAX D ; STORE BYTE IN FCB
INX D
DCR B ; COUNT THIS BYTE
JNZ NAAME ; PROCESS NEXT IF MORET
RET
FILL: MVI A,' ' ; STORE A SPACE
JMP NAME1
; THIS ROUTINE RECEIVES A RECORD USING THE ASCII PROTOCOL
APRCV: MVI A,'.' ; PROMPT REMOTE FOR NEXT RECORD
CALL PUTSIO
if not shoxfr
call viomrk
endif
APRCVX: LDA APNXT ; BUMP EXPECTED RECORD NUMBER
INR A
CPI '9'+1 ; WRAP-AROUND
JC APRCVY ; JUMP IF LEQ 9
MVI A,'0'
APRCVY: STA APNXT
if not shoxfr
call viodsp
endif
APRCV0: CALL TRMGET ; GET LOCAL KEYBOARD INPUT
CPI ETX ; IF ^C
JZ ABORT ; THEN ABORT THE TRANSFER
CALL GETSIX ; GET NEXT CHARACTER
CPI SOH ; <SOH> STARTS THE RECORD
JZ APRCV1
CPI ETX ; <ETX> BY ITSELF IS QUESTIONABLE
JNZ APRCV0
MVI A,'/' ; SEND A LOGICAL NAK
CALL PUTSIO
if not shoxfr
CALL VIODSP
endif
JMP APRCV0
APRCV1: MVI E,0 ; INIT CHECKSUM
MOV B,E ; INIT BYTE COUNT
MOV A,E ; CLEAR <EOT> FLAG
STA APEOT
LXI H,APBUF
CALL GETCKS ; GET SENDER'S RECORD NUMBER
STA APCUR
APRCV2: CALL GETCKS ; GET A CHECKSUMMED CHARACTER
JZ APRCV3
MOV M,A ; PUT BYTE IN BUFFER
INR B ; COUNT THIS BYTE
INX H
if not shoxfr
call ctxfr ; display '+' every 32 bytes
endif
JMP APRCV2
APRCV3: MOV C,E ; SAVE CHECKSUM
CALL GETCKS ; GET REMOTE'S CHECKSUM
CMP C ; IF SAME
JNZ APRCV4 ; THEN
LDA APNXT ; CHECK RECORD COUNT
MOV C,A
LDA APCUR
CMP C
JNZ APRCV8 ; JUMP IF NOT MATCHED
MOV A,B ; STORE BYTE COUNT
STA APLEN
LDA APEOT ; RETURN WITH EOT FLAG STATUS
ORA A
RET
APRCV4: MVI A,'/' ; ELSE REQUEST RETRANSMISSION
CALL PUTSIO
if not shoxfr
CALL VIODSP
endif
JMP APRCV0
APRCV8: JNC ABORT ; ABORT IF RCV GTR EXPECTED
MVI A,'.' ; MUST HAVE RECEIVED A DUPLICATE RECORD
CALL PUTSIO ; ACCEPT IT, AND TRY AGAIN
if not shoxfr
call viomrk
endif
JMP APRCV0
; ROUTINE TO SEND A MESSAGE
APSND: XRA A ; Flag for masking control characters
LXI H,APLEN ; BUFFER ADDRESS: LENGTH FOLLOWED BY DTA
APSND0: STA APFLG ; STORE MASK FLAG
SHLD APADDR ; STORE BUFFER ADDRESS
LDA APNXT ; BUMP NEXT RECORD COUNT
INR A
CPI '9'+1
JC ASND0A
MVI A,'0'
ASND0A: STA APNXT
if not shoxfr
call viodsp
endif
APSND1: MVI E,0 ; CLEAR CHECKSUM
LHLD APADDR
MOV B,M ; GET LENGTH
INX H ; POINT TO DATA
MVI A,SOH ; START THE MESSAGE
CALL APPUTS
LDA APNXT ; SEND RECORD NUMBER
CALL DOCKS ; UPDATE CHECKSUM
CALL APPUTS
APSND2: MOV A,M ; GET NEXT DATA BYTE
CALL DOCKS ;UPDATE CHECKSUM
CPI 20H ; IF CONTROL CHARACTER
JNC ASND2A ; THEN
LDA APFLG ; IF MASKING CONTROL CHARACTERS
ORA A
MOV A,M ; GET BYTE AGAIN
JNZ ASND2A ; THEN
CPI 05H ; FOR EFFICIENCY, ONLY MASK THE BADDIES
JC ASND2B ; MASK 00H 01H 02H 03H 04H
; NUL SOH STX ETX EOT
CPI dle
JZ ASND2B ; 10H DLE
CPI knak
JNZ ASND2A ; 15H NAK
ASND2B: MVI A,DLE ; SEND <DLE><DATA+40H)
CALL APPUTS
MOV A,M
ORI 40H
ASND2A: CALL APPUTS ; TRANSMIT THE CHARACTER
INX H
if not shoxfr
call ctxfr ; display '+' every 32 characters
endif
DCR B
JNZ APSND2 ; BACK FOR MORE IF ANY
APSND3: MVI A,ETX ; TERMINATE THE TEXT PORTION
CALL APPUTS
MOV A,E ; GET CHECKSUM
CPI 20H ; IF < 20H
JNC ASND3A ; THEN
MVI A,DLE ; SEND IT MASKED
CALL APPUTS
MOV A,E
ORI 40H
ASND3A: CALL APPUTS
ASND4A: MVI C,30 ; ABOUT 4 SECONDS
ASND4C: LXI D,2500H ;
ASND4: CALL GETSIN ; GET HOST'S REPLY
JNZ SND4B
CALL TRMGET
CPI ETX
JZ ABORT ; ABORT THE OPERATION IF ^C TYPED
DCX D ; DECREMENT INNER LOOP COUNT
MOV A,D
ORA E
JNZ ASND4
DCR C
JNZ ASND4C
MVI A,ETX ; SEND EXTRA <ETX>
CALL APPUTS
JMP ASND4A
snd4b:
if shoxfr
call viodsp
endif
if not shoxfr
call viomrk
endif
CPI '.'
RZ ; RETURN IF HOST GOT IT OKAY
CPI '/' ; ELSE IF /
JZ APSND1 ; THEN RETRANSMIT THE MESSAGE
CPI KNAK ; ELSE IF <NAK>
JZ ISSO ; THEN ABORT
JMP ASND4 ; ELSE KEEP WAITING
APPUTS: PUSH PSW ; SAVE CHAR
CALL GETSIN ; CHECK MODEM FIRST
JZ APPUT4 ; THEN
ani 7fh
CPI KNAK ; IF WE RECEIVE A <NAK>
JZ ISSO ; THEN SHUT DOWN THE PROTOCOL
CPI DC3 ; IF X-OFF
JNZ APPUT4 ; THEN
PUSH D ; DELAY A FEW SECONDS
PUSH B
MVI B,2
APPUT0: LXI D,8000H
APPUT1: CALL GETSIN ; IF CHAR PRESENT
JZ APPUT2 ; THEN
ani 7fh
CPI DC1 ; IF ^Q (XON)
JZ APPUT3 ; THEN EXIT
APPUT2: DCX D
MOV A,D
ORA E
JNZ APPUT1
DCR B
JNZ APPUT0
APPUT3: POP B ; RESTORE REGS AND RETURN
POP D
APPUT4: POP PSW ; GET CHAR
CALL PUTSIO ; SEND CHAR
RET
; ROUTINE TO GET A CHARACTER FROM UART WITH WAIT
GETSIN: CALL GETSIO ; RETURN SIO CHAR WITH BIT 7 = 0
RZ
ANI 7FH
RET
GETSIX: CALL GETSIO ; GET SIO CHAR OR WAIT
JZ GETSIX ; WAIT FOR A CHARACTER
CPI KNAK ; IF <NAK> RECEIVED
JZ ISSO ; THEN REVERT TO TERMINAL MODE
RET ; RETURN
GETCKS: CALL GETSIX ; GET NEXT SIO CHAR WITH CHECKSUMMING
CPI ETX ; IF <ETX>
RZ ; THEN RETURN
PUSH PSW
CPI EOT
JNZ NOTEOT ; THEN
STA APEOT ; SET <EOT> SEEN FLAG
NOTEOT: CPI DLE ; IF <DLE>
JNZ GETCK0 ; THEN
CALL GETSIX ; GET NEXT CHARACTER
ANI 1FH ; MAKE CONTROL CHAR OF IT
GETCK0: CALL DOCKS ; UPDATE CHECKSUM
POP PSW ; RESTORE FLAGS
MOV A,D ; RESTORE NEW CHAR
RET ; RETURN
DOCKS: MOV D,A ; SAVE BYTE
MOV A,E ; GET OLD CHECKSUM
RLC ; ROTATE ONE BIT LEFT
ADD D ; ADD NEW BYTE
ACI 0 ; ADD POSSIBLE CARRY
MOV E,A ; REPLACE CHECKSUM WITH UPDATED ONE
MOV A,D ; RESTORE NEW BYTE
RET
; VARIOUS MESSAGE STRINGS
proini: db cr,lf,'% CSEXEC - Initializing file transfer',cr,lf,'$'
dnload: db cr,lf,'% CSEXEC - Beginning Download',cr,lf,'$'
upload: db cr,lf,'% CSEXEC - Beginning Upload',cr,lf,'$'
abload: db cr,lf,'? CSEXEC - Aborting file transfer',cr,lf,'$'
DLBOMB: DB CR,LF,'% CSEXEC - That file already exists on your disk.',CR,LF
DB 'Do you wish to replace it (Y or N) ? $'
CREMSG: DB cr,lf,'? CSEXEC - Diskette is full!',CR,LF,'$'
opnmsg: db Cr,lf,'? CSEXEC - That file is not on your diskette!',cr,lf,'$'
wdrmsg: db cr,lf,'? CSEXEC - Your diskette is full!',cr,lf,'$'
dmsg: db cr,lf,'++DISCONNECTED++',cr,lf,'$'
; I/O SUBROUTINES FOR SIO
SIOGET: IN CTL ; GET MIO STATUS FLAGS
ANI SIOIR ; ISOLATE INPUT READY FLAG
if not hitrue
XRI SIOIR ; INVERT IT
endif
RZ ; RETURN IF NOW 0
IN SIO ; ELSE GET SIO CHARACTER
if shoxfr
call prodsp
endif
RET ; AND RETURN (Z FLAG = 0)
SIOPUT: PUSH PSW ; WRITE (A) TO SIO
PUTSI1: IN CTL ; WAIT FOR FLAG TO = 0
ANI SIOTR
if not hitrue
JNZ PUTSI1
endif
if hitrue
jz putsi1
endif
POP PSW
OUT SIO
if shoxfr
if hz19
push psw
mvi a,esc ; invert video for incoming characters
call viodsp
mvi a,'p'
call viodsp
pop psw
push psw
call prodsp
mvi a,esc
call viodsp
mvi a,'q' ; return to normal video
call viodsp
pop psw
endif
if not hz19
call prodsp
endif
endif
RET
sioini:
if hz89
mvi a,3 ; init uart to
out sio+3 ; 8 data bits, 1 stop bit, no parity
endif
if pmmi
mvi a,93 ; 8 data bits, 1 stop bit, no parity
out basprt
mvi a,52 ; 300 baud
out basprt+2
mvi a,127 ; originate mode
out basprt+3
endif
ret
if shoxfr
prodsp: push psw ; save the character
lda shoflg ; if in protocol
ora a
jz proter ; then
pop psw
push psw
ani 7fh ; remove high-order bit
cpi ' ' ; if this is a control char
jnc proyes ; then
lda xfrtyp ; if doing a binary transfer
cpi 'B'
jz pronot ; then "flag" all control characters
pop psw ; else flag only funny ones
push psw
cpi sholcc ; is it a normal control character?
jc pronot ; (ie, <HT> thru <RET>, 08h - 0Dh)
cpi shohcc+1
jc proyes
pronot: mvi a,'^' ; flag the control character
call viodsp
pop psw
push psw
adi 40h ; map char to letter
proyes: call viodsp
proter: pop psw
ret
endif
if pmmi ; routine to disconnect pmmi modem
discon: mvi a,03fh
out basprt+3
xra a
out basprt
out basprt+2
lxi d,dmsg ; print disconnect msg
mvi c,fn$pcb
call bdos
jmp bbase ; and exit
endif
; RAM STORAGE AREA
if shoxfr
shoflg: ds 1 ; 1 if in file transfer protocol
endif
if not shoxfr
xfrctr: ds 1 ; counter for displaying +'s
endif
CPMSTK: ds 2 ; SAVES CP/M'S STACK POINTER
CPMDEF: ds 1 ; SAVES CP/M'S DEFAULT DISK DRIVE
PRTFLG: ds 1 ; FF IF PRINTER ENABLED, 00 OTHERWISE
SIFLAG: ds 1 ; NON-ZERO IMPLIES <SI> RECEIVED AND PROTOCOL ACTIVE
APEOT: ds 1 ; NON ZERO IF <EOT> SEEN IN GETCKS
APFLG: ds 1 ; 00 IF MASKING CONTROL CHARACTERS, FF IF NOT
xfrtyp: ds 1 ; 'A' if ASCII, 'B' if binary
APADDR: ds 2 ; POINTER TO BUFFER
APLEN: DB 0 ; LENGTH OF RECORD AS RECEIVED
APBUF: DS 256 ; STORAGE FOR THE RECORD
IBP: DS 1 ; BYTE POINTER
APNXT: DS 1 ; EXPECTED RECORD NUMBER
APCUR: DS 1 ; CURRENT (RECEIVED) RECORD NUMBER
DS 256 ; STACK GOES HERE
STACK:
HEAD: DW PRTBUF
TAIL: DW PRTBUF
LINCNT: DB PAGLEN
PRTBUF EQU $
END START