title 'ccpz version 4.1' ; ;cp/m console command processor (ccp) revision 4.1 ;for z80-based cp/m 2.x systems ; ;******** refer to ccpz-vxx. not file for revision history ******** ; ; ******** structure notes ******** ; ; this ccp is divided into a number of major sections. the following ; is an outline of these sections and the names of the major routines ; located therein. ; ; section function/routines ; ------- ----------------- ; ; -- opening comments, equates, and macro definitions ; ; 0 jmp table into ccp ; ; 1 buffers ; ; 2 ccp starting modules ; ccp1 ccp restrt rstccp rccpnl ; prnnf ; ; 3 utilities ; crlf conout conin lcout lstout ; readf read bdosb printc print ; getdrv defdma dmaset reset bdosjp ; login openf open grbdos close ; searf sear1 searn subkil delete ; write create resetusr getusr setusr ; ; 4 ccp utilities ; setud setu0d ucase redbuf cnvbuf ; break usrnum error sdelm advan ; sblank addah number numerr hexnum ; dirptr slogin dlogin comlog scaner ; cmdser ; ; 5 ccp-resident commands and functions ; 5a dir dirpr fillq ; 5b era ; 5c list ; 5d type pager ; 5e save ; 5f ren ; 5g user ; 5h dfu ; 5i jump ; 5j go ; 5k com callprog errlog errjmp ; 5l get memload prnle ; ; false equ 0 true equ not false ; ; ; customization equates ; ; the following equates may be used to customize this ccp for the user's ; system and integration technique. the following constants are provided: ; ; rel - true if integration is to be done via movcpm ; - false if integration is to be done via ddt and sysgen ; ; base - base address of user's cp/m system (normally 0 for dr version) ; this equate allows easy modification by non-standard cp/m (eg,h89) ; ; ccploc - base page address of ccp; this value can be obtained by running ; the bdosloc program on your system, or by setting the ; msize and biosex equates to the system memory size in ; k-bytes and the "extra" memory required by your bios ; in k-bytes. biosex is zero if your bios is normal size, ; and can be negative if your bios is in prom or in ; non-contiguous memory. ; ; ras - remote-access system; setting this equate to true disables ; certain ccp commands that are considered harmful in a remote- ; access environment; use under remote-access systems (rbbs) for ; security purposes ; rel equ false ;set to true for movcpm integration ; base equ 0 ;base of cp/m system (set for standard cp/m) ; if rel ccploc equ 0 ;movcpm image else ; ; if rel is false, the value of ccploc may be set in one ; of two ways. the first way is to set msize and biosex ; as described above using the following three lines: ; msize equ 64 ;size of mem in k-bytes biosex equ 2 ;extra # k-bytes in bios ccploc equ 3400h+(msize-20-biosex)*1024 ;ccp origin ; ; the second way is to obtain the origin of your current ; ccp using bdsloc or its equivalent, then merely set ccploc ; to that value as as in the following line: ; ;ccploc equ 0bd00h ;fill in with bdosloc supplied value ; ; note that you should only use one method or the other. ; do not define ccploc twice! ; ; the following gives the required offset to load the ccp into the ; cp/m sysgen image through ddt (the roffset command); note that this ; value conforms with the standard value presented in the cp/m reference ; manuals, but it may not necessarily conform with the location of the ; ccp in your cp/m system; several systems (morrow designs, p&t, heath ; org-0 to name a few) have the ccp located at a non-standard address in ; the sysgen image. ; ccpr equ 0980h-ccploc ;ddt load offset endif ; ras equ false ;set to true if ccp is for a remote-access system ; ; ;***note to apple softcard users*** ; ; in their infinite (?) wisdom (???), microsoft decided that the way to ; get a two-column directory display instead of four-column (narrow 40-col ; screen, remember) was to have their bios poke ccp every time it was ; loaded. naturally, that will turn into a random poke on any non-standard ; ccp, like this one. the best way to get this ccp up on the apple is to ; load it into cpm56. com, at location 0e00h in the image. the bios code ; that pokes the ccp can also be modified at that time. the poke is done ; by "sta 0c8b2h", found at 24feh in the cpm56 image. to eliminate the ; poke forever, change the "sta" to "lda" by changing the contents of ; location 24feh from 32h to 3ah. if you want a two-column display, set ; the twocol switch below to a value of true. ; twocol equ false ;true if two col dir instead of four ; ; the following is presented as an option, but is not generally user-customiz- ; able. a basic design choice had to be made in the design of ccpz concerning ; the execution of submit files. the original ccp had a problem in this sense ; in that it always looked for the submit file from drive a: and the submit ; program itself (submit.com) would place the $$$.sub file on the currently ; logged-in drive, so when the user was logged into b: and he issued a submit ; command, the $$$.sub was placed on b: and did not execute because the ccp ; looked for it on a: and never found it. ; ; after much debate it was decided to have ccpz perform the same type of ; function as ccp (look for the $$$.sub file on a:), but the problem with ; submit.com still exists. hence, rgf designed supersub and rlc took his ; supersub and designed sub from it; both programs are set up to allow the ; selection at assembly time of creating the $$$.sub on the logged-in drive ; or on drive a:. ; ; a final definition of the indirect command file ($$$.sub or submit ; file) is presented as follows: ; ; "an indirect command file is one which contains ; a series of commands exactly as they would be ; entered from a cp/m console. the submit command ; (or sub command) reads this files and transforms ; it for processing by the ccpz (the $$$.sub file). ; ccpz will then execute the commands indicated ; exactly as if they were typed at the console." ; ; hence, to permit this to happen, the $$$.sub file must always ; be present on a specific drive, and a: is the choice for said drive. ; with this facility engaged as such, indirect command files like: ; ; dir ; a: ; dir ; ; can be executed, even though the currently logged-in drive is changed ; during execution. if the $$$.sub file was present on the currently ; logged-in drive, the above series of commands would not work since the ; ccpz would be looking for $$$.sub on the logged-in drive, and switching ; logged-in drives without moving the $$$.sub file as well would cause ; processing to abort. ; suba equ true ;set to true to have $$$.sub always on a: ;set to false to have $$$.sub on the logged-in drive ; ; the following flag enables extended processing for user-program supplied ; command lines. this is for command level 3 of ccpz. under the ccpz version ; 4.0 philosophy, three command levels exist: ; ; (1) that command issued by the user from his console at the '>' prompt ; (2) that command issued by a $$$.sub file at the '$' prompt ; (3) that command issued by a user program by placing the command into ; cibuff and setting the character count in cbuff ; ; setting clevel3 to true enables extended processing of the third level of ; ccpz command. all the user program need do is to store the command line and ; set the character count; ccpz will initialize the pointers properly, store ; the ending zero properly, and capitalize the command line for processing. ; once the command line is properly stored, the user executes the command line ; by reentering the ccpz through ccploc [note: the c register must contain ; a valid user/disk flag (see location 4) at this time.] ; clevel3 equ true ;enable command level 3 processing ; ; ; *** terminal and 'type' customization equates ; nlines equ 24 ;number of lines on crt screen wide equ true ;true if wide dir display fence equ '|' ;sep char between dir files ; pgdflt equ true ;set to false to disable paging by default pgdflg equ 'P' ;for type command: page or not (dep on pgdflt) ;this flag reverses the default effect ; maxusr equ 15 ;maximum user number accessable ; sysflg equ 'A' ;for dir command: list $sys and $dir soflg equ 'S' ;for dir command: list $sys files only supres equ true ;supresses user # report for user 0 defusr equ 0 ;default user number for com files sprmpt equ '$' ;ccp prompt indicating submit command cprmpt equ '>' ;ccp prompt indicating user command numbase equ 'H' ;character used to switch from default ;number base sectflg equ 'S' ;option char for save command to save sectors ; ; end of customization section ; cr equ 0dh lf equ 0ah tab equ 09h ; wboot equ base+0000h ;cp/m warm boot address udflag equ base+0004h ;user num in high nybble, disk in low bdos equ base+0005h ;bdos function call entry pt tfcb equ base+005ch ;default fcb buffer tbuff equ base+0080h ;default disk i/o buffer tpa equ base+0100h ;base of tpa ; ; ; macros to provide z80 extensions ; maclib z80 $*macro ;first turn off the expansions ; ; end of z80 macro extensions ; ; ; ; **** section 0 **** ; org ccploc ; ;entry points into ccpz ; ;if the ccpz is entered at location ccploc (at the jmp to ccp), then ; the default command in cibuff will be processed. if the ccpz is entered ; at location ccploc+3 (at the jmp to ccp1), then the default command in ; cibuff will not be processed. ; ;note: entry into ccpz in this way is permitted under ccpz version 4.0, ; but in order for this to work, cibuff and cbuff must be initialized properly ; and the c register must contain a valid user/disk flag (see location 4: the ; most significant nybble contains the user number and the least significant ; nybble contains the disk number). ; ;some user programs (such as synonym3) attempt to use the default ; command facility. under the original ccp, it was necessary to initialize ; the pointer after the reserved space for the command buffer to point to ; the first byte of the command buffer. under version 4.x of ccpz, this is ; no longer the case. the cibptr (command input buffer pointer) is located ; to be compatible with such programs (provided they determine the buffer ; length from the byte at mbuff [ccploc + 6]), but under version 4.x of ccpz ; this is no longer necessary. ccpz version 4.x automatically initializes ; this buffer pointer in all cases. ; entry: jmp ccp ;process potential default command jmp ccp1 ;do not process potential default command ; ; ; ; **** section 1 **** ; ; buffers et al ; ; input command line and default command ; ; the command line to be executed is stored here. this command line ; is generated in one of three ways: ; ; (1) by the user entering it through the bdos readln function at ; the du> prompt [user input from keyboard] ; (2) by the submit file facility placing it there from a $$$.sub ; file ; (3) by an external program or user placing the required command ; into this buffer ; ; in all cases, the command line is placed into the buffer starting at ; cibuff. this command line is terminated by the last character (not carriage ; return), and a character count of all characters in the command line ; up to and including the last character is placed into location cbuff ; (immediately before the command line at cibuff). the placed command line ; is then parsed, interpreted, and the indicated command is executed. ; if clevel3 is permitted, a terminating zero is placed after the command ; (otherwise the user program has to place this zero) and the cibptr is ; properly initialized (otherwise the user program has to init this ptr). ; if the command is placed by a user program, entering at ccploc is enough ; to have the command processed. again, under ccpz version 4.x, it is not ; necessary to store the pointer to cibuff in cibptr; ccpz will do this for ; the calling program if clevel3 is made true. ; ; warning: the command line must not exceed buflen characters in length. ; for user programs which load this command, the value of buflen can be ; obtained by examining the byte at mbuff (ccploc + 6). ; buflen equ 80 ;maximum buffer length mbuff db buflen ;maximum buffer length cbuff db 0 ;number of valid chars in command line cibuff db ' ' ;default (cold boot) command cibuf db 0 ;command string terminator db 'ccpz-v4.1' ;for dump identification ds buflen-($-cibuff)+1 ;total is 'buflen' bytes ; cibptr dw cibuff ;pointer to command input buffer ciptr dw cibuf ;ptr to curr cmd for error reporting ; ds 26 ;stack area stack equ $ ;top of stack ; ; file type for command ; commsg db 'COM' ; ; submit file control block ; subfcb equ $ if suba ;if $$$.sub on a: db 1 ;disk name set to default to drive a: else db 0 ;disk name set to default to current drive endif ; db '$$$' ;file name db ' ' db 'SUB' ;file type db 0 ;extent number db 0 ;s1 subfs2 ds 1 ;s2 subfrc ds 1 ;record count ds 16 ;disk group map subfcr ds 1 ;current record number ; ; command file control block ; fcbdn ds 1 ;disk name fcbfn ds 8 ;file name fcbft ds 3 ;file type ds 1 ;extent number ds 2 ;s1 and s2 ds 1 ;record count fcbdm ds 16 ;disk group map fcbcr ds 1 ;current record number ; ; other buffers ; pagcnt db nlines-2 ;lines left on page chrcnt db 0 ;char count for type ; ; ccp built-in command table ; nchars equ 4 ;number of chars/command ; ; ccp command name table ; each table entry is composed of the 4-byte command and 2-byte address ; cmdtbl db 'DIR ' dw dir db 'LIST' dw list db 'TYPE' dw type db 'USER' dw user db 'DFU ' dw dfu ; if not ras ;for non-ras db 'GO ' dw go db 'ERA ' dw era db 'SAVE' dw save db 'REN ' dw ren db 'GET ' dw get db 'JUMP' dw jump endif ; ncmnds equ ($-cmdtbl)/(nchars+2) ; ; ; ; **** section 2 **** ; ccp starting points ; ; start ccp and don't process default command stored ; ccp1: xra a ;set no default command sta cbuff ; ; start ccp and possibly process default command ; ; note on modification by rgf: bdos returns 0ffh in ; accumulator whenever it logs in a directory, if any ; file name contains a '$' in it.this is now used as ; a clue to determine whether or not to do a search ; for submit file, in order to eliminate wasteful searches. ; ccp: lxi sp,stack ;reset stack push b mov a,c ;c=user/disk number (see loc 4) rar ;extract user number rar rar rar ani 0fh mov e,a ;set user number call setusr call reset ;reset disk system sta rngsub ;save submit clue from drive a: pop b mov a,c ;c=user/disk number (see loc 4) ani 0fh ;extract default disk drive sta tdrive ;set it jrz nolog ;skip if 0...already logged call login ;log in default disk ; if not suba ;if $$$.sub is on current drive sta rngsub ;bdos '$' clue endif ; nolog: lxi d,subfcb ;check for $$$.sub on current disk ; rngsub equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte (immediate arg) is the rngsub flag ora a ;set flags on clue cma ;prepare for coming 'cma' cnz sear1 cma ;0ffh is returned if no $$$.sub, so complement sta rngsub ;set flag (0=no $$$.sub) lda cbuff ;execute default command? ora a ;0=no jrnz rs1 ; ; prompt user and input command line from him ; restrt: lxi sp,stack ;reset stack ; ; print prompt (du>) ; call crlf ;print prompt call getdrv ;current drive is part of prompt adi 'A' ;convert to ascii a-p call conout call getusr ;get user number ; if supres ;if suppressing usr # report for usr 0 ora a jrz rs000 endif ; cpi 10 ;user < 10? jrc rs00 sui 10 ;subtract 10 from it push psw ;save it mvi a,'1' ;output 10's digit call conout pop psw ; rs00: adi '0' ;output 1's digit (convert to ascii) call conout ; ; read input line from user or $$$.sub ; rs000: call redbuf ;input command line from user (or $$$.sub) ; ; process input line ; rs1 equ $ ; if clevel3 ;if third command level is permitted call cnvbuf ;capitalize command line, place ending 0, ;and set cibptr value endif ; call defdma ;set tbuff to dma address call getdrv ;get default drive number sta tdrive ;set it call scaner ;parse command name from command line cnz error ;error if command name contains a '?' lxi d,rstccp ;put return address of command push d ;on the stack lda tempdr ;is command of form 'd:command'? ora a ;nz=yes jnz com ;immediately call cmdser ;scan for ccp-resident command jnz com ;not ccp-resident mov a,m ;found it: get low-order part inx h ;get high-order part mov h,m ;store high mov l,a ;store low pchl ;execute ccp routine ; ; entry point for restarting ccp and logging in default drive ; rstccp: call dlogin ;log in default drive ; ; entry point for restarting ccp without logging in default drive ; rccpnl: call scaner ;extract next token from command line lda fcbfn ;get first char of token sui ' ' ;any char? lxi h,tempdr ora m jnz error jr restrt ; ; no file error message ; prnnf: call printc ;no file message db 'No fil','e'+80h ret ; ; ; ; **** section 3 **** ; i/o utilities ; ; output char in reg a to console and don't change bc ; ; output ; crlf: mvi a,cr call conout mvi a,lf ;fall thru to conout ; conout: push b mvi c,02h ; output: mov e,a push h call bdos pop h pop b ret ; conin: mvi c,01h ;get char from con: with echo jr bdosb ; lcout: push psw ;output char to con: or lst: dep on prflg ; prflg equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte (immediate arg) is the print flag ora a ;0=type jrz lc1 pop psw ;get char ; ; output char in reg a to list device ; lstout: push b mvi c,05h jr output ; lc1: pop psw ;get char push psw call conout ;output to con: pop psw cpi lf ;check for paging jz pager ret ; readf: lxi d,fcbdn ;fall thru to read ; read: mvi c,14h ;fall thru to bdosb ; ; call bdos and save bc ; bdosb: push b call bdos pop b ora a ret ; ; print string (ending in 0) pted to by ret adr;start with ; printc: push psw ;save flags call crlf ;new line pop psw ; print: xthl ;get ptr to string push psw ;save flags call prin1 ;print string pop psw ;get flags xthl ;restore hl and ret adr ret ; ; print string (ending in 0) pted to by hl ; prin1: mov a,m ;get next byte call conout ;print char mov a,m ;get next byte again for test inx h ;pt to next byte ora a ;set flags rz ;done if zero rm ;done if msb set jr prin1 ; ; bdos function routines ; ; return number of current disk in a ; getdrv: mvi c,19h jr bdosjp ; ; set 80h as dma address ; defdma: lxi d,tbuff ;80h=tbuff ; dmaset: mvi c,1ah jr bdosjp ; reset: mvi c,0dh ; bdosjp: jmp bdos ; login: mov e,a mvi c,0eh jr bdosjp ;save some code space ; openf: xra a sta fcbcr lxi d,fcbdn ;fall thru to open ; open: mvi c,0fh ;fall thru to grbdos ; grbdos: call bdos inr a ;set zero flag for error return ret ; close: mvi c,10h jr grbdos ; searf: lxi d,fcbdn ;specify fcb ; sear1: mvi c,11h jr grbdos ; searn: mvi c,12h jr grbdos ; ; check for submit file in execution and abort it if so ; subkil: lxi h,rngsub ;check for submit file in execution mov a,m ora a ;0=no rz mvi m,0 ;abort submit file lxi d,subfcb ;delete $$$.sub ; delete: mvi c,13h jr bdosjp ;save more space ; write: mvi c,15h jmp bdosb ; create: mvi c,16h jr grbdos ; ; reset user number if changed ; resetusr equ $ ; tmpusr equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte (immediate arg) is tmpusr mov e,a ;place in e jr setusr ;then go set user getusr: mvi e,0ffh ;get current user number ; setusr: mvi c,20h ;set user number to value in e (get if e=ffh) jr bdosjp ;more space saving ; ; end of bdos functions ; ; ; ; **** section 4 **** ; ccp utilities ; ; set user/disk flag to current user and default disk ; setud: call getusr ;get number of current user add a ;place it in high nybble add a add a add a lxi h,tdrive ;mask in default drive number (low nybble) ora m ;mask in sta udflag ;set user/disk number ret ; ; set user/disk flag to user 0 and default disk ; setu0d equ $ ; tdrive equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte (immediate arg) is tdrive sta udflag ;set user/disk number ret ; ; convert char in a to upper case ; ucase: cpi 61h ;lower-case a rc cpi 7bh ;greater than lower-case z? rnc ani 5fh ;capitalize ret ; ; input next command to ccp ; this routine determines if a submit file is being processed ; and extracts the command line from it if so or from the user's console ; redbuf: lda rngsub ;submit file currently in execution? ora a ;0=no jrz rb1 ;get line from console if not lxi d,subfcb ;open $$$.sub push d ;save de call open pop d ;restore de jrz rb1 ;erase $$$.sub if end of file and get cmnd lda subfrc ;get value of last record in file dcr a ;pt to next to last record sta subfcr ;save new value of last record in $$$.sub call read ;de=subfcb jrnz rb1 ;abort $$$.sub if error in reading last rec lxi d,cbuff ;copy last record (next submit cmnd) to cbuff lxi h,tbuff ;from tbuff lxi b,buflen ;number of bytes ldir lxi h,subfs2 ;pt to s2 of $$$.sub fcb mvi m,0 ;set s2 to zero inx h ;pt to record count dcr m ;decrement record count of $$$.sub lxi d,subfcb ;close $$$.sub call close jrz rb1 ;abort $$$.sub if error mvi a,sprmpt ;print submit prompt call conout lxi h,cibuff ;print command line from $$$.sub call prin1 call break ;check for abort (any char) ; if clevel3 ;if third command level is permitted rz ;if (no abort), return to caller and run else ;if third command level is not permitted jrz cnvbuf ;if (no abort), capitalize command endif ; call subkil ;kill $$$.sub if abort jmp restrt ;restart ccp ; ; input command line from user console ; rb1: call subkil ;erase $$$.sub if present call setud ;set user and disk mvi a,cprmpt ;print prompt call conout mvi c,0ah ;read command line from user lxi d,mbuff call bdos ; if clevel3 ;if third command level is permitted jmp setu0d ;set current disk number in lower params else ;if third command level is not permitted call setu0d ;set current disk number if lower params ;and fall thru to cnvbuf endif ; ; capitalize string (ending in 0) in cbuff and set ptr for parsing ; cnvbuf: lxi h,cbuff ;pt to user's command mov b,m ;char count in b inr b ;add 1 in case of zero ; cb1: inx h ;pt to 1st valid char mov a,m ;capitalize command char call ucase mov m,a djnz cb1 ;continue to end of command line ; cb2: mvi m,0 ;store ending lxi h,cibuff ;set command line ptr to 1st char shld cibptr ret ; ; check for any char from user console;ret w/zero set if none ; break: push d ;save de mvi c,11 ;csts check call bdosb cnz conin ;get input char ; brkbk: pop d ret ; ; get the requested user number from the command line and validate it. ; usrnum: call number cpi maxusr+1 rc ; ; invalid command -- print it ; error: call crlf ;new line lhld ciptr ;pt to beginning of command line ; err2: mov a,m ;get char cpi ' '+1 ;simple '?' if or less jrc err1 push h ;save ptr to error command char call conout ;print command char pop h ;get ptr inx h ;pt to next jr err2 ;continue ; err1: call print ;print '?' db '?'+80h call subkil ;terminate active $$$.sub if any jmp restrt ;restart ccp ; ; check to see if de pts to delimiter; if so, ret w/zero flag set ; sdelm: ldax d ora a ;0=delimiter rz cpi ' ' ;error if < jrc error rz ;=delimiter cpi '=' ;'='=delimiter rz cpi 5fh ;underscore=delimiter rz cpi '.' ;'.'=delimiter rz cpi ':' ;':'=delimiter rz cpi ';' ;';'=delimiter rz cpi '<' ;'<'=delimiter rz cpi '>' ;'>'=delimiter ret ; ; advance input ptr to first non-blank and fall through to sblank ; advan: lded cibptr ; ; skip string pted to by de (string ends in 0) until end of string ; or non-blank encountered (beginning of token) ; sblank: ldax d ora a rz cpi ' ' rnz inx d jr sblank ; ; add a to hl (hl=hl+a) ; addah: add l mov l,a rnc inr h ret ; ; extract decimal number from command line ; return with value in reg a;all registers may be affected ; number: call scaner ;parse number and place in fcbfn lxi h,fcbfn+10 ;pt to end of token for conversion mvi b,11 ;11 chars max ; ; check for suffix for hexadecimal number ; nums: mov a,m ;get chars from end, searching for suffix dcx h ;back up cpi ' ' ;space? jrnz nums1 ;check for suffix djnz nums ;count down jr num0 ;by default, process ; nums1: cpi numbase ;check against base switch flag jrz hnum0 ; ; process decimal number ; num0: lxi h,fcbfn ;pt to beginning of token lxi b,1100h ;c=accumulated value, b=char count ; (c=0, b=11) ; num1: mov a,m ;get char cpi ' ' ;done if jrz num2 inx h ;pt to next char sui '0' ;convert to binary (ascii 0-9 to binary) cpi 10 ;error if >= 10 jrnc numerr mov d,a ;digit in d mov a,c ;new value = old value * 10 rlc rlc rlc add c ;check for range error jrc numerr add c ;check for range error jrc numerr add d ;new value = old value * 10 + digit jrc numerr ;check for range error mov c,a ;set new value djnz num1 ;count down ; ; return from number ; num2: mov a,c ;get accumulated value ret ; ; number error routine for space conservation ; numerr: jmp error ;use error routine - this is relative pt ; ; extract hexadecimal number from command line ; return with value in reg a; all registers may be affected ; hexnum: call scaner ;parse number and place in fcbfn ; hnum0: lxi h,fcbfn ;pt to token for conversion lxi d,0 ;de=accumulated value mvi b,11 ;b=char count ; hnum1: mov a,m ;get char cpi ' ' ;done? jrz hnum3 ;return if so cpi 'H' ;done if h suffix jrz hnum3 sui '0' ;convert to binary jrc numerr ;return and done if error cpi 10 ;0-9? jrc hnum2 sui 7 ;a-f? cpi 10h ;error? jrnc numerr ; hnum2: inx h ;pt to next char mov c,a ;digit in c mov a,d ;get accumulated value rlc ;exchange nybbles rlc rlc rlc ani 0f0h ;mask out low nybble mov d,a mov a,e ;switch low-order nybbles rlc rlc rlc rlc mov e,a ;high nybble of e=new high of e, ;low nybble of e=new low of d ani 0fh ;get new low of d ora d ;mask in high of d mov d,a ;new high byte in d mov a,e ani 0f0h ;mask out low of e ora c ;mask in new low mov e,a ;new low byte in e djnz hnum1 ;count down ; ; return from hexnum ; hnum3: xchg ;returned value in hl mov a,l ;low-order byte in a ret ; ; pt to directory entry in tbuff whose offset is specified by a and c ; dirptr: lxi h,tbuff ;pt to temp buffer add c ;pt to 1st byte of dir entry call addah ;pt to desired byte in dir entry mov a,m ;get desired byte ret ; ; check for specified drive and log it in if not default ; slogin: xra a ;set fcbdn for default drive sta fcbdn call comlog ;check drive rz jr dlog5 ;do login otherwise ; ; check for specified drive and log in default drive if specified<>default ; dlogin: call comlog ;check drive rz ;abort if same lda tdrive ;log in default drive ; dlog5: jmp login ; ; routine common to both login routines; on exit, z set means abort ; comlog equ $ ; tempdr equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte (immediate arg) is tempdr ora a ;0=no rz dcr a ;compare it against default lxi h,tdrive cmp m ret ;abort if same ; ; extract token from command line and place it into fcbdn; ; format fcbdn fcb if token resembles file name and type (filename.typ); ; on input, cibptr pts to char at which to start scan; ; on output, cibptr pts to char at which to continue and zero flag is reset ; if '?' is in token ; scaner: xra a ;a=0 to start at drive specification byte ; scan1: lxi h,fcbdn ;point to fcbdn call addah ;offset into fcb push h push h xra a ;set temporary drive number to default sta tempdr call advan ;skip to non-blank or end of line sded ciptr ;set ptr to non-blank or end of line pop h ;get ptr to next byte in fcbdn ldax d ;end of line? ora a ;0=yes jrz scan2 sbi 'A'-1 ;convert possible drive spec to number mov b,a ;store number (a:=0, b:=1, etc) in b inx d ;pt to next char ldax d ;see if it is a colon (:) cpi ':' jrz scan3 ;yes, we have a drive spec dcx d ;no, back up ptr to first non-blank char ; scan2: lda tdrive ;set 1st byte of fcbdn as default drive mov m,a jr scan4 ; scan3: mov a,b ;we have a drive spec sta tempdr ;set temporary drive mov m,b ;set 1st byte of fcbdn as specified drive inx d ;pt to byte after ':' ; ; extract filename from possible filename.typ ; scan4: mvi b,8 ;max of 8 chars in file name ; scan5: call sdelm ;done if delimiter encountered - fill jrz scan9 inx h ;pt to next byte in fcbdn cpi '*' ;is (de) a wild card? jrnz scan6 ;continue if not mvi m,'?' ;place '?' in fcbdn and don't advance de if so jr scan7 ; scan6: mov m,a ;store filename char in fcbdn inx d ;pt to next char in command line ; scan7: djnz scan5 ;decrement char count until 8 elapsed ; scan8: call sdelm ;8 chars or more - skip until delimiter jrz scan10 ;zero flag set if delimiter found inx d ;pt to next char in command line jr scan8 ; scan9: inx h ;pt to next byte in fcbdn mvi m,' ' ;fill filename part with djnz scan9 ; ; extract file type from possible filename.typ ; scan10: mvi b,3 ;prepare to extract type cpi '.' ;if (de) delimiter is a '.', we have a type jrnz scan15 ;fill file type bytes with inx d ;pt to char in command line after '.' ; scan11: call sdelm ;check for delimiter jrz scan15 ;fill rest of type if it is a delimiter inx h ;pt to next byte in fcbdn cpi '*' ;wild? jrnz scan12 ;store char if not wild mvi m,'?' ;store '?' and don't advance command line ptr jr scan13 ; scan12: mov m,a ;store char in fcbdn inx d ;pt to next char in command line ; scan13: djnz scan11 ;count down chars in file type (3 max) ; scan14: call sdelm ;skip rest of chars after 3-char type to jrz scan16 ;delimiter inx d jr scan14 ; scan15: inx h ;fill in rest of typ with mvi m,' ' djnz scan15 ; ; fill in ex, s1, s2, and rc with zeroes ; scan16: mvi b,4 ;4 bytes ; scan17: inx h ;pt to next byte in fcbdn mvi m,0 djnz scan17 ; ; scan complete -- de pts to delimiter byte after token ; sded cibptr ; ; set zero flag to indicate presence of '?' in filename.typ ; pop h ;get ptr to fcbdn in hl lxi b,11 ;scan for '?' in filename.typ (c=11 bytes) ; scan18: inx h ;pt to next byte in fcbdn mov a,m cpi '?' jrnz scan19 inr b ;b<>0 to indicate '?' encountered ; scan19: dcr c ;count down jrnz scan18 mov a,b ;a=b=number of '?' in filename.typ ora a ;set zero flag to indicate any '?' ret ; ; cmdtbl (command table) scanner ; on return, hl pts to address of command if ccp-resident ; on return, zero flag set means ccp-resident command ; cmdser: lxi h,cmdtbl ;pt to command table mvi c,ncmnds ;set command counter ; cms1: lxi d,fcbfn ;pt to stored command name mvi b,nchars ;number of chars/command (8 max) ; cms2: ldax d ;compare against table entry cmp m jrnz cms3 ;no match inx d ;pt to next char inx h djnz cms2 ;count down ldax d ;next char in input command must be cpi ' ' jrnz cms4 ret ;command is ccp-resident (zero flag set) ; cms3: inx h ;skip to next command table entry djnz cms3 ; cms4: inx h ;skip address inx h dcr c ;decrement table entry number jrnz cms1 inr c ;clear zero flag ret ;command is disk-resident (zero flag clear) ; ; ; ; **** section 5 **** ; ccp-resident commands ; ; ; ;section 5a ;command: dir ;function: to display a directory of the files on disk ;forms: ; dir displays the dir files ; dir s displays the sys files ; dir a display both dir and sys files ; dir: mvi a,80h ;set system bit examination push psw call scaner ;extract possible d:filename.typ token call slogin ;log in drive if necessary lxi h,fcbfn ;make fcb wild (all '?') if no filename.typ mov a,m ;get first char of filename.typ cpi ' ' ;if , all wild cz fillq call advan ;look at next input char mvi b,0 ;sys token default jrz dir2 ;jump; there isn't one cpi sysflg ;system flag specifier? jrz gotsys ;got system specifier cpi soflg ;sys only? jrnz dir2 mvi b,80h ;flag sys only ; gotsys: inx d sded cibptr cpi soflg ;sys only spec? jrz dir2 ;then leave bit spec unchagned pop psw ;get flag xra a ;set no system bit examination push psw ; dir2: pop psw ;get flag ; dir2a: ;drop into dirpr to print directory ; then restart ccp ; ; directory print routine; on entry, msb of a is 1 (80h) if system files excl ; dirpr: mov d,a ;store system flag in d mvi e,0 ;set column counter to zero push d ;save column counter (e) and system flag (d) mov a,b ;sys only specifier sta systst call searf ;search for specified file (first occurrance) cz prnnf ;print no file msg;reg a not changed ; ; entry selection loop; on entry, a=offset from searf or searn ; dir3: jrz dir11 ;done if zero flag set dcr a ;adjust to returned value rrc ;convert number to offset into tbuff rrc rrc ani 60h mov c,a ;offset into tbuff in c (c=offset to entry) mvi a,10 ;add 10 to pt to system file attribute bit call dirptr pop d ;get system bit mask from d push d ana d ;mask for system bit ; systst equ $+1 ;pointer to in-the-code buffer systst cpi 0 jrnz dir10 pop d ;get entry count (= counter) mov a,e ;add 1 to it inr e push d ;save it ; if twocol ani 01h ;output if 2 entries printed in line else ani 03h ;output if 4 entries printed in line endif ; push psw jrnz dir4 call crlf ;new line jr dir5 ; dir4: call print ; if wide db ' ' ;2 spaces db fence ;then fence char db ' ',' '+80h ;then 2 more spaces else db ' ' ;space db fence ;then fence char db ' '+80h ;then space endif ; dir5: mvi b,01h ;pt to 1st byte of file name ; dir6: mov a,b ;a=offset call dirptr ;hl now pts to 1st byte of file name ani 7fh ;mask out msb cpi ' ' ;no file name? jrnz dir8 ;print file name if present pop psw push psw cpi 03h jrnz dir7 mvi a,09h ;pt to 1st byte of file type call dirptr ;hl now pts to 1st byte of file type ani 7fh ;mask out msb cpi ' ' ;no file type? jrz dir9 ;continue if so ; dir7: mvi a,' ' ;output ; dir8: call conout ;print char inr b ;incr char count mov a,b cpi 12 ;end of filename.typ? jrnc dir9 ;continue if so cpi 09h ;end if filename only? jrnz dir6 ;print typ if so mvi a,'.' ;print dot between file name and type call conout jr dir6 ; dir9: pop psw ; dir10: call break ;check for abort jrnz dir11 call searn ;search for next file jr dir3 ;continue ; dir11: pop d ;restore stack ret ; ; fill fcb @hl with '?' ; fillq: mvi b,11 ;number of chars in fn & ft ; fqlp: mvi m,'?' ;store '?' inx h djnz fqlp ret ; ; ; ;section 5b ;command: era ;function: erase files ;forms: ; era erase specified files and print their names ; if not ras ;not for remote-access system ; era: call scaner ;parse file specification cpi 11 ;all wild (all files = 11 '?')? jrnz era1 ;if not, then do erases call printc db 'All','?'+80h call conin ;get reply call ucase ;capitalize cpi 'Y' ;yes? jnz restrt ;restart ccp if not call crlf ;new line ; era1: call slogin ;log in selected disk if any xra a ;print all files (examine system bit) mov b,a ;no sys-only opt to dirpr call dirpr ;print directory of erased files lxi d,fcbdn ;delete file specified call delete ret ;reenter ccp ; endif ; ; ; ;section 5c ;command: list ;function: print out specified file on the lst: device ;forms: ; list print file (no paging) ; list: mvi a,0ffh ;turn on printer flag jr type0 ; ; ; ;section 5d ;command: type ;function: print out specified file on the con: device ;forms: ; type print file ; type p print file with paging flag ; type: xra a ;turn off printer flag ; ; entry point for ccp list function (list) ; type0: sta prflg ;set flag call scaner ;extract filename.typ token jnz error ;error if any question marks call advan ;get pgdflg if it's there sta pgflg ;save it as a flag jrz noslas ;jump if input ended inx d ;put new buf pointer xchg shld cibptr ; noslas: call slogin ;log in selected disk if any call openf ;open selected file jz type4 ;abort if error call crlf ;new line mvi a,nlines-1 ;set line count sta pagcnt lxi h,chrcnt ;set char position/count mvi m,0ffh ;empty line mvi b,0 ;set tab char counter ; type1: lxi h,chrcnt ;pt to char position/count mov a,m ;end of buffer? cpi 80h jrc type2 push h ;read next block call readf pop h jrnz type3 ;error? xra a ;reset count mov m,a ; type2: inr m ;increment char count lxi h,tbuff ;pt to buffer call addah ;compute address of next char from offset mov a,m ;get next char ani 7fh ;mask out msb cpi 1ah ;end of file (^z)? rz ;restart ccp if so ; ; output char to con: or lst: device with tabulation ; cpi cr ;reset tab count? jrz tabrst cpi lf ;reset tab count? jrz tabrst cpi tab ;tab? jrz ltab call lcout ;output char inr b ;increment char count jr type2l ; tabrst: call lcout ;output or mvi b,0 ;reset tab counter jr type2l ; ltab: mvi a,' ' ; call lcout inr b ;incr pos count mov a,b ani 7 jrnz ltab ; ; continue processing ; type2l: call break ;check for abort jrz type1 ;continue if no char cpi 'C'-'@' ;^c? rz ;restart if so jr type1 ; type3: dcr a ;no error? rz ;restart ccp ; type4: jmp errlog ; ; paging routines ; pager counts down lines and pauses for input (direct) if count expires ; pagset sets lines/page count ; pager: push h lxi h,pagcnt ;count down dcr m jrnz pgbak ;jump if not end of page mvi m,nlines-2 ;refill counter ; pgflg equ $+1 ;pointer to in-the-code buffer pgflg mvi a,0 ;0 may be changed by pgflg equate cpi pgdflg ;page default override option wanted? ; if pgdflt ;if paging is default jrz pgbak ;pgdflg means no paging, please else ;if paging not default jrnz pgbak ;pgdflg means please paginate endif ; call conin ;get char to continue cpi 'C'-'@' ;^c jz rstccp ;restart ccp ; pgbak: pop h ;restore hl ret ; ; ; ;section 5e ;command: save ;function: to save the contents of the tpa onto disk as a file ;forms: ; save ; save specified number of pages (start at 100h) ; from tpa into specified file; is in dec ; save s ; like save above, but numeric argument specifies ; number of sectors rather than pages ; if not ras ;not for remote-access system ; save: call number ;extract number from command line push psw ;save it call scaner ;extract filename.type jnz error ;must be no '?' in it call slogin ;log in selected disk lxi d,fcbdn ;delete file in case it already exists push d call delete pop d call create ;make new file jrz save3 ;error? xra a ;set record count field of new file's fcb sta fcbcr pop psw ;get page count mov l,a ;hl=page count mvi h,0 push h call advan ;look for 's' for sector option inx d ;pt to after 's' token pop h cpi sectflg jrz save0 dcx d ;no 's' token, so back up dad h ;double it for hl=sector (128 bytes) count ; save0: sded cibptr ;set ptr to bad token or after good token lxi d,tpa ;pt to start of save area (tpa) ; save1: mov a,h ;done with save? ora l ;hl=0 if so jrz save2 dcx h ;count down on sectors push h ;save ptr to block to save lxi h,128 ;128 bytes per sector dad d ;pt to next sector push h ;save on stack call dmaset ;set dma address for write (address in de) lxi d,fcbdn ;write sector call write pop d ;get ptr to next sector in de pop h ;get sector count jrnz save3 ;write error? jr save1 ;continue ; save2: lxi d,fcbdn ;close saved file call close inr a ;error? jrnz save4 ; save3: call prnle ;print 'no space' error ; save4: call defdma ;set dma to 0080 ret ;restart ccp ; endif ; ; ; ;section 5f ;command: ren ;function: to change the name of an existing file ;forms: ; ren = perform function ; if not ras ;not for remote-access system ; ren: call scaner ;extract file name jnz error ;error if any '?' in it lda tempdr ;save current default disk push psw call slogin ;log in selected disk call searf ;look for specified file jrz ren0 ;continue if not found call printc db 'File exist','s'+80h pop psw ;clear stack ret ;restart ccp ; ren0: lxi h,fcbdn ;save new file name lxi d,fcbdm lxi b,16 ;16 bytes ldir call advan ;advance cibptr cpi '=' ;'=' ok jrnz ren4 ; ren1: xchg ;pt to char after '=' in hl inx h shld cibptr ;save ptr to old file name call scaner ;extract filename.typ token jrnz ren4 ;error if any '?' pop psw ;get old default drive mov b,a ;save it lxi h,tempdr ;compare it against current default drive mov a,m ;match? ora a jrz ren2 cmp b ;check for drive error mov m,b jrnz ren4 ; ren2: mov m,b xra a sta fcbdn ;set default drive lxi d,fcbdn ;rename file mvi c,17h ;bdos rename fct call grbdos rnz ; ren3: call prnnf ;print no file msg ; ren4: jmp errlog ; endif ; ; ; ;section 5g ;command: user ;function: change current user number ;forms: ; user select specified user number; is in dec ; user: call usrnum ;extract user number from command line mov e,a ;place user number in e call setusr ;set specified user ; rstjmp: jmp rccpnl ;restart ccp ; ; ; ;section 5h ;command: dfu ;function: set the default user number for the command/file scanner (memload) ;forms: ; dfu select default user number; is in dec ; dfu: call usrnum ;get user number sta dfusr ;put it away jr rstjmp ;restart ccp (no default login) ; ; ; ;section 5i ;command: jump ;function: to call the program (subroutine) at the specified address ; without loading from disk ;forms: ; jump call at ; is in hex ; if not ras ;not for remote-access system ; jump: call hexnum ;get load address in hl jr callprog ;perform call ; endif ; ; ; ;section 5j ;command: go ;function: to call the program in the tpa without loading ; loading from disk. same as jump 100h, but much ; more convenient, especially when used with ; parameters for programs like stat. also can be ; allowed on remote-access systems with no problems. ; ;form: ; go ; if not ras ;only if ras ; go: lxi h,tpa ;always to tpa jr callprog ;perform call ; endif ; ; ; ;section 5k ;command: com file processing ;function: to load the specified com file from disk and execute it ;forms: ; ; com: lda fcbfn ;any command? cpi ' ' ;' ' means command was 'd:' to switch jrnz com1 ;not , so must be transient or error lda tempdr ;look for drive spec ora a ;if zero, just blank jz rccpnl dcr a ;adjust for log in sta tdrive ;set default drive call setu0d ;set drive with user 0 call login ;log in drive jmp rccpnl ;restart ccp ; com1: lda fcbft ;file type must be blank cpi ' ' jnz error lxi h,commsg ;place default file type (com) into fcb lxi d,fcbft ;copy into file type lxi b,3 ;3 bytes ldir lxi h,tpa ;set execution/load address push h ;save for execution call memload ;load memory with file specified in cmd line ;(no return if error or too big) pop h ;get execution address ; ; callprog is the entry point for the execution of the loaded ; program;on entry to this routine, hl must contain the execution ; address of the program (subroutine) to execute ; callprog: shld execadr ;perform in-line code modification call dlogin ;log in default drive call scaner ;search command line for next token lxi h,tempdr ;save ptr to drive spec push h mov a,m ;set drive spec sta fcbdn mvi a,10h ;offset for 2nd file spec call scan1 ;scan for it and load it into fcbdn+16 pop h ;set up drive specs mov a,m sta fcbdm xra a sta fcbcr lxi d,tfcb ;copy to default fcb lxi h,fcbdn ;from fcbdn lxi b,33 ;set up default fcb ldir lxi h,cibuff ; com4: mov a,m ;skip to end of 2nd file name ora a ;end of line? jrz com5 cpi ' ' ;end of token? jrz com5 inx h jr com4 ; ; load command line into tbuff ; com5: mvi b,0 ;set char count lxi d,tbuff+1 ;pt to char pos ; com6: mov a,m ;copy command line to tbuff stax d ora a ;done if zero jrz com7 inr b ;incr char count inx h ;pt to next inx d jr com6 ; ; run loaded transient program ; com7: mov a,b ;save char count sta tbuff call crlf ;new line call defdma ;set dma to 0080 call setud ;set user/disk ; ; execution (call) of program (subroutine) occurs here ; execadr equ $+1 ;change address for in-line code modification call tpa ;call transient call defdma ;set dma to 0080, in case ;prog changed it on us call setu0d ;set user 0/disk call login ;login disk jmp restrt ;restart ccp ; ; ; ;section 5l ;command: get ;function: to load the specified file from disk to the specified address ;forms: ; get load the specified file at the specified page; ; is in hex ; if not ras ;not for remote-access system ; get: call hexnum ;get load address in hl push h ;save address call scaner ;get file name pop h ;restore address jrnz errjmp ;must be unambiguous ; ; fall thru to memload ; endif ; ; load memory with the file whose name is specified in the command line ; on input, hl contains starting address to load ; ; exit back to caller if no error.if com file too big or memory ; full, exit to mlerr. ; memload: shld loadadr ;set load address call getusr ;get current user number sta tmpusr ;save it for later sta tselusr ;temp user to select ; ; mla is a reentry point for a non-standard cp/m modification ; this is the return point for when the .com (or get) file is not found the ; first time, drive a: is selected for a second attempt. ; mla: call slogin ;log in specified drive if any call openf ;open command.com file jrnz mla1 ;file found - load it ; ; error routine to select user 0 if all else fails ; dfusr equ $+1 ;mark in-the-code variable mvi a,defusr ;get default user ; tselusr equ $+1 ;mark in-the-code variable cpi defusr ;same? jrz mla0 ;jump if so sta tselusr ;else put down new one mov e,a call setusr ;go set new user number jr mla ;and try again ; ; error routine to select drive a: if default was originally selected ; mla0: lxi h,tempdr ;get drive from current command xra a ;a=0 ora m jrnz mlerr ;error if already disk a: mvi m,1 ;select drive a: jr mla ; ; file found -- proceed with load ; mla1 equ $ ; loadadr equ $+1 ;memory load address (in-line code mod) lxi h,tpa ;set start address of memory load ; ml2: mvi a,entry/256-1 ;get high-order adr of just below ccp cmp h ;are we going to overwrite the ccp? jrc prnle ;error if so push h ;save address of next sector xchg ;... in de call dmaset ;set dma address for load lxi d,fcbdn ;read next sector call read pop h ;get address of next sector jrnz ml3 ;read error or eof? lxi d,128 ;move 128 bytes per sector dad d ;pt to next sector in hl jr ml2 ; ml3: dcr a ;load complete jz resetusr ;if zero, ok, go reset correct user ;# on way out, else fall through to prnle ; ; load error ; prnle: call printc db 'Ful','l'+80h ; ; transient load error ; mlerr: call resetusr ;reset current user number ; ;reset must be done before login errlog: call dlogin ;log in default disk ; errjmp: jmp error ; ; END