stat: do; declare cpmversion literally '20h'; /* requires 2.0 cp/m */ /* c p / m s t a t u s c o m m a n d (s t a t) */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* status status status status status status */ /* copyright(c) 1975, 1976, 1977, 1978, 1979, 1984 digital research box 579 pacific grove, ca 93950 */ /* modified 10/30/78 to fix the space computation */ /* modified 01/28/79 to remove despool dependencies */ /* modified 07/26/79 to operate under cp/m 2.0 */ /* modified 03/14/84 to remove iobyte modification for Personal CP/M */ declare jump byte data(0c3h), jadr address data (.status); /* jump to status */ /* function call 32 returns the address of the disk parameter block for the currently selected disk, which consists of: scptrk (2 by) number of sectors per track blkshf (1 by) log2 of blocksize (2**blkshf=blksize) blkmsk (1 by) 2**blkshf-1 extmsk (1 by) logical/physical extents maxall (2 by) max alloc number dirmax (2 by) size of directory-1 dirblk (2 by) reservation bits for directory chksiz (2 by) size of checksum vector offset (2 by) offset for operating system */ declare /* fixed locations for cp/m */ bdosa literally '0006h', /* bdos base */ buffa literally '0080h', /* default buffer */ fcba literally '005ch', /* default file control block */ dolla literally '006dh', /* dollar sign position */ parma literally '006eh', /* parameter, if sent */ rreca literally '007dh', /* random record 7d,7e,7f */ rreco literally '007fh', /* high byte of random overflow */ sectorlen literally '128', /* sector length */ memsize address at(bdosa), /* end of memory */ rrec address at(rreca), /* random record address */ rovf byte at(rreco), /* overflow on getfile */ doll byte at(dolla), /* dollar parameter */ parm byte at(parma), /* parameter */ sizeset byte, /* true if displaying size field */ dpba address, /* disk parameter block address */ dpb based dpba structure (spt address, bls byte, bms byte, exm byte, mxa address, dmx address, dbl address, cks address, ofs address), scptrk literally 'dpb.spt', blkshf literally 'dpb.bls', blkmsk literally 'dpb.bms', extmsk literally 'dpb.exm', maxall literally 'dpb.mxa', dirmax literally 'dpb.dmx', dirblk literally 'dpb.dbl', chksiz literally 'dpb.cks', offset literally 'dpb.ofs'; boot: procedure external; /* reboot */ end boot; mon1: procedure(f,a) external; declare f byte, a address; end mon1; mon2: procedure(f,a) byte external; declare f byte, a address; end mon2; mon3: procedure(f,a) address external; declare f byte, a address; end mon3; status: procedure; declare copyright(*) byte data ( ' Copyright (c) 1984, Digital Research'); /* dummy outer procedure 'status' will start at 100h */ /* determine status of currently selected disk */ declare alloca address, /* alloca is the address of the disk allocation vector */ alloc based alloca (1024) byte; /* allocation vector */ declare true literally '1', false literally '0', forever literally 'while true', cr literally '13', lf literally '10'; printchar: procedure(char); declare char byte; call mon1(2,char); end printchar; crlf: procedure; call printchar(cr); call printchar(lf); end crlf; printb: procedure; /* print blank character */ call printchar(' '); end printb; printx: procedure(a); declare a address; declare s based a byte; do while s <> 0; call printchar(s); a = a + 1; end; end printx; print: procedure(a); declare a address; /* print the string starting at address a until the next 0 is encountered */ call crlf; call printx(a); end print; break: procedure byte; return mon2(11,0); /* console ready */ end break; declare dcnt byte; version: procedure byte; /* returns current cp/m version # */ return mon2(12,0); end version; select: procedure(d); declare d byte; call mon1(14,d); end select; open: procedure(fcb); declare fcb address; dcnt = mon2(15,fcb); end open; search: procedure(fcb); declare fcb address; dcnt = mon2(17,fcb); end search; searchn: procedure; dcnt = mon2(18,0); end searchn; cselect: procedure byte; /* return current disk number */ return mon2(25,0); end cselect; setdma: procedure(dma); declare dma address; call mon1(26,dma); end setdma; getalloca: procedure address; /* get base address of alloc vector */ return mon3(27,0); end getalloca; getlogin: procedure address; /* get the login vector */ return mon3(24,0); end getlogin; writeprot: procedure; /* write protect the current disk */ call mon1(28,0); end writeprot; getrodisk: procedure address; /* get the read-only disk vector */ return mon3(29,0); end getrodisk; setind: procedure; /* set file indicators for current fcb */ call mon1(30,fcba); end setind; set$dpb: procedure; /* set disk parameter block values */ dpba = mon3(31,0); /* base of dpb */ end set$dpb; getuser: procedure byte; /* return current user number */ return mon2(32,0ffh); end getuser; setuser: procedure(user); declare user byte; call mon1(32,user); end setuser; getfilesize: procedure(fcb); declare fcb address; call mon1(35,fcb); end getfilesize; declare oldsp address, /* sp on entry */ stack(16) address; /* this program's stack */ declare fcbmax literally '512', /* max fcb count */ fcbs literally 'memory',/* remainder of memory */ fcb(33) byte at (fcba), /* default file control block */ buff(128) byte at (buffa); /* default buffer */ declare bpb address; /* bytes per block */ set$bpb: procedure; call set$dpb; /* disk parameters set */ bpb = shl(double(1),blkshf) * sectorlen; end set$bpb; select$disk: procedure(d); declare d byte; /* select disk and set bpb */ call select(d); call set$bpb; /* bytes per block */ end select$disk; getalloc: procedure(i) byte; /* return the ith bit of the alloc vector */ declare i address; return rol(alloc(shr(i,3)), (i and 111b) + 1); end getalloc; declare accum(4) byte, /* accumulator */ ibp byte; /* input buffer pointer */ compare: procedure(a) byte; /* compare accumulator with four bytes addressed by a */ declare a address; declare (s based a) (4) byte; declare i byte; do i = 0 to 3; if s(i) <> accum(i) then return false; end; return true; end compare; scan: procedure; /* fill accum with next input value */ declare (i,b) byte; setacc: procedure(b); declare b byte; accum(i) = b; i = i + 1; end setacc; /* deblank input */ do while buff(ibp) = ' '; ibp=ibp+1; end; /* initialize accum length */ i = 0; do while i < 4; if (b := buff(ibp)) > 1 then /* valid */ call setacc(b); else /* blank fill */ call setacc(' '); if b <= 1 or b = ',' or b = ':' or b = '*' or b = '.' or b = '>' or b = '<' or b = '=' then buff(ibp) = 1; else ibp = ibp + 1; end; ibp = ibp + 1; end scan; pdecimal: procedure(v,prec); /* print value v with precision prec (10,100,1000) with leading zero suppression */ declare v address, /* value to print */ prec address, /* precision */ zerosup byte, /* zero suppression flag */ d byte; /* current decimal digit */ zerosup = true; do while prec <> 0; d = v / prec ; /* get next digit */ v = v mod prec;/* get remainder back to v */ prec = prec / 10; /* ready for next digit */ if prec <> 0 and zerosup and d = 0 then call printb; else do; zerosup = false; call printchar('0'+d); end; end; end pdecimal; add$block: procedure(ak,ab); declare (ak, ab) address; /* add one block to the kilobyte accumulator */ declare kaccum based ak address; /* kilobyte accum */ declare baccum based ab address; /* byte accum */ baccum = baccum + bpb; do while baccum >= 1024; baccum = baccum - 1024; kaccum = kaccum + 1; end; end add$block; count: procedure(mode) address; declare mode byte; /* true if counting 0's */ /* count kb remaining, kaccum set upon exit */ declare ka address, /* kb accumulator */ ba address, /* byte accumulator */ i address, /* local index */ bit byte; /* always 1 if mode = false */ ka, ba = 0; bit = 0; do i = 0 to maxall; if mode then bit = getalloc(i); if not bit then call add$block(.ka,.ba); end; return ka; end count; abortmsg: procedure; call print(.('** Aborted **',0)); end abortmsg; userstatus: procedure; /* display active user numbers */ declare i byte; declare user(32) byte; declare ufcb(*) byte data ('????????????',0,0,0); call print(.('Active User :',0)); call pdecimal(getuser,10); call print(.('Active Files:',0)); do i = 0 to last(user); user(i) = false; end; call setdma(.fcbs); call search(.ufcb); do while dcnt <> 255; if (i := fcbs(shl(dcnt and 11b,5))) <> 0e5h then user(i and 1fh) = true; call searchn; end; do i = 0 to last(user); if user(i) then call pdecimal(i,10); end; end userstatus; drivestatus: procedure; declare rpb address, rpd address; pv: procedure(v); declare v address; call crlf; call pdecimal(v,10000); call printchar(':'); call printb; end pv; /* print the characteristics of the currently selected drive */ call print(.(' ',0)); call printchar(cselect+'A'); call printchar(':'); call printx(.(' Drive Characteristics',0)); rpb = shl(double(1),blkshf); /* records/block=2**blkshf */ if (rpd := (maxall+1) * rpb) = 0 and (rpb <> 0) then call print(.('65536: ',0)); else call pv(rpd); call printx(.('128 Byte Record Capacity',0)); call pv(count(false)); call printx(.('Kilobyte Drive Capacity',0)); call pv(dirmax+1); call printx(.('32 Byte Directory Entries',0)); call pv(shl(chksiz,2)); call printx(.('Checked Directory Entries',0)); call pv((extmsk+1) * 128); call printx(.('Records/ Extent',0)); call pv(rpb); call printx(.('Records/ Block',0)); call pv(scptrk); call printx(.('Sectors/ Track',0)); call pv(offset); call printx(.('Reserved Tracks',0)); call crlf; end drivestatus; diskstatus: procedure; /* display disk status */ declare login address, d byte; login = getlogin; /* login vector set */ d = 0; do while login <> 0; if low(login) then do; call select$disk(d); call drivestatus; end; login = shr(login,1); d = d + 1; end; end diskstatus; match: procedure(va,vl) byte; /* return index+1 to vector at va if match */ declare va address, v based va (16) byte, vl byte; declare (i,j,match,sync) byte; j,sync = 0; do sync = 1 to vl; match = true; do i = 0 to 3; if v(j) <> accum(i) then match=false; j = j + 1; end; if match then return sync; end; return 0; /* no match */ end match; declare devl(*) byte data ('VAL:USR:DSK:'); devreq: procedure byte; /* process device request, return true if found */ declare (i,j,items) byte; items = 0; do forever; call scan; if (i:=match(.devl,8)) = 0 then return items<>0; items = items+1; /* found first/next item */ if i = 1 then /* list possible assignment */ do; call print(.('Temp R/O Disk: d:=R/O',0)); call print(.('Set Indicator: d:filename.typ ', '$R/O $R/W $SYS $DIR',0)); call print(.('Disk Status : DSK: d:DSK:',0)); call print(.('User Status : USR:',0)); end; else if i = 2 then /* list user status values */ call userstatus; else if i = 3 then /* show the disk device status */ call diskstatus; /* end of current item, look for more */ call scan; if accum(0) = ' ' then return true; if accum(0) <> ',' then do; call print(.('Bad Delimiter',0)); return true; end; end; /* of do forever */ end devreq; pvalue: procedure(v); declare (d,zero) byte, (k,v) address; k = 10000; zero = false; do while k <> 0; d = low(v/k); v = v mod k; k = k / 10; if zero or k = 0 or d <> 0 then do; zero = true; call printchar('0'+d); end; end; call printchar('k'); call crlf; end pvalue; comp$alloc: procedure; alloca = getalloca; call printchar(cselect+'A'); call printx(.(': ',0)); end comp$alloc; prcount: procedure; /* print the actual byte count */ call pvalue(count(true)); end prcount; pralloc: procedure; /* print allocation for current disk */ call print (.('Bytes Remaining On ',0)); call comp$alloc; call prcount; end pralloc; prstatus: procedure; /* print the status of the disk system */ declare (login, rodisk) address; declare d byte; login = getlogin; /* login vector set */ rodisk = getrodisk; /* read only disk vector set */ d = 0; do while login <> 0; if low(login) then do; call select$disk(d); call comp$alloc; call printx(.('R/',0)); if low(rodisk) then call printchar('O'); else call printchar('W'); call printx(.(', Space: ',0)); call prcount; end; login = shr(login,1); rodisk = shr(rodisk,1); d = d + 1; end; call crlf; end prstatus; setdisk: procedure; if fcb(0) <> 0 then call select$disk(fcb(0)-1); end setdisk; getfile: procedure; /* process file request */ declare fnam literally '11', fext literally '12', fmod literally '14', frc literally '15', fln literally '15', fdm literally '16', fdl literally '31', ftyp literally '9', rofile literally '9', /* read/only file */ infile literally '10'; /* invisible file */ declare fcbn address, /* number of fcb's collected so far */ finx(fcbmax) address, /* index vector used during sort */ fcbe(fcbmax) address, /* extent counts */ fcbb(fcbmax) address, /* byte count (mod kb) */ fcbk(fcbmax) address, /* kilobyte count */ fcbr(fcbmax) address, /* record count */ bfcba address, /* index into directory buffer */ fcbsa address, /* index into fcbs */ bfcb based bfcba (32) byte, /* template over directory */ fcbv based fcbsa (16) byte; /* template over fcbs entry */ declare i address, /* fcb counter during collection and display */ l address, /* used during sort and display */ k address, /* " */ m address, /* " */ kb byte, /* byte counter */ lb byte, /* byte counter */ mb byte, /* byte counter */ (b,f) byte, /* counters */ matched byte; /* used during fcbs search */ multi16: procedure; /* utility to compute fcbs address from i */ fcbsa = shl(i,4) + .fcbs; end multi16; declare scase byte; /* status case # */ declare fstatlist(*) byte data('R/O',0,'R/W',0,'SYS',0,'DIR',0); setfilestatus: procedure byte; /* eventually, scase set r/o=0,r/w=1,dat=2,sys=3 */ declare fstat(*) byte data('R/O R/W SYS DIR '); if doll = ' ' then return false; call move(4,.parm,.accum); /* $???? */ if accum(0) = 'S' and accum(1) = ' ' then return not (sizeset := true); /* must be a parameter */ if (scase := match(.fstat,4)) = 0 then call print(.('Invalid File Indicator',0)); return true; end setfilestatus; printfn: procedure; declare (k, lb) byte; /* print file name */ do k = 1 to fnam; if (lb := fcbv(k) and 7fh) <> ' ' then do; if k = ftyp then call printchar('.'); call printchar(lb); end; end; end printfn; call set$bpb; /* in case default disk */ call setdisk; sizeset = false; scase = 255; if setfilestatus then do; if scase = 0 then return; scase = scase - 1; end; else if fcb(1) = ' ' then /* no file named */ do; call pralloc; return; end; /* read the directory, collect all common file names */ fcbn,fcb(0) = 0; fcb(fext),fcb(fmod) = '?'; /* question mark matches all */ call search(fcba); /* fill directory buffer */ collect: /* label for debug */ do while dcnt <> 255; /* another item found, compare it for common entry */ bfcba = shl(dcnt and 11b,5)+buffa; /* dcnt mod 4 * 32 */ matched = false; i = 0; do while not matched and i < fcbn; /* compare current entry */ call multi16; do kb = 1 to fnam; if bfcb(kb) <> fcbv(kb) then kb = fnam; else /* complete match if at end */ matched = kb = fnam; end; i = i + 1; end; checkmatched: /* label for debug */ if matched then i = i - 1; else do; /* copy to new position in fcbs */ fcbn = (i := fcbn) + 1; call multi16; /* fcbsa set to next to fill */ if (fcbn > fcbmax) or (fcbsa + 16) >= memsize then do; call print(.('** Too Many Files **',0)); i = 0; fcbn = 1; call multi16; end; /* save index to element for later sort */ finx(i) = i; do kb = 0 to fnam; fcbv(kb) = bfcb(kb); end; fcbe(i),fcbb(i),fcbk(i),fcbr(i) = 0; end; /* entry is at, or was placed at location i in fcbs */ fcbe(i) = fcbe(i) + 1; /* extent incremented */ /* record count */ fcbr(i) = fcbr(i) + bfcb(frc) + (bfcb(fext) and extmsk) * 128; /* count kilobytes */ countbytes: /* label for debug */ lb = 1; if maxall > 255 then lb = 2; /* double precision inx */ do kb = fdm to fdl by lb; mb = bfcb(kb); if lb = 2 then /* double precision inx */ mb = mb or bfcb(kb+1); if mb <> 0 then /* allocated */ call add$block(.fcbk(i),.fcbb(i)); end; call searchn; /* to next entry in directory */ end; /* of do while dcnt <> 255 */ display: /* label for debug */ /* now display the collected data */ if fcbn = 0 then call print(.('File Not Found',0)); else if scase = 255 then /* display collected data */ do; /* sort the file names in ascending order */ if fcbn > 1 then /* requires at least two to sort */ do; l = 1; do while l > 0; /* bubble sort */ l = 0; do m = 0 to fcbn - 2; i = finx(m+1); call multi16; bfcba = fcbsa; i = finx(m); call multi16; /* sets fcbsa, basing fcbv */ do kb = 1 to fnam; /* compare for less or equal */ if (b:=bfcb(kb)) < (f:=fcbv(kb)) then /* switch */ do; k = finx(m); finx(m) = finx(m + 1); finx(m + 1) = k; l = l + 1; kb = fnam; end; else if b > f then kb = fnam; /* stop compare */ end; end; end; end; if sizeset then call print(.(' Size ',0)); else call crlf; call printx(.(' Recs Bytes Ext Acc',0)); l = 0; do while l < fcbn; i = finx(l); /* i is the index to next in order */ call multi16; call crlf; /* print the file length */ call move(16,.fcbv(0),fcba); fcb(0) = 0; if sizeset then do; call getfilesize(fcba); if rovf <> 0 then call printx(.('65536',0)); else call pdecimal(rrec,10000); call printb; end; call pdecimal(fcbr(i),10000); /* rrrrr */ call printb; /* blank */ call pdecimal(fcbk(i),10000); /* bbbbbk */ call printchar('k'); call printb; call pdecimal(fcbe(i),1000); /* eeee */ call printb; call printchar('R'); call printchar('/'); if rol(fcbv(rofile),1) then call printchar('O'); else call printchar('W'); call printb; call printchar('A'+cselect); call printchar(':'); /* print filename.typ */ if (mb:=rol(fcbv(infile),1)) then call printchar('('); call printfn; if mb then call printchar(')'); l = l + 1; end; call pralloc; end; else setfileatt: /* label for debug */ /* set file attributes */ do; l = 0; do while l < fcbn; if break then do; call abortmsg; return; end; i = l; call multi16; call crlf; call printfn; do case scase; /* set to r/o */ fcbv(rofile) = fcbv(rofile) or 80h; /* set to r/w */ fcbv(rofile) = fcbv(rofile) and 7fh; /* set to sys */ fcbv(infile) = fcbv(infile) or 80h; /* set to dir */ fcbv(infile) = fcbv(infile) and 7fh; end; /* place name into default fcb location */ call move(16,fcbsa,fcba); fcb(0) = 0; /* in case matched user# > 0 */ call setind; /* indicators set */ call printx(.(' set to ',0)); call printx(.fstatlist(shl(scase,2))); l = l + 1; end; end; end getfile; setdrivestatus: procedure; /* handle possible drive status assignment */ call scan; /* remove drive name */ call scan; /* check for = */ if accum(0) = '=' then do; call scan; /* get assignment */ if compare(.('R/O ')) then do; call setdisk; /* a: ... */ call writeprot; end; else call print(.('Invalid Disk Assignment',0)); end; else /* not a disk assignment */ do; call setdisk; if match(.devl,8) = 3 then call drive$status; else call getfile; end; end setdrivestatus; /* save stack pointer and reset */ oldsp = stackptr; stackptr = .stack(length(stack)); /* process request */ if version < cpmversion then call print(.('Wrong CP/M Version (Requires 2.0 or greater)',0)); else do; /* size display if $S set in command */ ibp = 1; /* initialize buffer pointer */ if fcb(0) = 0 and fcb(1) = ' ' then /* stat only */ call prstatus; else do; if fcb(0) <> 0 then call setdrivestatus; else do; if not devreq then /* must be file name */ call getfile; end; end; end; /* restore old stack before exit */ stackptr = oldsp; end status; end;