home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
norge.freeshell.org (192.94.73.8)
/
192.94.73.8.tar
/
192.94.73.8
/
pub
/
computers
/
cpm
/
alphatronic
/
DRIPAK.ZIP
/
CPM_3-0
/
SOURCES
/
CCP3.ASM
< prev
next >
Wrap
Assembly Source File
|
1982-12-31
|
64KB
|
2,807 lines
title 'CP/M 3 - Console Command Processor - November 1982'
; version 3.00 Nov 30 1982 - Doug Huskey
; Copyright (C) 1982
; Digital Research
; P.O. Box 579
; Pacific Grove, CA 93950
; Revised: (date/name of person modifying this source)
; ****************************************************
; ***** The following equates must be set to 100H ***
; ***** + the addresses specified in LOADER.PRN ***
; ***** ***
equ1 equ rsxstart ;does this adr match loader's?
equ2 equ fixchain ;does this adr match loader's?
equ3 equ fixchain1 ;does this adr match loader's?
equ4 equ fixchain2 ;does this adr match loader's?
equ5 equ rsx$chain ;does this adr match loader's?
equ6 equ reloc ;does this adr match loader's?
equ7 equ calcdest ;does this adr match loader's?
equ8 equ scbaddr ;does this adr match loader's?
equ9 equ banked ;does this adr match loader's?
equ10 equ rsxend ;does this adr match loader's?
equ11 equ ccporg ;does this adr match loader's?
equ12 equ ccpend ;This should be 0D80h
rsxstart equ 0100h
fixchain equ 01D0h
fixchain1 equ 01EBh
fixchain2 equ 01F0h
rsx$chain equ 0200h
reloc equ 02CAh
calcdest equ 030Fh
scbaddr equ 038Dh
banked equ 038Fh
rsxend equ 0394h
ccporg equ 041Ah
; ****************************************************
; NOTE: THE ABOVE EQUATES MUST BE CORRECTED IF NECESSARY
; AND THE JUMP TO START AT THE BEGINNING OF THE LOADER
; MUST BE SET TO THE ORIGIN ADDRESS BELOW:
org ccporg ;LOADER is at 100H to 3??H
; (BE SURE THAT THIS LEAVES ENOUGH ROOM FOR THE LOADER BIT MAP)
; Conditional Assembly toggles:
true equ 0ffffh
false equ 0h
newdir equ true
newera equ true ;confirm any ambiguous file name
dayfile equ true
prompts equ false
func152 equ true
multi equ true ;multiple command lines
;also shares code with loader (100-2??h)
;
;************************************************************************
;
; GLOBAL EQUATES
;
;************************************************************************
;
;
; CP/M BASE PAGE
;
wstart equ 0 ;warm start entry point
defdrv equ 4 ;default user & disk
bdos equ 5 ;CP/M BDOS entry point
osbase equ bdos+1 ;base of CP/M BDOS
cmdrv equ 050h ;command drive
dfcb equ 05ch ;1st default fcb
dufcb equ dfcb-1 ;1st default fcb user number
pass0 equ 051h ;1st default fcb password addr
len0 equ 053h ;1st default fcb password length
dfcb1 equ 06ch ;2nd default fcb
dufcb1 equ dfcb1-1 ;2nd default fcb user number
pass1 equ 054h ;2nd default fcb password addr
len1 equ 056h ;2nd default fcb password length
buf equ 80h ;default buffer
tpa equ 100h ;transient program area
if multi
comlen equ 100h-19h ;maximum size of multiple command
;RSX buffer with 16 byte header &
;terminating zero
else
comlen equ tpa-buf
endif
;
; BDOS FUNCTIONS
;
vers equ 31h ;BDOS vers 3.1
cinf equ 1 ;console input
coutf equ 2 ;console output
crawf equ 6 ;raw console input
pbuff equ 9 ;print buffer to console
rbuff equ 10 ;read buffer from console
cstatf equ 11 ;console status
resetf equ 13 ;disk system reset
self equ 14 ;select drive
openf equ 15 ;open file
closef equ 16 ;close file
searf equ 17 ;search first
searnf equ 18 ;search next
delf equ 19 ;delete file
readf equ 20 ;read file
makef equ 22 ;make file
renf equ 23 ;rename file
dmaf equ 26 ;set DMA address
userf equ 32 ;set/get user number
rreadf equ 33 ;read file
flushf equ 48 ;flush buffers
scbf equ 49 ;set/get SCB value
loadf equ 59 ;program load
allocf equ 98 ;reset allocation vector
trunf equ 99 ;read file
parsef equ 152 ;parse file
;
; ASCII characters
;
ctrlc: equ 'C'-40h
cr: equ 'M'-40h
lf: equ 'J'-40h
tab: equ 'I'-40h
eof: equ 'Z'-40h
;
;
; RSX MEMORY MANAGEMENT EQUATES
;
; RSX header equates
;
entry equ 06h ;RSX contain jump to start
nextadd equ 0bh ;address of next RXS in chain
prevadd equ 0ch ;address of previous RSX in chain
warmflg equ 0eh ;remove on wboot flag
endchain equ 18h ;end of RSX chain flag
;
; LOADER.RSX equates
;
module equ 100h ;module address
;
; COM file header equates
;
comsize equ tpa+1h ;size of the COM file
rsxoff equ tpa+10h ;offset of the RSX in COM file
rsxlen equ tpa+12h ;length of the RSX
;
;
; SYSTEM CONTROL BLOCK OFFSETS
;
pag$off equ 09ch
;
olog equ pag$off-0ch ; removeable media open vector
rlog equ pag$off-0ah ; removeable media login vector
bdosbase equ pag$off-004h ; real BDOS entry point
hashl equ pag$off+000h ; system variable
hash equ pag$off+001h ; hash code
bdos$version equ pag$off+005h ; BDOS version number
util$flgs equ pag$off+006h ; utility flags
dspl$flgs equ pag$off+00ah ; display flags
clp$flgs equ pag$off+00eh ; CLP flags
clp$drv equ pag$off+00fh ; submit file drive
prog$ret$code equ pag$off+010h ; program return code
multi$rsx$pg equ pag$off+012h ; multiple command buffer page
ccpdrv equ pag$off+013h ; ccp default drive
ccpusr equ pag$off+014h ; ccp default user number
ccpconbuf equ pag$off+015h ; ccp console buffer address
ccpflag1 equ pag$off+017h ; ccp flags byte 1
ccpflag2 equ pag$off+018h ; ccp flags byte 2
ccpflag3 equ pag$off+019h ; ccp flags byte 3
conwidth equ pag$off+01ah ; console width
concolumn equ pag$off+01bh ; console column position
conpage equ pag$off+01ch ; console page length (lines)
conline equ pag$off+01dh ; current console line number
conbuffer equ pag$off+01eh ; console input buffer address
conbuffl equ pag$off+020h ; console input buffer length
conin$rflg equ pag$off+022h ; console input redirection flag
conout$rflg equ pag$off+024h ; console output redirection flag
auxin$rflg equ pag$off+026h ; auxillary input redirection flag
auxout$rflg equ pag$off+028h ; auxillary output redirection flag
listout$rflg equ pag$off+02ah ; list output redirection flag
page$mode equ pag$off+02ch ; page mode flag 0=on, 0ffH=off
page$def equ pag$off+02dh ; page mode default
ctlh$act equ pag$off+02eh ; ctl-h active
rubout$act equ pag$off+02fh ; rubout active (boolean)
type$ahead equ pag$off+030h ; type ahead active
contran equ pag$off+031h ; console translation subroutine
con$mode equ pag$off+033h ; console mode (raw/cooked)
ten$buffer equ pag$off+035h ; 128 byte buffer available
; to banked BIOS
outdelim equ pag$off+037h ; output delimiter
listcp equ pag$off+038h ; list output flag (ctl-p)
q$flag equ pag$off+039h ; queue flag for type ahead
scbad equ pag$off+03ah ; system control block address
dmaad equ pag$off+03ch ; dma address
seldsk equ pag$off+03eh ; current disk
info equ pag$off+03fh ; BDOS variable "info"
resel equ pag$off+041h ; disk reselect flag
relog equ pag$off+042h ; relog flag
fx equ pag$off+043h ; function number
usrcode equ pag$off+044h ; current user number
dcnt equ pag$off+045h ; directory record number
searcha equ pag$off+047h ; fcb address for searchn function
searchl equ pag$off+049h ; scan length for search functions
multcnt equ pag$off+04ah ; multi-sector I/O count
errormode equ pag$off+04bh ; BDOS error mode
drv0 equ pag$off+04ch ; search chain - 1st drive
drv1 equ pag$off+04dh ; search chain - 2nd drive
drv2 equ pag$off+04eh ; search chain - 3rd drive
drv3 equ pag$off+04fh ; search chain - 4th drive
tempdrv equ pag$off+050h ; temporary file drive
patch$flag equ pag$off+051h ; patch flags
date equ pag$off+058h ; date stamp
com$base equ pag$off+05dh ; common memory base address
error equ pag$off+05fh ; error jump...all BDOS errors
top$tpa equ pag$off+062h ; top of user TPA (address at 6,7)
;
; CCP FLAG 1 BIT MASKS
; (used with getflg, setflg and resetflg routines)
;
chainflg equ 080h ; program chain (funct 49)
not$chainflg equ 03fh ; mask to reset chain flags
chainenv equ 040h ; preserve usr/drv for chained prog
comredirect equ 0b320h ; command line redirection active
menu equ 0b310h ; execute ccp.ovl for menu systems
echo equ 0b308h ; echo commands in batch mode
userparse equ 0b304h ; parse user numbers in commands
subfile equ 0b301h ; $$$.SUB file found or active
subfilemask equ subfile-0b300h
rsx$only$set equ 02h ; RSX only load (null COM file)
rsx$only$clr equ 0FDh ; reset RSX only flag
;
; CCP FLAG 2 BIT MASKS
; (used with getflg, setflg and resetflg routines)
;
ccp10 equ 0b4a0h ; CCP function 10 call (2 bits)
ccpsub equ 0b420h ; CCP present (for SUBMIT, PUT, GET)
ccpbdos equ 0b480h ; CCP present (for BDOS buffer save)
dskreset equ 20h ; CCP does disk reset on ^C from prompt
submit equ 0b440h ; input redirection active
submitflg equ 40h ; input redirection flag value
order equ 0b418h ; command order
; 0 - COM only
; 1 - COM,SUB
; 2 - SUB,COM
; 3 - reserved
datetime equ 0b404h ; display date & time of load
display equ 0b403h ; display filename & user/drive
filename equ 02h ; display filename loaded
location equ 01h ; display user & drive loaded from
;
; CCP FLAG 3 BIT MASKS
; (used with getflg, setflg and resetflg routines)
;
rsxload equ 1h ; load RSX, don't fix chain
coldboot equ 2h ; try to exec profile.sub
;
; CONMODE BIT MASKS
;
ctlc$stat equ 0cf01h ;conmode CTL-C status
;
;
;************************************************************************
;
; Console Command Processor - Main Program
;
;************************************************************************
;
;
;
start:
;
lxi sp,stack
lxi h,ccpret ;push CCPRET on stack, in case of
push h ; profile error we will go there
lxi d,scbadd
mvi c,scbf
call bdos
shld scbaddr ;save SCB address
mvi l,com$base+1
mov a,m ;high byte of commonbase
sta banked ;save in loader
mvi l,bdosbase+1 ;HL addresses real BDOS page
mov a,m ;BDOS base in H
sta realdos ;save it for use in XCOM routine
;
lda osbase+1 ;is the LOADER in memory?
sub m ;compare link at 6 with real BDOS
jnz reset$alloc ;skip move if loader already present
;
;
movldr:
lxi b,rsxend-rsxstart ;length of loader RSX
call calcdest ;calculate destination and (bias+200h)
mov h,e ;set to zero
mov l,e
; lxi h,module-100h ;base of loader RSX (less 100h)
call reloc ;relocate loader
lhld osbase ;HL = BDOS entry, DE = LOADER base
mov l,e ;set L=0
mvi c,6
call move ;move the serial number down
mvi e,nextadd
call fixchain1
;
;
reset$alloc:
mvi c,allocf
call bdos
;
;
;
;************************************************************************
;
; INITIALIZE SYSTEM CONTROL BLOCK
;
;************************************************************************
;
;
scbinit:
;
; # dir columns, page size & function 9 delimiter
;
mvi b,conwidth
call getbyte
inr a ;get console width (rel 1)
rrc
rrc
rrc
rrc
ani 0fh ;divide by 16
lxi d,dircols
stax d ;dircols = conwidth/16
mvi l,conpage
mov a,m
dcr a ;subtract 1 for space before prompt
inx d
stax d ;pgsize = conpage
xra a
inx d
stax d ;line=0
mvi a,'$'
inx d
stax d ;pgmode = nopage (>0)
mvi l,outdelim
mov m,a ;set function 9 delimiter
;
; multisector count, error mode, console mode
; & BDOS version no.
;
mvi l,multcnt
mvi m,1 ;set multisector I/O count = 1
inx h ;.errormode
xra a
mov m,a ;set return error mode = 0
mvi l,con$mode
mvi m,1 ;set ^C status mode
inx h
mov m,a ;zero 2nd conmode byte
mvi l,bdos$version
mvi m,vers ;set BDOS version no.
;
; disk reset check
;
mvi l,ccpflag2
mov a,m
ani dskreset ;^C at CCP prompt?
mvi c,resetf
push h
cnz bdos ;perform disk reset if so
pop h
;
; remove temporary RSXs (those with remove flag on)
;
rsxck:
mvi l,ccpflag1 ;check CCP flag for RSX only load
mov a,m
ani rsx$only$set ;bit = 1 if only RSX has been loaded
push h
cz rsx$chain ;don't fix-up RSX chain if so
pop h
mov a,m
ani rsx$only$clr ;clear RSX only loader flag
mov m,a ;replace it
;
; chaining environment
;
ani chain$env ;non-zero if we preserve programs
push h ;user & drive for next transient
;
; user number
;
mvi l,ccpusr ; HL = .CCP USER (saved in SCB)
lxi b,usernum ; BC = .CCP'S DEFAULT USER
mov d,h
mvi e,usrcode ; DE = .BDOS USER CODE
ldax d
stax b ; usernum = bdos user number
mov a,m ; ccp user
jnz scb1 ; jump if chaining env preserved
stax b ; usernum = ccp default user
scb1: stax d ; bdos user = ccp default user
;
; transient program's current disk
;
inx b ;.CHAINDSK
mvi e,seldsk ;.BDOS CURRENT DISK
ldax d
jnz scb2 ; jump if chaining env preserved
mvi a,0ffh
; cma ; make an invalid disk
scb2: stax b ; chaindsk = bdos disk (or invalid)
;
; current disk
;
dcx h ;.CCP's DISK (saved in SCB)
inx b ;.CCP's CURRENT DISK
mov a,m
stax b
stax d ; BDOS current disk
;
; $$$.SUB drive
;
mvi l,tempdrv
inx b ;.SUBFCB
mov a,m
stax b ; $$$.SUB drive = temporary drive
;
; check for program chain
;
pop h ;HL =.ccpflag1
mov a,m
ani chainflg ;is it a chain function (47)
jz ckboot ;jump if not
lxi h,buf
chain: lxi d,cbufl
mvi c,tpa-buf-1
mov a,c
stax d
inx d
call move ;hl = source, de = dest, c = count
jmp ccpparse
;
; execute profile.sub ?
;
ckboot: mvi l,ccpflag3
mov a,m
ani coldboot ;is this a cold start
jnz ccpcr ;jump if not
mov a,m
ori coldboot ;set flag for next time
mov m,a
sta errflg ;set to ignore errors
lxi h,profile
jmp chain ;attempt to exec profile.sub
profile:
db 'PROFILE.S',0
;
;
;
;************************************************************************
;
; BUILT-IN COMMANDS (and errors) RETURN HERE
;
;************************************************************************
;
;
ccpcr:
; enter here on each command or error condition
call setccpflg
call crlf
ccpret:
lxi h,stack-2 ;reset stack in case of error
sphl ;preserve CCPRET on stack
xra a
sta line
lxi h,ccpret ;return for next builtin
push h
call setccpflg
dcx h ;.CCPFLAG1
mov a,m
ani subfilemask ;check for $$$.SUB submit
jz prompt
;
;
;
;************************************************************************
;
; $$$.SUB file processing
;
;************************************************************************
;
;
lxi d,cbufl ;set DMA to command buffer
call setbuf
mvi c,openf
call sudos ;open it if flag on
mvi c,cstatf ;check for break if successful open
cz sudos ;^C typed?
jnz subclose ;delete $$$.SUB if break or open failed
lxi h,subrr2
mov m,a ;zero high random record #
dcx h
mov m,a ;zero middle random record #
dcx h
push h
lda subrc
dcr a
mov m,a ;set to read last record of file
mvi c,rreadf
cp sudos
pop h
dcr m ;record count (truncate last record)
mvi c,delf
cm sudos
ora a ;error on read?
;
;
subclose:
push psw
mvi c,trunf ;truncate file (& close it)
call sudos
pop psw ;any errors ?
jz ccpparse ;parse command if not
;
;
subkill:
lxi b,subfile
call resetflg ;turn off submit flag
mvi c,delf
call sudos ;kill submit
;
;
;
;************************************************************************
;
; GET NEXT COMMAND
;
;************************************************************************
;
;
;
; prompt user
;
prompt:
lda usernum
ora a
cnz pdb ;print user # if non-zero
call dirdrv1
mvi a,'>'
call putc
;
if multi
;move ccpconbuf addr to conbuffer addr
lxi d,ccpconbuf*256+conbuffer
call wordmov ;process multiple command, unless in submit
ora a ;non-zero => multiple commands active
push psw ;save A=high byte of ccpconbuf
lxi b,ccpbdos
cnz resetflg ;turn off BDOS flag if multiple commands
endif
call rcln ;get command line from console
call resetccpflg ;turn off BDOS, SUBMIT & GET ccp flags
if multi
pop psw ;D=high byte of ccpconbuf
cnz multisave ;save multiple command buffer
endif
;
;
;
;************************************************************************
;
; PARSE COMMAND
;
;************************************************************************
;
;
ccpparse:
;
; reset default page mode
; (in case submit terminated)
;
call subtest ;non-zero if submit is active
jnz get$pg$mode ;skip, if so
set$pg$mode:
mvi l,page$def
mov a,m ;pick up default
dcx h
mov m,a ;place in mode
get$pg$mode:
mvi l,page$mode
mov a,m
sta pgmode
;
;check for multiple commands
;convert to upper case
;reset ccp flag, in case entered from a CHAIN (or profile)
;
call uc ;convert to upper case, ck if multiple command
rz ;get another line if null or comment
;
;transient or built-in command?
;
lxi d,ufcb ;include user number byte in front of FCB
call gcmd ;parse command name
lda fcb+9 ;file type specified?
cpi ' '
jnz ccpdisk2 ;execute from disk, if so
lxi h,ufcb ;user or drive specified?
mov a,m ;user number
inx h
ora m ;drive
inx h
mov a,m ;get 1st character of filename
jnz ccpdisk3 ;jump if so
;
;BUILT-IN HANDLER
;
ccpbuiltin:
lxi h,ctbl ;search table of internal commands
lxi d,fcb+1
lda fcb+3
cpi ' '+1 ;is it shorter that 3 characters?
cnc tbls ;is it a built-in?
jnz ccpdisk0 ;load from disk if not
lda option ;[ in command line?
ora a ;options specified?
mov a,b ;built-in index from tbls
lhld parsep
shld errsav ;save beginning of command tail
lxi h,ptbl ;jump to processor if options not
jz tblj ;specified
cpi 4
jc trycom
lxi h,fcb+4
jnz ccpdisk0 ;if DIRS then look for DIR.COM
mvi m,' '
;
;LOAD TRANSIENT (file type unspecified)
;
ccpdisk0:
lxi b,order
call getflg ;0=COM 8=COM,SUB 16=SUB,COM
jz ccpdisk2 ;search for COM file only
mvi b,8 ;=> 2nd choice is SUB
sub b ;now a=0 (COM first) or 8 (SUB first)
jz ccpdisk1 ;search for COM first then SUB
mvi b,0 ;search for SUB first then COM
ccpdisk1:
push b ;save 2nd type to try
call settype ; A = offset of type in type table
call exec ;try to execute, return if unsuccessful
pop psw ;try 2nd type
call settype
;
;LOAD TRANSIENT (file type specified)
;
ccpdisk2:
call exec
jmp perror ;error if can't find it
;
;DRIVE SPECIFIED (check for change drives/users command)
;
ccpdisk3:
cpi ' ' ;check for filename
jnz ccpdisk0 ;execute from disk if specified
call eoc ;error if not end of command
lda ufcb ;user specified?
sui 1
jc ccpdrive
ccpuser:
sta usernum ;CCP's user number
mvi b,ccpusr
call setbyte ;save it in SCB
call setuser ;set current user
ccpdrive:
lda fcb ;drive specified?
dcr a
rm ;return if not
push psw
call select
pop psw
sta disk ;CCP's drive
mvi b,ccpdrv
jmp setbyte ;save it in SCB
;;
;
;************************************************************************
;
; BUILT-IN COMMANDS
;
;************************************************************************
;
;
; Table of internal ccp commands
;
;
ctbl: db 'DIR '
db 'TYPE '
db 'ERASE '
db 'RENAME '
db 'DIRSYS '
db 'USER '
db 0
;
ptbl: dw dir
dw type
dw era
dw ren
dw dirs
dw user
;;
;;-----------------------------------------------------------------------
;;
;; DIR Command
;;
;; DIR list directory of current default user/drive
;; DIR <X>: list directory of user/drive <X>
;; DIR <AFN> list all files on the current default user/drive
;; with names that match <AFN>
;; DIR <X>:<AFN> list all files on user/drive <X> with names that
;; match <AFN>
;;
;;-----------------------------------------------------------------------
;;
;
if newdir
dirdrv:
lda dfcb ;get disk number
endif
dirdrv0:
dcr a
jp dirdrv2
dirdrv1:
lda disk ;get current disk
dirdrv2:
adi 'A'
jmp pfc ;print it (save BC,DE)
;
;
if newdir
dir:
mvi c,0 ;flag for DIR (normal)
lxi d,sysfiles
jmp dirs1
;
;
dirs:
mvi c,080h ;flag for DIRS (system)
lxi d,dirfiles
dirs1: push d
call direct
pop d ;de = .system files message
jz nofile ;jump if no files found
mov a,l ;A = number of columns
cmp b ;did we print any files?
cnc crlf ;print crlf if so
lxi h,anyfiles
dcr m
inr m
rz ;return if no files
;except those requested
dcr m ;set to zero
jmp pmsgnl ;tell the operator other files exist
;
;
direct:
push b ;save DIR/DIRS flag
call sbuf80 ;set DMA = 80h
call gfn ;parse file name
lxi d,dfcb+1
ldax d
cpi ' '
mvi b,11
cz setmatch ;use "????????.???" if none
call eoc ;make sure there's nothing else
call srchf ;search for first directory entry
pop b
rz ;if no files found
dir0:
lda dircols ;number of columns for dir
mov l,a
mov b,a
inr b ;set # names to print per line (+1)
dir1:
push h ;L=#cols, B=curent col, C=dir/dirs
lxi h,10 ;get byte with SYS bit
dad d
mov a,m
pop h
ani 80h ;look at SYS bit
cmp c ;DIR/DIRS flag in C
jz dir2 ;display, if modes agree
mvi a,1 ;set anyfiles true
sta anyfiles
jmp dir3 ;don't print anything
;
; display the filename
;
dir2:
dcr b
cz dirln ;sets no. of columns, puts crlf
mov a,b ;number left to print on line
cmp l ;is current col = number of cols
cz dirdrv ;display the drive, if so
mvi a,':'
call pfc ;print colon
call space
call pfn ;print file name
call space ;pad with space
dir3:
push b ;save current col(B), DIR/DIRS(C)
push h ;save number of columns(L)
call break ;drop out if keyboard struck
call srchn ;search for another match
pop h
pop b
jnz dir1
direx:
inr a ;clear zero flag
ret
else
dirs: ; display system files only
mvi a,0d2h ; JNC instruction
sta dir11 ; skip on non-system files
;
dir: ; display non-system files only
lxi h,ccpcr
push h ; push return address
call gfn ;parse file name
inx d
ldax d
cpi ' '
mvi b,11
cz setmatch ;use "????????.???" if none
call eoc ;make sure there's nothing else
call findone ;search for first directory entry
jz dir4
mvi b,5 ;set # names to print per line
dir1: lxi h,10 ;get byte with SYS bit
dad d
mov a,m
ral ;look at SYS bit
dir11: jc dir3 ;don't print it if SYS bit set
mov a,b
push b
dir2: lxi h,9 ;get byte with R/O bit
dad d
mov a,m
ral ;look at R/O bit
mvi a,' ' ;print space if not R/O
jnc dir21 ;jump if not R/O
mvi a,'*' ;print star if R/O
dir21: call pfc ;print character
call pfn ;print file name
mvi a,13 ;figure out how much padding is needed
sub c
dir25: push psw
call space ;pad it out with spaces
pop psw
dcr a
jnz dir25 ;loop if more required
pop b
dcr b ;decrement # names left on line
jnz dir3
call crlf ;go to new line
mvi b,5 ;set # names to print on new line
dir3: push b
call break ;drop out if keyboard struck
call srchn ;search for another match
pop b
jnz dir1
dir4: mvi a,0dah ;JC instruction
sta dir11 ;restore normal dir mode (skip system files)
jmp ccpcr
endif
;;
;;-----------------------------------------------------------------------
;;
;; TYPE command
;;
;; TYPE <UFN> Print the contents of text file <UFN> on
;; the console.
;;
;;-----------------------------------------------------------------------
;;
type: lxi h,ccpcr
push h ;push return address
call getfn ;get and parse filename
mvi a,127 ;initialize buffer pointer
sta bufp
mvi c,openf
call sbdosf ;open file if a filename was typed
type1: call break ;exit if keyboard struck
call getb ;read byte from file
rnz ;exit if physical eof or read error
cpi eof ;check for eof character
rz ;exit if so
call putc ;print character on console
jmp type1 ;loop
;
;;-----------------------------------------------------------------------
;;
;; USER command
;;
;; USER <NN> Set the user number
;;
;;-----------------------------------------------------------------------
;;
user:
lxi d,unmsg ;Enter User #:
call getprm
call gdn ;convert to binary
rz ;return if nothing typed
jmp ccpuser ;set user number
;
;;-----------------------------------------------------------------------
;;
;; ERA command
;;
;; ERA <AFN> Erase all file on the current user/drive
;; which match <AFN>.
;; ERA <X>:<AFN> Erase all files on user/drive <X> which
;; match <AFN>.
;;
;;-----------------------------------------------------------------------
;;
era: call getfn ;get and parse filename
jz era1
call ckafn ;is it ambiguous?
jnz era1
lxi d,eramsg
call pmsg
lhld errorp
mvi c,' ' ;stop at exclamation mark or 0
call pstrg ;echo command
lxi d,confirm
call getc
call crlf
mov a,l ;character in L after CRLF routine
ani 5fh ;convert to U/C
cpi 'Y' ;Y (yes) typed?
rnz ;return, if not
ora a ;reset zero flag
era1: mvi c,delf
jmp sbdosf
;;-----------------------------------------------------------------------
;;
;;
;; REN command
;;
;;-----------------------------------------------------------------------
;;
ren: call gfn ;zero flag set if nothing entered
push psw
lxi h,16
dad d
xchg
push d ;DE = .dfcb+16
push h ;HL = .dfcb
mvi c,16
call move ;DE = dest, HL = source
call gfn
pop h ;HL=.dfcb
pop d ;DE=.dfcb+16
call drvok
mvi c,renf ;make rename call
pop psw ;zero flag set if nothing entered
;
;;-----------------------------------------------------------------------
;;
;; BUILT-IN COMMAND BDOS CALL & ERROR HANDLERS
;;
;;-----------------------------------------------------------------------
;
sbdosf:
push psw
cnz eoc ;make sure there's nothing else
pop psw
lxi d,dfcb
mvi b,0ffh
mvi h,1 ;execute disk command if we don't call
cnz bdosf ;call if something was entered
rnz ;return if successful
ferror:
dcr h ;was it an extended error?
jm nofile
lhld errsav
shld parsep
trycom: call exec
call pfn
lxi d,required
jmp builtin$err
;
;;-----------------------------------------------------------------------
;
;
; check for drive conflict
; HL = FCB
; DE = FCB+16
;
drvok: ldax d ;get byte from 2nd fcb
cmp m ;ok if they match
rz
ora a ;ok if 2nd is 0
rz
inr m ;error if the 1st one's not 0
dcr m
jnz perror
mov m,a ;copy from 2nd to 1st
ret
;;-----------------------------------------------------------------------
;;
;; check for ambiguous reference in file name/type
;;
;; entry: b = length of string to check (ckafn0)
;; de = fcb area to check (ckafn0) - 1
;; exit: z = set if any ? in file reference (ambiguous)
;; z = clear if unambiguous file reference
;;
ckafn:
mvi b,11 ;check entire name and type
ckafn0: inx d
ldax d
cpi '?' ;is it an ambiguous file name
if newera
rz ;return true if any afn
else
rnz ;return true only if *.*
endif
dcr b
jnz ckafn0
if newera
dcr b ;clear zero flag to return false
endif
ret ;remove above DCR to return true
;;
;;-----------------------------------------------------------------------
;;
;; get parameter (generally used to get a missing one)
;;
getprm:
call skps ;see if already there
rnz ;return if so
getp0:
if prompts
push d
lxi d,enter
call pmsg
pop d
endif
call pmsg ;print prompt
call rcln ;get response
jmp uc ;convert to upper case
;
;;
;;-----------------------------------------------------------------------
if not newdir
;;
;; search for first file, print "No File" if none
;;
findone:
call srchf
rnz ;found
endif
;;-----------------------------------------------------------------------
nofile:
lxi d,nomsg ;tell user no file found
builtin$err:
call pmsgnl
jmp ccpret
;
;
;************************************************************************
;
; EXECUTE DISK RESIDENT COMMAND
;
;************************************************************************
;
;
xfcb: db 0,'SUBMIT COM' ;processor fcb
;
;
; execute submit file (or any other processor)
;
xsub: ;DE = .fcb
ldax d
mvi b,clp$drv
call setbyte ;save submit file drive
lxi h,xfcb
mvi c,12
call move ;copy processor into fcb
lxi h,cbufl ;set parser pointer back to beginning
mvi m,' '
inx h ;move past blank
shld parsep
; execute SUBMIT.COM
;
;
; execute disk resident command (return if not found or error)
;
exec:
;try to open and execute fcb
lxi d,fcb+9
lxi h,typtbl
call tbls ;search for type in type table
rnz ;return if no match
lxi d,ufcb
ldax d ;check to see if user specified
ora a
rnz ;return if so
inx d
ldax d ;check if drive specified
mov c,a
push b ;save type (B) and drive (C)
mvi c,0 ;try only 1 open if drive specified
ora a
jnz exec1 ;try to open as specified
lxi b,(drv0-1)*256+4;try upto four opens from drv chain
lda disk
inr a
mov h,a ;save default disk in H
mvi l,1 ;allow only 1 match to default disk
exec0: inr b ;next drive to try in SCB drv chain
dcr c ;any more tries?
mov a,c
push h
cp getbyte
pop h
ora a
jm exec3
jz exec01 ;jump if drive is 0 (default drive)
cmp h ;is it the default drive
jnz exec02 ;jump if not
exec01: mov a,h ;set drive explicitly
dcr l ;is it the 2nd reference
jm exec0 ;skip, if so
exec02: stax d ;put drive in FCB
exec1: push b ;save drive offset(B) & count(C)
push h
call opencom ;on default drive & user
pop h
pop b
jz exec0 ;try next if open unsuccessful
;
; successful open, now jump to processor
;
exec2:
if dayfile
lxi b,display
call getflg
jz exec21
ldax d
call dirdrv0
mvi a,':'
call pfc
push d
call pfn
pop d
push d
lxi h,8
dad d
mov a,m
ani 80h
lxi d,userzero
cnz pmsg
call crlf
pop d
endif
exec21: pop psw ;recover saved command type
lxi h,xptbl
;
; table jump
;
; entry: hl = address of table of addresses
; a = entry # (0 thru n-1)
;
tblj: add a ;adjust for two byte entries
call addhla ;compute address of entry
push d
mov e,m ;fetch entry
inx h
mov d,m
xchg
pop d
pchl ;jump to it
;
typtbl: db 'COM '
db 'SUB '
db 'PRL '
db 0
;
xptbl: dw xcom
dw xsub
dw xcom
;
; unsuccessful attempt to open command file
;
exec3: pop b ;recover drive
mov a,c
stax d ;replace in fcb
ret
;
;
settype:
;set file type specified from type table
;a = offset (x2) of desired type (in bytes)
rrc
lxi h,typtbl
call addhla ;hl = type in type table
lxi d,fcb+9
mvi c,3
jmp move ;move type into fcb
;
;
;
; EXECUTE COM FILE
;
xcom: ;DE = .fcb
;
; set up FCB for loader to use
;
lxi h,tpa
shld fcbrr ;set load address to 100h
lhld realdos-1 ;put fcb in the loader's stack
dcr h ;page below LOADER (or bottom RSX)
mvi l,0C0h ;offset for FCB in page below the BDOS
push h ;save for LOADER call
ldax d ;get drive from fcb(0)
sta cmdrv ;set command drive field in base page
xchg
mvi c,35
call move ;now move FCB to the top of the TPA
;
; set up base page
;
lxi h,errflg ;tell parser to ignore errors
inr m
xcom3: lhld parsep
dcx h ;backup over delimiter
lxi d,buf+1
xchg
shld parsep ;set parser to 81h
call copy0 ;copy command tail to 81h with
;terminating 0 (returns A=length)
sta buf ;put command tail length at 80h
xcom5: call gfn ;parse off first argument
shld pass0
mov a,b
sta len0
lxi d,dfcb1
call gfn0 ;parse off second argument
shld pass1
mov a,b
sta len1
xcom7: lxi h,chaindsk ;.CHAINDSK
mov a,m
ora a
cp select
lda usernum
call setuser ;set default user, returns H=SCB
add a ;shift user to high nibble
add a
add a
add a
mvi l,seldsk
ora m ;put disk in low nibble
sta defdrv ;set location 4
;
; initialize stack
;
xcom8: pop d ;DE = .fcb
lhld realdos-1 ;base page of BDOS
xra a
mov l,a ;top of stack below BDOS
sphl ;change the stack pointer for CCP
mov h,a ;push warm start address on stack
push h ;for programs returning to the CCP
inr h ;Loader will return to TPA
push h ;after loading a transient program
;
; initialize fcb0(CR), console mode, program return code
; & removable media open and login vectors
;
xcom9: sta 7ch ;clear next record to read
mvi b,con$mode
call setbyte ;set to zero (turn off ^C status)
mvi l,olog
mov m,a ;zero removable open login vector
inx h
mov m,a
inx h
mov m,a ;zero removable media login vector
inx h
mov m,a
mvi l,ccpflag1
mov a,m
ani chain$flg ;chaining?
jnz loader ;load program without clearing
mvi l,prog$ret$code ;the program return code
mov m,a ;A=0
inx h
mov m,a ;set program return = 0000h
;
; call loader
;
loader:
mov a,m ;reset chain flag if set,
ani not$chainflg ;has no effect if we fell through
mov m,a
mvi c,loadf ;use load RSX to load file
jmp bdos ;now load it
;
;
;
;
;************************************************************************
;
; BDOS FUNCTION INTERFACE - Non FCB functions
;
;************************************************************************
;
;
;
;;-----------------------------------------------------------------------
;;
;;
;;
;; print character on terminal
;; pause if screen is full
;; (BDOS function #2)
;;
;; entry: a = character (putc entry)
;; e = character (putc2 entry)
;;
putc: cpi lf ;end of line?
jnz putc1 ;jump if not
lxi h,pgsize ;.pgsize
mov a,m ;check page size
inx h ;.line
inr m ;line=line+1
sub m ;line=page?
jnz putc0
mov m,a ;reset line=0 if so
inx h ;.pgmode
mov a,m ;is page mode off?
ora a ;page=0 if so
lxi d,more
cz getc ;wait for input if page mode on
cpi ctrlc
jz ccpcr
mvi e,cr
call putc2 ;print a cr
putc0: mvi a,lf ;print the end of line char
putc1: mov e,a
putc2: mvi c,coutf
jmp bdos
;;
;;-----------------------------------------------------------------------
;;
;; get character from console
;; (BDOS function #1)
;;
getc: call pmsg
getc1: mvi c,cinf
jmp bdos
;;
;;-----------------------------------------------------------------------
;;
;; print message string on terminal
;; (BDOS function #9)
;;
pmsg: mvi c,pbuff
jmp bdos
;;
;;-----------------------------------------------------------------------
;;
;; read line from console
;; (calls BDOS function #10)
;;
;; exit: z = set if null line
;;
;; This function uses the buffer "cbuf" (see definition of
;; function 10 for a description of the buffer). All input
;; is converted to upper case after reading and the pointer
;; "parsep" is set to the begining of the first non-white
;; character string.
;;
rcln: lxi h,cbufmx ;get line from terminal
mvi m,comlen ;set maximum buffer size
xchg
mvi c,rbuff
call bdos
lxi h,cbufl ;terminate line with zero byte
mov a,m
inx h
call addhla
mvi m,0 ;put zero at the end
jmp crlf ;advance to next line
;
;;
;;-----------------------------------------------------------------------
;;
;; exit routine if keyboard struck
;; (calls BDOS function #11)
;;
;; Control is returned to the caller unless the console
;; keyboard has a character ready, in which case control
;; is transfer to the main program of the CCP.
;;
break: call break1
rz
jmp ccpcr
break1: mvi c,cstatf
call rw
rz
mvi c,cinf
jmp rw
;;
;;-----------------------------------------------------------------------
;;
;; set disk buffer address
;; (BDOS function #26)
;;
;; entry: de -> buffer ("setbuf" only)
;;
sbuf80: lxi d,buf
setbuf: mvi c,dmaf
jmp bdos
;;
;;-----------------------------------------------------------------------
;;
;; select disk
;; (BDOS function #14)
;;
;; entry: a = drive
;;
select:
mov e,a
mvi c,self
jmp bdos
;
;;
;;-----------------------------------------------------------------------
;;
;; set user number
;; (BDOS function #32)
;;
;; entry: a = user #
;; exit: H = SCB page
;;
setuser:
mvi b,usrcode
jmp set$byte
;
;
;
;************************************************************************
;
; BDOS FUNCTION INTERFACE - Functions with a FCB Parameter
;
;************************************************************************
;
;
;;
;; open file
;; (BDOS function #15)
;;
;; exit: z = set if file not found
;;
;;
opencom: ;open command file (SUB, COM or PRL)
lxi b,openf ;b=0 => return error mode of 0
lxi d,fcb ;use internal FCB
;; BDOS CALL ENTRY POINT (used by built-ins)
;;
;; entry: b = return error mode (must be 0 or 0ffh)
;; c = function no.
;; de = .fcb
;; exit: z = set if error
;; de = .fcb
;;
bdosf: lxi h,32 ;offset to current record
dad d ;HL = .current record
mvi m,0 ;set to zero for read/write
push b ;save function(C) & error mode(B)
push d ;save .fcb
ldax d ;was a disk specified?
ana b ;and with 0 or 0ffh
dcr a ;if so, select it in case
cp select ;of permanent error (if errmode = 0ffh)
lxi d,passwd
call setbuf ;set dma to password
pop d ;restore .fcb
pop b ;restore function(C) & error mode(B)
push d
lhld scbaddr
mvi l,errormode
mov m,b ;set error mode
push h ;save .errormode
call bdos
pop d ;.errormode
xra a
stax d ;reset error mode to 0
lda disk
mvi e,seldsk
stax d ;reset current disk to default
push h ;save bdos return values
call sbuf80
pop h ;bdos return
inr l ;set z flag if error
pop d ;restore .fcb
ret
;;
;;-----------------------------------------------------------------------
;;
;; close file
;; (BDOS function #16)
;;
;; exit: z = set if close error
;;
;;close: mvi c,closef
;; jmp oc
;;
;;-----------------------------------------------------------------------
;;
;; delete file
;;
;; exit: z = set if file not found
;;
;; The match any character "?" may be used without restriction
;; for this function. All matched files will be deleted.
;;
;;
;;delete:
;; mvi c,delf
;; jmp oc
;;
;;-----------------------------------------------------------------------
;;
;; create file
;; (BDOS function #22)
;;
;; exit: z = set if create error
;;
;;make: mvi c,makef
;; jmp oc
;;-----------------------------------------------------------------------
;;
;; search for first filename match (using "DFCB" and "BUF")
;; (BDOS function #17)
;;
;; exit: z = set if no match found
;; z = clear if match found
;; de -> directory entry in buffer
;;
srchf: mvi c,searf ;set search first function
jmp srch
;;
;;-----------------------------------------------------------------------
;;
;; search for next filename match (using "DFCB" and "BUF")
;; (BDOS function #18)
;;
;; exit: z = set if no match found
;; z = clear if match found
;; de -> directory entry in buffer
;;
srchn: mvi c,searnf ;set search next function
srch: lxi d,dfcb ;use default fcb
call bdos
inr a ;return if not found
rz
dcr a ;restore original return value
add a ;shift to compute buffer pos'n
add a
add a
add a
add a
lxi h,buf ;add to buffer start address
call addhla
xchg ;de -> entry in buffer
xra a ;may be needed to clear z flag
dcr a ;depending of value of "buf"
ret
;;
;;-----------------------------------------------------------------------
;;
;; read file
;; (BDOS function #20)
;;
;; entry: hl = buffer address (readb only)
;; exit z = set if read ok
;;
read: xra a ;clear getc pointer
sta bufp
mvi c,readf
lxi d,dfcb
rw: call bdos
ora a
ret
;
;;
;;-----------------------------------------------------------------------
;;
;; $$$.SUB interface
;;
;; entry: c = bdos function number
;; exit z = set if successful
sudos: lxi d,subfcb
jmp rw
;
;
;
;************************************************************************
;
; COMMAND LINE PARSING SUBROUTINES
;
;************************************************************************
;
;------------------------------------------------------------------------
;
; COMMAND LINE PREPARSER
; reset function 10 flag
; set up parser
; convert to upper case
;
; All input is converted to upper case and the pointer
; "parsep" is set to the begining of the first non-blank
; character string. If the line begins with a ; or :, it
; is treated specially:
;
; ; comment the line is ignored
; : conditional the line is ignored if a fatal
; error occured during the previous
; command, otherwise the : is
; ignored
;
; An exclamation point is used to separate multiple commands on a
; a line. Two adjacent exclaimation points translates into a single
; exclaimation point in the command tail for compatibility.
;------------------------------------------------------------------------
;
;
uc:
call resetccpflg
xchg ;DE = .SCB
xra a
sta option ;zero option flag
lxi h,cbuf
call skps1 ;skip leading spaces/tabs
xchg
cpi ';' ;HL = .scb
rz
cpi '!'
jz uc0
cpi ':'
jnz uc1
mvi l,prog$ret$code
inr m
inr m ;was ^C typed? (low byte 0FEh)
jz uc0 ;successful, if so
inx h
inr m ;is high byte 0FFh?
rz ;skip command, if so
uc0: inx d ;skip over 1st character
uc1: xchg ;HL=.command line
shld parsep ;set parse pointer to beginning of line
uc3: mov a,m ;convert lower case to upper
cpi '['
jnz uc4
sta option ;'[' is the option delimiter => command option
uc4: cpi 'a'
jc uc5
cpi 'z'+1
jnc uc5
sui 'a'-'A'
mov m,a
uc5:
if multi
cpi '!'
cz multistart ;HL=.char, A=char
endif
inx h ;advance to next character
ora a ;loop if not end of line
jnz uc3
;
; skip spaces
; return with zero flag set if end of line
;
skps: lhld parsep ;get current position
skps1: shld parsep ;save position
shld errorp ;save position for error message
mov a,m
ora a ;return if end of command
rz
cpi ' '
jz skps2
cpi tab ;skip spaces & tabs
rnz
skps2: inx h ;advance past space/tab
jmp skps1 ;loop
;
;-----------------------------------------------------------------------
;
; MULTIPLE COMMANDS PER LINE HANDLER
;
;-----------------------------------------------------------------------
if multi
multistart:
;
; A = current character in command line
; HL = address of current character in command line
;
;double exclaimation points become one
mov e,l
mov d,h
inx d
ldax d
cpi '!' ;double exclaimation points
push psw
push h
cz copy0 ;convert to one, if so
pop h
pop psw
rz
;we have a valid multiple command line
mvi m,0 ;terminate command line here
xchg
;multiple commands not allowed in submits
;NOTE: submit unravels multiple commands making the
;following test unnecessary. However, with GET[system]
;or CP/M 2.2 SUBMIT multiple commands will be posponed
;until the entire submit completes...
; call subtest ;submit active
; mvi a,0
; rnz ;return with A=0, if so
;set up the RSX buffer
lhld osbase ;get high byte of TPA address
dcr h ;subtract 1 page for buffer
mvi l,endchain ;HL = RSX buffer base-1
mov m,a ;set end of chain flag to 0
push h ;save it
multi0: inx h
inx d
ldax d ;get character from cbuf
mov m,a ;place in RSX
cpi '!'
jnz multi1
mvi m,cr ;change exclaimation point to cr
multi1: ora a
jnz multi0
mvi m,cr ;end last command with cr
inx h
mov m,a ;terminate with a zero
;set up RSX prefix
mvi l,6 ;entry point
mvi m,jmp ;put a jump instruction there
inx h
mvi m,9 ;make it a jump to base+9 (RSX exit)
inx h
mov m,h
inx h ;HL = RSX exit point
mvi m,jmp ;put a jump instruction there
mvi l,warmflg ;HL = remove on warm start flag
mov m,a ;set (0) for RSX to remain resident
mov l,a ;set low byte to 0 for fixchain
xchg ;DE = RSX base
call fixchain ;add the RSX to the chain
;save buffer address
lhld scbaddr
mvi l,ccpconbuf ;save buffer address in CCP conbuf field
pop d ;DE = RSX base
inx d
mov m,e
inx h
mov m,d
mvi l,multi$rsx$pg
mov m,d ;save the RSX base
xra a ;zero in a to fall out of uc
ret
;
;
; save the BDOS conbuffer address and
; terminate RSX if necessary.
;
multisave:
lxi d,conbuffer*256+ccpconbuf
call wordmov ;first copy conbuffer in case SUBMIT
ora a ;and/or GET are active
lxi d,conbuffl*256+ccpconbuf
cz wordmov ;if conbuff is zero then conbufl has the
push h ;next address
call break1
pop h ;H = SCB page
mvi l,ccpconbuf
jnz multiend
mov e,m
inx h
mov d,m ;DE = next conbuffer address
inr m
dcr m ;is high byte zero?
dcx h ;HL = .ccpconbuf
jz multiend ;remove multicmd RSX if so
ldax d ;check for terminating zero
ora a
rnz ;return if not
;
; we have exhausted all the commands
multiend:
; HL = .ccpconbuf
xra a
mov m,a ;set buffer to zero
inx h
mov m,a
mvi l,multi$rsx$pg
mov h,m
mvi l,0eh ;HL=RSX remove on warmstart flag
dcr m ;set to true for removal
jmp rsx$chain ;remove the multicmd rsx buffer
endif
;;
;************************************************************************
;
; FILE NAME PARSER
;
;************************************************************************
;
;
;
; get file name (read in if none present)
;
;
;; The file-name parser in this CCP implements
;; a user/drive specification as an extension of the normal
;; CP/M drive selection feature. The syntax of the
;; user/drive specification is given below. Note that a
;; colon must follow the user/drive specification.
;;
;; <a>: <a> is an alphabetic character A-P specifing one
;; of the CP/M disk drives.
;;
;; <n>: <n> is a decimal number 0-15 specifying one of the
;; user areas.
;;
;; <n><a>: A specification of both user area and drive.
;;
;; <a><n>: Synonymous with above.
;;
;; Note that the user specification cannot be included
;; in the parameters of transient programs or precede a file
;; name. The above syntax is parsed by gcmd (get command).
;;
;; ************************************************************
getfn:
if prompts
lxi d,fnmsg
getfn0:
call getprm
endif
gfn: lxi d,dfcb
gfn0: call skps ;sets zero flag if eol
push psw
call gfn2
pop psw
ret
;
; BDOS FUNCTION 152 INTERFACE
;
;entry: DE = .FCB
; HL = .buffer
;flags/A reg preserved
;exit: DE = .FCB
;
;
gfn2: shld parsep
shld errorp
push d ;save .fcb
lxi d,pfncb
mvi c,parsef
if func152
call bdos
else
call parse
endif
pop d ;.fcb
mov a,h
ora l ;end of command? (HL = 0)
mov b,m ;get delimiter
inx h ;move past delimiter
jnz gfn3
lxi h,zero+2 ;set HL = .0
gfn3: mov a,h
ora l ;parse error? (HL = 0ffffh)
jnz gfn4
lxi h,zero+2
call perror
gfn4: mov a,b
cpi '.'
jnz gfn6
dcx h
gfn6: shld parsep ;update parse pointer
gfnpwd: mvi c,16
lxi h,pfcb
push d
call move
lxi d,passwd ;HL = .disk map in pfcb
mvi c,10
call move ;copy to passwd
pop d ;HL = .password len
mov a,m
zero: lxi h,0 ;must be an "lxi h,0"
ora a ;is there a password?
mov b,a
jz gfn8
lhld errorp ;HL = .filename
gfn7: mov a,m
cpi ';'
inx h
jnz gfn7
gfn8: ret ;B = len, HL = .password
;
; PARSE CP/M 3 COMMAND
; entry: DE = .UFCB (user no. byte in front of FCB)
; PARSEP = .command line
gcmd:
push d
xra a
stax d ;clear user byte
inx d
stax d ;clear drive byte
inx d
call skps ;skip leading spaces
;
; Begin by looking for user/drive-spec. If none if found,
; fall through to main file-name parsing section. If one is found
; then branch to the section that handles them. If an error occurs
; in the user/drive spec; treat it as a filename for compatibility
; with CP/M 2.2. (e.g. STAT VAL: etc.)
;
lhld parsep ;get pointer to current parser position
pop d
push d ;DE = .UFCB
mvi b,4 ;maximum length of user/drive spec
gcmd1: mov a,m ;get byte
cpi ':' ;end of user/drive-spec?
jz gcmd2 ;parse user/drive if so
ora a ;end of command?
jz gcmd8 ;parse filename (Func 152), if so
dcr b ;maximum user/drive spec length exceeded?
inx h
jnz gcmd1 ;loop if not
;
; Parse filename, type and password
;
gcmd8:
pop d
xra a
stax d ;set user = default
lhld parsep
gcmd9: inx d ;past user number byte
ldax d ;A=drive
push psw
call gfn2 ;BDOS function 152 interface
pop psw
stax d
ret
;
; Parse the user/drive-spec
;
gcmd2:
lhld parsep ;get pointer to beginning of spec
mov a,m ;get character
gcmd3: cpi '0' ;check for user number
jc gcmd4 ;jump if not numeric
cpi '9'+1
jnc gcmd4
call gdns ;get the user # (returned in B)
pop d
push d
ldax d ;see if we already have a user #
ora a
jnz gcmd8 ;skip if we do
mov a,b ;A = specified user number
inr a ;save it as the user-spec
stax d
jmp gcmd5
gcmd4: cpi 'A' ;check for drive-spec
jc gcmd8 ;skip if not a valid drive character
cpi 'P'+1
jnc gcmd8
pop d
push d
inx d
ldax d ;see if we already have a drive
ora a
jnz gcmd8 ;skip if so
mov a,m
sui '@' ;convert to a drive-spec
stax d
inx h
gcmd5: mov a,m ;get next character
cpi ':' ;end of user/drive-spec?
jnz gcmd3 ;loop if not
inx h
pop d ;.ufcb
jmp gcmd9 ;parse the file name
;
;************************************************************************
;
; TEMPORARY PARSE CODE
;
;************************************************************************
;
if not func152
; version 3.0b Oct 08 1982 - Doug Huskey
;
;
passwords equ true
parse: ; DE->.(.filename,.fcb)
;
; filename = [d:]file[.type][;password]
;
; fcb assignments
;
; 0 => drive, 0 = default, 1 = A, 2 = B, ...
; 1-8 => file, converted to upper case,
; padded with blanks (left justified)
; 9-11 => type, converted to upper case,
; padded with blanks (left justified)
; 12-15 => set to zero
; 16-23 => password, converted to upper case,
; padded with blanks
; 26 => length of password (0 - 8)
;
; Upon return, HL is set to FFFFH if DE locates
; an invalid file name;
; otherwise, HL is set to 0000H if the delimiter
; following the file name is a 00H (NULL)
; or a 0DH (CR);
; otherwise, HL is set to the address of the delimiter
; following the file name.
;
xchg
mov e,m ;get first parameter
inx h
mov d,m
push d ;save .filename
inx h
mov e,m ;get second parameter
inx h
mov d,m
pop h ;DE=.fcb HL=.filename
xchg
parse0:
push h ;save .fcb
xra a
mov m,a ;clear drive byte
inx h
lxi b,20h*256+11
call pad ;pad name and type w/ blanks
lxi b,4
call pad ;EXT, S1, S2, RC = 0
lxi b,20h*256+8
call pad ;pad password field w/ blanks
lxi b,12
call pad
call skip
;
; check for drive
;
ldax d
cpi ':' ;is this a drive?
dcx d
pop h
push h ;HL = .fcb
jnz parse$name
;
; Parse the drive-spec
;
parsedrv:
ldax d ;get character
ani 5fh ;convert to upper case
sui 'A'
jc perr1
cpi 16
jnc perr1
inx d
inx d ;past the ':'
inr a ;set drive relative to 1
mov m,a ;store the drive in FCB(0)
;
; Parse the file-name
;
parse$name:
inx h ;HL = .fcb(1)
call delim
jz parse$ok
if passwords
lxi b,7*256
else
mvi b,7
endif
parse6: ldax d ;get a character
cpi '.' ;file-type next?
jz parse$type ;branch to file-type processing
cpi ';'
jz parsepw
call gfc ;process one character
jnz parse6 ;loop if not end of name
jmp parse$ok
;
; Parse the file-type
;
parse$type:
inx d ;advance past dot
pop h
push h ;HL =.fcb
lxi b,9
dad b ;HL =.fcb(9)
if passwords
lxi b,2*256
else
mvi b,2
endif
parse8: ldax d
cpi ';'
jz parsepw
call gfc ;process one character
jnz parse8 ;loop if not end of type
;
parse$ok:
pop b
push d
call skip
call delim
pop h
rnz
lxi h,0
ora a
rz
cpi cr
rz
xchg
ret
;
; handle parser error
;
perr:
pop b ;throw away return addr
perr1:
pop b
lxi h,0ffffh
ret
;
if passwords
;
; Parse the password
;
parsepw:
inx d
pop h
push h
lxi b,16
dad b
lxi b,7*256+1
parsepw1:
call gfc
jnz parsepw1
mvi a,7
sub b
pop h
push h
lxi b,26
dad b
mov m,a
ldax d ;delimiter in A
jmp parse$ok
else
;
; skip over password
;
parsepw:
inx d
call delim
jnz parsepw
jmp parse$ok
endif
;
; get next character of name, type or password
;
gfc: call delim ;check for end of filename
rz ;return if so
cpi ' ' ;check for control characters
inx d
jc perr ;error if control characters encountered
inr b ;error if too big for field
dcr b
jm perr
if passwords
inr c
dcr c
jnz gfc1
endif
cpi '*' ;trap "match rest of field" character
jz setwild
gfc1: mov m,a ;put character in fcb
inx h
dcr b ;decrement field size counter
ora a ;clear zero flag
ret
;;
setwild:
mvi m,'?' ;set match one character
inx h
dcr b
jp setwild
ret
;
; skip spaces
;
skip0: inx d
skip: ldax d
cpi ' ' ;skip spaces & tabs
jz skip0
cpi tab
jz skip0
ret
;
; check for delimiter
;
; entry: A = character
; exit: z = set if char is a delimiter
;
delimiters: db cr,tab,' .,:;[]=<>|',0
delim: ldax d ;get character
push h
lxi h,delimiters
delim1: cmp m ;is char in table
jz delim2
inr m
dcr m ;end of table? (0)
inx h
jnz delim1
ora a ;reset zero flag
delim2: pop h
rz
;
; not a delimiter, convert to upper case
;
cpi 'a'
rc
cpi 'z'+1
jnc delim3
ani 05fh
delim3: ani 07fh
ret ;return with zero set if so
;
; pad with blanks
;
pad: mov m,b
inx h
dcr c
jnz pad
ret
;
endif
;
;
;************************************************************************
;
; SUBROUTINES
;
;************************************************************************
;
if multi
;
; copy SCB memory word
; d = source offset e = destination offset
;
wordmov:
lhld scbaddr
mov l,d
mov d,h
mvi c,2
;
endif
;
; copy memory bytes
; de = destination hl = source c = count
;
move:
mov a,m
stax d ;move byte to destination
inx h
inx d ;advance pointers
dcr c ;loop if non-zero
jnz move
ret
;
; copy memory bytes with terminating zero
; hl = destination de = source
; returns c=length
copy0: mvi c,0
copy1: ldax d
mov m,a
ora a
mov a,c
rz
inx h
inx d
inx b
jmp copy1
;;
;;-----------------------------------------------------------------------
;;
;; get byte from file
;;
;; exit: z = set if byte gotten
;; a = byte read
;; z = clear if error or eof
;; a = return value of bdos read call
;;
getb: xra a ;clear accumulator
lxi h,bufp ;advance buffer pointer
inr m
cm read ;read sector if buffer empty
ora a
rnz ;return if read error or eof
lda bufp ;compute pointer into buffer
lxi h,buf
call addhla
xra a ;set zero flag
mov a,m ;get byte
ret
;;
;;-----------------------------------------------------------------------
;;
;;
;; system control block flag routines
;;
;; entry: c = bit mask (1 bit on)
;; b = scb byte offset
;;
subtest:
lxi b,submit
getflg:
; return flag value
; exit: zero flag set if flag reset
; c = bit mask
; hl = flag byte address
;
lhld scbaddr
mov l,b
mov a,m
ana c ; a = bit
ret
;
setccpflg:
lxi b,ccp10
;
setflg:
; set flag on (bit = 1)
;
call getflg
mov a,c
ora m
mov m,a
ret
;
resetccpflg:
lxi b,ccp10
;
resetflg:
; reset flag off (bit = 0)
;
call getflg
mov a,c
cma
ana m
mov m,a
ret
;;
;;
;; SET/GET SCB BYTE
;;
;; entry: A = byte ("setbyte" only)
;; B = SCB byte offset from page
;;
;; exit: A = byte ("getbyte" only)
;;
setbyte:
lhld scbaddr
mov l,b
mov m,a
ret
;
getbyte:
lhld scbaddr
mov l,b
mov a,m
ret
;
;;-----------------------------------------------------------------------
;;
;;
;; print message followed by newline
;;
;; entry: de -> message string
;;
pmsgnl: call pmsg
;
; print crlf
;
dirln: mov b,l ;number of columns for DIR
crlf: mvi a,cr
call pfc
mvi a,lf
jmp pfc
;;
;;-----------------------------------------------------------------------
;;
;; print decimal byte
;;
pdb: sui 10
jc pdb2
mvi e,'0'
pdb1: inr e
sui 10
jnc pdb1
push psw
call putc2
pop psw
pdb2: adi 10+'0'
jmp putc
;;-----------------------------------------------------------------------
;;
;;
;; print string terminated by 0 or char in c
;;
pstrg: mov a,m ;get character
ora a
rz
cmp c
rz
call pfc ;print character
inx h ;advance pointer
jmp pstrg ;loop
;;
;;-----------------------------------------------------------------------
;;
;; check for end of command (error if extraneous parameters)
;;
eoc: call skps
rz
;
; handle parser error
;
perror:
lxi h,errflg
mov a,m
ora a ;ignore error????
mvi m,0 ;clear error flag
rnz ;yes...just return to CCPRET
lhld errorp ;get pointer to what we're parsing
mvi c,' '
call pstrg
perr2: mvi a,'?' ;print question mark
call putc
jmp ccpcr
;
;;-----------------------------------------------------------------------
;;
;;
;; print error message and exit processor
;;
;; entry: bc -> error message
;;
;;msgerr: push b
;; call crlf
;; pop d
;; jmp pmsgnl
;;
;;-----------------------------------------------------------------------
;;
;; get decimal number (0 <= N <= 255)
;;
;; exit: a = number
;;
gdn: call skps ;skip initial spaces
lhld parsep ;get pointer to current character
shld errorp ;save in case of parsing error
rz ;return if end of command
mov a,m ;get it
cpi '0' ;error if non-numeric
jc perror
cpi '9'+1
jnc perror
call gdns ;convert number
shld parsep ;save new position
ori 1 ;clear zero and carry flags
mov a,b
ret
;
gdns: mvi b,0
gdns1: mov a,m
sui '0'
rc
cpi 10
rnc
push psw
mov a,b ;multiply current accumulator by 10
add a
add a
add b
add a
mov b,a
pop psw
inx h ;advance to next character
add b ;add it in to the current accumulation
mov b,a
cpi 16
jc gdns1 ;loop unless >=16
jmp perror ;error if invalid user number
;;
;;-----------------------------------------------------------------------
;;
;; print file name
;;
if newdir
pfn: inx d ;point to file name
mvi h,8 ;set # characters to print, clear # printed
call pfn1 ;print name field
call space
mvi h,3 ;set # characters to print
pfn1: ldax d ;get character
ani 7fh
call pfc ;print it if not
inx d ;advance pointer
dcr h ;loop if more to print
jnz pfn1
ret
;
space: mvi a,' '
;
pfc: push b
push d
push h
call putc
pop h
pop d
pop b
ret
else
pfn: inx d ;point to file name
lxi b,8*256 ;set # characters to print, clear # printed
call pfn1 ;print name field
ldax d ;see if there's a type
ani 7fh
cpi ' '
rz ;return if not
mvi a,'.' ;print dot
call pfc
mvi b,3 ;set # characters to print
pfn1: ldax d ;get character
ani 7fh
cpi ' ' ;is it a space?
cnz pfc ;print it if not
inx d ;advance pointer
dcr b ;loop if more to print
jnz pfn1
ret
;
space: mvi a,' '
;
pfc: inr c ;increment # characters printed
push b
push d
call putc
pop d
pop b
ret
endif
;;
;;-----------------------------------------------------------------------
;;
;; add a to hl
;;
addhla: add l
mov l,a
rnc
inr h
ret
;;
;;-----------------------------------------------------------------------
;;
;; set match-any string into fcb
;;
;; entry: de -> fcb area
;; b = # bytes to set
;;
setmatch:
mvi a,'?' ;set match one character
setm1: stax d ;fill rest of field with match one
inx d
dcr b ;loop if more to fill
jnz setm1
ora a
ret
;;
;;-----------------------------------------------------------------------
;;
;; table search
;;
;; Search table of strings separated by spaces and terminated
;; by 0. Accept abbreviations, but set string = matched string
;; on exit so that we don't try to execute abbreviation.
;;
;; entry: de -> string to search for
;; hl -> table of strings to match (terminate table with 0)
;; exit: z = set if match found
;; a = entry # (0 thru n-1)
;; z = not set if no match found
;;
tbls: lxi b,0ffh ;clear entry & entry length counters
tbls0: push d ;save match string addr
push h ;save table string addr
tbls1: ldax d ;compare bytes
ani 7fh ;kill upper bit (so SYS + R/O match)
cpi ' '+1 ;end of search string?
jc tbls2 ;skip compare, if so
cmp m
jnz tbls3 ;jump if no match
tbls2: inx d ;advance string pointer
inr c ;increment entry length counter
mvi a,' '
cmp m
inx h ;advance table pointer
jnz tbls1 ;continue with this entry if more
pop h ;HL = matched string in table
pop d ;DE = string address
call move ; C = length of string in table
mov a,b ;return current entry counter value
ret
;
tbls3: mvi a,' ' ;advance hl past current string
tbls4: cmp m
inx h
jnz tbls4
pop d ;throw away last table address
pop d ;DE = string address
inr b ;increment entry counter
mvi c,0ffh
mov a,m ;check for end of table
sui 1
jnc tbls0 ;loop if more entries to test
ret
;
;************************************************************************
;************************************************************************
;
;************************************************************************
;
; DATA AREA
;
;************************************************************************
; ;Note uninitialized data placed at the end (DS)
;
;
if prompts
enter: db 'Enter $'
unmsg: db 'User #: $'
fnmsg: db 'File: $'
else
unmsg: db 'Enter User #: $'
endif
nomsg: db 'No File$'
required:
db ' required$'
eramsg:
db 'ERASE $'
confirm:
db ' (Y/N)? $'
more: db cr,lf,cr,lf,'Press RETURN to Continue $'
if dayfile
userzero db ' (User 0)$'
endif
;
;
;
if newdir
anyfiles: db 0 ;flag for SYS or DIR files exist
dirfiles: db 'NON-'
sysfiles: db 'SYSTEM FILE(S) EXIST$'
endif
errflg: db 0 ;parse error flag
if multi
multibufl:
dw 0 ;multiple commands buffer length
endif
scbadd: db scbad-pag$off,0
;********** CAUTION FOLLOWING DATA MUST BE IN THIS ORDER *********
pfncb: ;BDOS func 152 (parse filename)
parsep: dw 0 ;pointer to current position in command
pfnfcb: dw pfcb ;.fcb for func 152
usernum: ;CCP current user
db 0
chaindsk:
db 0 ;transient's current disk
disk: db 0 ;CCP current disk
subfcb: db 1,'$$$ SUB',0
ccpend: ;end of file (on disk)
ds 1
submod: ds 1
subrc: ds 1
ds 16
subcr: ds 1
subrr: ds 2
subrr2: ds 1
dircols:
ds 1 ;number of columns for DIR/DIRS
pgsize: ds 1 ;console page size
line: ds 1 ;console line #
pgmode: ds 1 ;console page mode
;*****************************************************************
errorp: ds 2 ;pointer to beginning of current param.
errsav: ds 2 ;pointer to built-in command tail
bufp: ds 1 ;buffer pointer for getb
realdos:
ds 1 ;base page of BDOS
;
option: ds 1 ;'[' in line?
passwd: ds 10 ;password
ufcb: ds 1 ;user number (must procede fcb)
FCB:
ds 1 ; drive code
ds 8 ; file name
ds 3 ; file type
ds 4 ; control info
ds 16 ; disk map
fcbcr: ds 1 ; current record
fcbrr: ds 2 ; random record
pfcb: ds 36 ; fcb for parsing
;
;
;
;
; command line buffer
;
cbufmx: ds 1
cbufl: ds 1
cbuf: ds comlen
ds 50h
stack:
ccptop: ;top page of CCP
end