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
/
UPLOAD12.MAC
< prev
next >
Wrap
Text File
|
2000-06-30
|
24KB
|
732 lines
;****************************************************************************
; FILE UPLOAD UTILITY FOR CIS A PROTOCOL.
; WRITTEN 3/17/82 BY BOB RICHARDSON
; COPYRIGHT (C) 1982 PERFORMANCE BUSINESS MACHINES
; program distributed by permission- further distribution must contain this
; notice, the copyright notice and the authors name
;
; INVOKED BY "UPLOAD FNAME.FTP" AND USES DEFAULT FCB AND COMMAND LINE
; *************************************************************************
.z80
; equates
soh equ 01h ; start of header
etx equ 03h ; end of text
eot equ 04h ; end of transmission
enq equ 05h ; enq char - not used
si equ 0fh ; shift in - starts protocol on terminal
so equ 0eh ; shift out - ends protocol
;
knak equ 15h ; nak
dle equ 10h ; data link escape - used to mask chars for transparency
esc equ 1bh ; escape
eof equ 1ah ; ctl-z
ctlz equ 1ah ; also
cr equ 0dh ; carriage return
lf equ 0ah ; line feed
tof equ 0ch ; top of form
;
cldboot equ 00h ; bios coldboot vector
iobyte equ 0003h ; addr of iobyte
deffcb equ 05ch ; addr of default fcb
command equ 080h ; addr of command line
bdos equ 05h ; addr of bdos jmp
; BDOS FUNCTIONS
prnstg equ 09h ; print string delimited by $
rdcbuf equ 0ah ; read console buffer function
fn$opn equ 0fh ; open disk file
fn$cls equ 010h ; close disk file
fn$del equ 013h ; delete disk file
fn$rds equ 014h ; read sequential
fn$wts equ 015h ; write sequential
fn$mak equ 016h ; make file
fn$ren equ 017h ; rename file
fn$std equ 01ah ; set dma function
;
; BIOS OFFSETS FOR VARIOUS CALLS
const equ 03h ; constat call
conin equ 06h ; conin
conout equ 09h ; character out to console
list equ 0ch ; character to line printer
punch equ 0fh ; char to punch device
rdr equ 12h ; get char from reader device
reader equ 12h ; alternate spelling
; FCB OFFSETS
current equ 32 ; offset to current record number
ftype equ 09 ; and offset to type
; Version info
vers equ '1' ; ascii version
rev equ '2' ; and rev level
; History info
; 3/20/1982 FIRST COMPLETE VERSION RELEASED
; BY THE AUTHOR BOB RICHARDSON OF MICROPRO INTL
; CORPORATION - FURTHER DISTRIBUTION MUST CONTAIN
; THIS COMMENT - this file made available courtesy
; of MicroPro International Corp. and the author
;
;**************************************************************************
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; code begins:
; MAIN DRIVER LOOP FOR THE UPLOAD PROTOCOL
;
upld:
ld sp,upld ; the Charlie Strom memorial local stack
call announce ; copyrite and vers, rev level
call dskinit ; initialize disk buffer
call procol ; turn on protocol, open file, and start
upldrt:
call sndhdr ; then send the header for file xfer
call waitack ; and wait for ack response
jp c,upldrt ; retry if nak response
jp nz,comfail ; error so dump job
call sendack ; else prompt for first record
uplp:
call getrec ; get terminals record
jp c,uplp ; wait for resend if nak
ld a,(seeneot) ; get eotflag
cp 00h ; and test for completion
jp nz,fin ; eof - recd eot record
upl1:
call putrec ; write rec(s) to disk
jp c,dspacen ; no space on host disk - send fail message
call z,sendack
jp uplp ; loop till eof
;
fin:
call sendack ; ack eot message
call complete ; turn off protocol and send all done message
call fclose ; dump buffer tailings if any
jp cldboot ; terminate
;************************************************************************
; end of driver beginning of subroutines
biosvct:
ld hl,(cldboot+1) ;get start of bios table
add hl,de ; get addr for branch
jp (hl) ; return handled to inline location
;************************************************************************
; Get rev and version and copyright notice to operator
announce:
ld de,cpyrite ; copyright notice
call prnmes ; to console
ret ; to caller
;
cpyrite:
defb cr,lf,'Upload Vers. ',vers,'.',rev,cr,lf
defb ' Copyright (C) 1982 PBM Division MicroPro International Corporation ','$'
;
; **************************************************************************
; Kudos to Russ Renshaw for inventing this protocol
; and special thanks to charlie, tom, and dave - sysops of the CIS CP-MIG
; without whose help none of this code would be here
; ***************************************************************************
; INITIALIZE THE PROTOCOL AND OPEN FILES
procol:
ld de,deffcb ; get default fcb
ld c,fn$opn ; open file function
call bdos ; see if we can open file
cp 04h ; test for successful open
jp c,isfil ; send file exists message if file there
ld a,(command) ; get count of oper supplied chars
or a ; and insure non zero value
jp z,nospec ; complain if not right
ld hl,deffcb+ftype ; addr of file type
push hl ; and save for next use
ld de,typsav ; save area
ld bc,03h ; length of file type
ldir ; move to save area - operator supplied file type
pop de ; here is the next use of filetype addr in fcb
ld hl,dollar ; $$$ for temporary file type
ld bc,03h ; length of file type
ldir ; move it in
; the above added for pip compatibility
ld a,0 ; get zero
ld (masking),a ; and start masking ctl chars in msg text
call rmtnm ; prompt operator for name at his end
ld a,(conbuff+1) ; start of data - contains byte count
ld c,a ; is count for move
ld b,0 ; with high order=0
ld hl,conbuff+2 ; start of actual name
call noblnk ; bypass all blanks
jp z,comfail ; if this passes machine is broken - get a new
; one.
ld de,filespec ; addr in esc a message
push bc ; save the number of non blanks
ldir ; move filespec to message
pop hl ; restore count
ld a,cr ; get cr to terminate the esc a string
ld (de),a ; and move it to the esc a message buffer end
inc hl ; update count to reflect this fact
ld (tmpsav),hl ; and save for next routine
; here we create the temporary $$$ file on the disk
ld de,deffcb ; so make the file - all is well
ld c,fn$del ; first delete it just in case
push de ; save for next call
call bdos ; to pyramid building routine
pop de ; restore fcb pointer
ld c,fn$mak ; make function
call bdos ; mush
cp 04h ; test sucessful completion
jp nc,nodirsp ; else give error for no directory space
ld a,0 ; get zero
ld (deffcb+current),a ; to current record
ret ; to caller
;
tmpsav:
defw 00h ; save area for operator count from
; remote file name
; **********************************************************************
; send the esc a header to the terminal - refer to the protocol document
; for the format of this record - is essentially the same as normal
; but fields have special meanings.
sndhdr:
; and then turn on protocol in terminal
ld a,si ; get shift in char
call punout ; send it
ld a,esc ; send esc
call punout ; charge
ld a,'A' ; esc a for message
call punout ; mush ye huskies mush
ld hl,(tmpsav) ; get count from operator answer for name
push hl ; move to bc
ld hl,escames ; get message balance addr
pop bc ; restore count from command line
ld a,c ; get count in accumulator
add a,escalen ; and add in normal length
ld b,a ; get in byte counter
call prmesout ; send message as normal
xor a ; set z flag
ret ; and return
; bypass leading blanks in command line
noblnk:
ld a,(hl) ; get char
cp 20h ; test blank
ret nz ; non blank
dec c ; reduce count
ret z ; return error if exhausted
inc hl ; increment buffer pointer
jp noblnk
; file exists on host- blow off terminal as security measure
;
isfil:
ld de,isflmes ; file found message
call prnmes ; to console
jp cldboot ; and terminate abnormally
;
isflmes:
defb cr,lf,'FILE ALREADY EXISTS ON HOST- CHECK DIRECTORY$'
; nospec is issued when user omits the
; filespec in the command line
nospec:
ld de,nospecm ; file found message
call prnmes ; to console
jp cldboot ; and terminate abnormally
;
nospecm:
defb cr,lf,'I am sorry- you must specify a name for upload$'
; error - the host has no directory space
nodirsp:
ld de,nodirmes ; no directory space
call prnmes ; to console
jp cldboot ; and terminate
;
nodirmes:
defb cr,lf,'NO DIRECTORY SPACE ON HOST !!!','$'
;
; message for ESC A header - sent if all is well to start upload
escames:
defb 'U' ; upload
defb 'B' ; Binary transfer
escalen equ $-escames ; length for send routine
filespec:
defs 16h ; name of file to upload
typsav:
defs 03h ; save area for file type until sucessfull
dollar:
defb '$$$' ; temporary file type in case of io error
;
;**************************************************************************
;get name for remote computer
; a <cr> response will cause the same name to be used as on host
rmtnm:
ld de,remquery ; ask the terminal what it wants to call it
call prnmes ; to the operating system such as it is
ld de,conbuff ; get a response
call rdcon ; and then
ld hl,conbuff+2 ; convert to insure upper case
ld a,(conbuff+1) ; get char count xferred
cp 0 ; insure some characters
jp z,naminv ; else take default value
ld c,a ; get counter for blank test to
call noblnk ; further insure no error
jp z,naminv ; else use same name as on host
ld b,a ; in byte counter
; roll lower to upper case if necessary
rmtnm1:
ld a,(hl) ; pick up char
cp 061h ; test for lower case
jr c,rmtntl ; not lower if carry
cp 07bh ; still looking if less than z
jr nc,rmtntl ; so go on about business
and 05fh ; else roll
ld (hl),a ; and save
rmtntl:
inc hl ; bump character pointer
djnz rmtnm1 ; and get next character
ret ; and return to caller
; use same name as host for remote file
;
naminv:
ld hl,command+1 ; use the command line input
ld de,conbuff+2 ; for the remote name
ld a,(command) ; length
ld c,a ; to counter with
ld (conbuff+1),a ; count in command line
ld b,0 ; zero high order
ldir ; move characters
ret ; to caller
;
; buffer for response to filename question
conbuff:
defb 010h ; sixteen bytes max I'll allow
defb 00h ; initial count
defs 16 ; and blank buffer
;
remquery:
defb cr,lf,' I need the file name on your computer',cr,lf,'->','$'
;
;
;***************************************************************************
; TRANSMIT ACK OR NAK TO TERMINAL
sendack:
ld a,'.' ; get ack character
jp acknak ; branch to common code
;
sendnak:
ld a,'/' ; nak char
acknak:
call punout ; send it
scf ; insure carry reset for logic flow in mn loop
ccf ; could have used or a , i know - good document
ret ; but thats a subject for another time
;*****************************************************************************
; send a record using the CIS-A protocol
; used primarily for the esc a header in this program
;
prmesout:
push bc ; save byte count
push hl ; save buffer pointer
xor a ; get zero
ld (chksum),a ; and init checksum
ld a,soh ; get start of header char
call punout ; and send it
ld a,(currec) ; get current record
call sumupd ; and update checksum
call punout ; and send it
pop hl ; restore buffer addr
pop bc ; restore count to b
;
pmeslp:
push hl ; save pointer
push bc ; and char count
ld a,(hl) ; get char
call sumupd ; update checksum
call tstmsk ; test if masking necessary
call punout ; send char
pop bc ; restore count
pop hl ; get buffer pointer
inc hl ; increment it
djnz pmeslp ; and loop until all done
;
ld a,etx ; get etx char
call punout ; send it
ld a,(chksum) ; get check sum
cp 020h ; test for < ascii space
jp nc,pmesl1 ; if = or greater, do not mask
or 040h ; else add to supply transparency
push af ; save checksum
ld a,dle ; send dle
call punout ; to remote
pop af ; restore char
pmesl1:
call punout ; send it
ret ; and return
;*************************************************************************
; Test here for masking of control chars, handle if necessary
; control chars are masked to prevent confusion between innocent bit combos
; and protocol control chars
tstmsk:
push af ; save char
ld a,(masking) ; get switch value
cp 00h ; test for on status
jp nz,tstmsr ; if off return immediate
pop af ; restore original char
push af
cp 05h ; test if one of the offending chars
jp c,tstms1 ; mask if so
cp dle ; or if equal the dle
jp z,tstms1 ; go masked
cp knak ; or if = to
jp z,tstms1 ; the fatal nak mask it
; common return
tstmsr:
pop af
ret ; common return if no masking necessary
; masking needed - so mask it
tstms1:
ld a,dle ; send dle char first
call punout ; and send it
pop af ; followed by char+40
or 040h ; to insure transparecy
ret
;
masking:
defb 00h ; flag for control char masking
;
;****************************************************************************
; update the checksum
; called whenever we need checksumming - uses simple checksum algorithm
sumupd:
push af ; save char
ld e,a ; and leave it in reg
ld a,(chksum) ; get old checksum
rlca ; and rotate it
add a,e ; add new byte
adc a,0 ; and possible carry
ld (chksum),a ; and save it
pop af ; restore character
ret ; and return
;****************************************************************************
; Read a record from the serial port
; using the Compuserve A protocol
getrec:
xor a ; init checksum
ld (chksum),a ; for use soon
call rdrin ; get a char from the rdr device
cp etx ; maybe he is just nervous
jp z,getrec ; so wait - questionable situation
cp soh ; better be an soh
jp nz,comfail ; else abort the protocol
; get the terminals record number
call rdrin ; get record number
ld (trmrno),a ; and save it for later ack/nak branch
call sumupd ; and start checksumming
; set up to fill a buffer
ld a,00h ; zero to char count
ld (charcnt),a ; for index pointer
ld (charcnt+1),a ; both halves must get cleared
ld (seeneot),a ; and reset the eot status byte
ld hl,buffer ; get address of comm buffer
; then read data until etx
getr1: ; mainloop
push hl ; save the buffer pointer
call rdrin ; and get a char
pop hl ; restore buffer pointer
cp etx ; see if its the end of record
jp z,getetx ; so go get checksum if so
cp eot ; test for eot
jp z,geteot ; and handle if recieved
getr2:
cp dle ; was it a masking char?
jr nz,getr3 ; regular unmasked character
push hl ; else get next char
call rdrin ; from terminal
pop hl ; restore buffer pointer
and 03fh ; and correct for masking
getr3:
ld (hl),a ; save in buffer
inc hl ; update pointer
call sumupd ; update checksum
ld bc,(charcnt) ; update count
inc bc ; to reflect chars in buffer
ld (charcnt),bc ; merrily counting
jp getr1 ; and go back for more
;
; here when eot is spotted
geteot:
ld (seeneot),a ; set eot recieved flag
call sumupd ; update the checksum for eot
jp getr1 ; and return to loop for etx, chksum
; recvd an etx
getetx:
call rdrin ; get term's checksum
cp dle ; see if its masked
jr nz,getet1 ; and bypass this if not
call rdrin ; get real checksum
and 01fh ; and make it a control char
; validate the transmission
getet1:
ld c,a ; and test to see
ld a,(chksum) ; that all is ok
cp c ; zero if equal
jp nz,getnak ; reject if not
ld a,(trmrno) ; get term record number
ld c,a ; and save for compare
ld a,(currec) ; get what host thinks is current
sub c ; and test for terminal high
jp c,comfail ; signal communications failure if so
ld (trmrno),a ; else save a flag for disk write routine
call updrnum ; everything looks ok - we are acking
xor a ; so clear carry flag to show all went well
ret ; and return
; error has occured in xmission
getnak:
call sendnak ; something is very wrong- send a nak
scf ; set the carry flag
ret ; and retry
; transmission control variables
trmrno:
defb 00h ; area for term. record number
seeneot:
defb 00h ; flag to indicate eot detected
charcnt:
defw 00h ; counter for chars received
;
;
;
;**************************************************************************
; Routine to write the approved characters to disk. only error is no space
; write a record to the disk a character at a time..
putrec:
ld a,(trmrno) ; get flag for record number
or a
jp nz,dputfin ; bypass put unless correct record
ld hl,buffer ; get start of comm record
ld bc,(charcnt) ; and get count of chars
ld a,b ; and test for zero error
or c ;
jp z,dputfin ; bypass putloop if so
;
dputlp:
ld a,(hl) ; get the char
push hl ; save the buffer pointer
push bc ; save the count
call ptchar ; put 1 char to disk stream
pop bc ; restore count
pop hl ; restore buffer pointer
inc hl ; update ptr
dec bc ; and update count
ld a,b ; test for zero
or c ; value in byte counter
jp nz,dputlp ; and spin till done
;
dputfin:
ld a,(dskerr) ; test for possible disk error
or a ; should be zero
ret z ; ret good if so
scf ; else set error for disk space
ret ; and return
;
; initialize the disk buffer on startup or after a write
dskinit:
ld hl,dbuff ; start addr
ld de,dbuff+1 ; for overlapping move
ld bc,buffend-dbuff-1 ; buffer length-1
ld a,ctlz ; ctlz to clear with
ld (hl),a ; save the seed
ldir ; and clear
ret ; to caller
;
; routine to put a character in the disk buffer and write if buffer is full
; writes will ONLY occur on eot or full buffer
ptchar:
ld hl,(dpointr) ; get current pointer
ld (hl),a ; and save character
inc hl ; point to next
ld (dpointr),hl ; and save it
ld de,buffend ; get limit
xor a ; clear carry
sbc hl,de ; test for end
ret nz ; return if not boundry
call ptitout ; write the record
ld hl,dbuff ; re-init pointers
ld (dpointr),hl ; for next pass
call dskinit ; re-init buffer
ret ; and return
ptitout:
ld de,dbuff ; get dma addr
ld c,fn$std ; set dmaadr function
call bdos ; to os
ld de,deffcb ; fcbaddr
ld c,fn$wts ; write sequential function
call bdos ; and its done
ld (dskerr),a ; save possible error status
ret ; and return to caller
;
; close the file and write record if non-empty
fclose:
ld hl,(dpointr) ; get pointer value
ld de,dbuff ; and init value
xor a ; clear carry
sbc hl,de ; is pointer at start of buffer??
jr z,fclos1 ; yes, bypass flush
call ptitout
; close the file and rename it to the originally specified name
fclos1:
ld de,deffcb ; for file close function
ld c,fn$cls ; the aforementioned function
call bdos ; close and go
ld de,deffcb+16 ; get next 16 for rename setup
xor a ; clear drive byte
inc de
ld (de),a ; for later
ld hl,deffcb+1 ; and point to old name
ld bc,08h ; length for move
ldir ; move in file name
ld hl,typsav ; get original file type
ld bc,03h ; and length
ldir ; and move it in too
ld c,fn$ren ; rename function change fil.$$$ to fil.ext
ld de,deffcb ; addr of fcb for renamed file
call bdos ; rename it
ret ; to caller
;
dskerr:
defb 00h
;
;*************************************************************************
; Communications have failed - reset everything and split
;
comfail:
ld a,knak ; send physical abort character
call punout ; and abort
ld de,failmes ; get comm failure message
call prnmes ; send message
ld de,deffcb ; and delete any file by that name
ld c,fn$del ; delete function
call bdos ; go out in the best way
jp cldboot ; and abort
;
failmes:
defb CR,LF,' Communications Failure - Upload aborted','$'
;**********************************************8
; Host is out of disk space
dspacen:
ld a,knak ; send physical abort character
call punout ; and abort
ld de,dspcmes ; get comm failure message
call prnmes ; send message
jp cldboot ; and abort
;
dspcmes:
defb cr,lf,' Host out of disk space - Upload aborted','$'
;
;**************************************************************************
; EOF - send a good eot message to let host know we are done
puteot:
ld a,0ffh ; turn of the switch to insure
ld (masking),a ; that eot is sent unmasked
;
ld hl,eotmes ; get addr of eot char
ld b,1 ; setup
call prmesout ; and send it
ret
complete:
ld a,so ; turn off protocol mode at terminal
call punout ; now
ld de,ucommes ; get upload complete
call prnmes ; send it
;
ret
ucommes:
defb cr,lf,' UPLOAD COMPLETE ','$'
eotmes:
defb eot
;**********************************************************************
; Wait for an ack from the terminal
waitack:
call pcharin ; get protocol char
cp '.' ; is it ack
jp z,gotack ; then handle
cp '/' ; is it nak?
jp z,rexmit ; then retransmit
cp knak ; check for abort
jp nz,waitack ; else loop
;
ld a,01 ; set nz, clear carry
or a ; and return
ret
;
rexmit:
scf ; return carry set
ret
;
gotack:
call updrnum ; update current record number
xor a ; set zero flag and clear carry
ret
;**************************************************************************
; update current record number
updrnum:
ld a,(currec) ; get current record number
inc a ; and increment
cp '9'+1 ; test for overflow
jr c,updrok ; still valid if carry
ld a,'0' ; else change it
updrok:
ld (currec),a ; and save result
ret ; then return
;****************************************************************************
; START OF IO ROUTINES - THESE ROUTINES MAY BE MODIFIED AS REQUIRED TO SUPPORT
; THE USERS HARDWARE ENVIRONMENT
;****************************************************************************
; send a message to terminal using print string convention - this routine
; assumes terminal is accessible as console and uses bdos
prnmes:
ld c,prnstg ; settup function number
call bdos ; call the operating system
ret ; and return
;***************************************************************************
; this routine reads a standard console buffer from the operator- again, using
; bdos
rdcon:
ld c,rdcbuf ; read console buffer function
call bdos ; to os
ret ; to caller
;***********************************************************************
; This routine uses the bios punch call to access the console port
; the routine must send the char in a to the modem without stripping parity
;
punout:
push af ; save char
ld c,a ; get char in proper register
ld de,punch ; get offset
call biosvct ; go doit
pop af ; restore char
ret
;**************************************************************************
; This routine calls the bios reader input to get an 8 bit character
; character is returned in a with parity bit INTACT!
rdrin:
ld de,reader ; get proper offset
call biosvct ; go get the char
cp knak ; see it its knak
jp z,comfail ; comm failure if so
ret
;
; *************************************************************************
; read one char from modem - parity may be stripped
pcharin:
ld de,conin ; get 1 char via bios
call biosvct ; and return
ret ; to caller
;
currec:
defb '1' ; initial record number
chksum:
defb 00h ; initial check sum
dpointr:
defw dbuff ; initial pointer value
;
;
dbuff:
ds 128 ; dma address
buffend equ $
buffer equ $
end