;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; CHUNKS.M68 - Simultaneous SBR & LIT to determine file room on disk ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; =============== BEGINNING OF COPYRIGHT NOTICE ================ ; CHUNKS.M68 is FREEWARE, Copyright (c) Bob Fowler, Nov 1992 ; Permission is given to copy this program freely, but this ; copyright notice must be included and remain unchanged. ; You may not charge anyone for this program. ; ================== END OF COPYRIGHT NOTICE =================== ; CHUNKS.LIT syntax: ; CHUNKS {filnam} {=} {/C:n} ; ; CHUNKS.SBR calling format: ; XCALL CHUNKS,NDISK,DISK,BLOCKS,USED,CHUNK(1),NCHUNKS,ERRCOD ; XCALL CHUNKS,NDISK,DISK,BLOCKS,USED,CHUNK, NCHUNKS,ERRCOD ; Where: ; NDISK (F6,Bn) = indicates n-th disk in AMOS device table ; DISK (Sn) = a disk device name (eg,"SMD000021:","DSK0") ; BLOCKS (F6,Bn) = total blocks on disk ; USED (F6,Bn) = total used blocks on disk ; CHUNK (F6,Bn) = largest contiguous chunk ; CHUNK(1) (F6,Bn) = first element of array of largest chunks ; NCHUNKS (F6,Bn) = number of chunks to return ; ERRCOD (F6,Bn) = error code (0=none, nn=minor, nnn=major) ; Send NDISK,DISK,NCHUNKS, receive BLOCKS,USED,CHUNK(n),ERRCOD ; General register usage throughout: ; A3 = (SBR) base of AlphaBASIC XCALL parameter table ; = (LIT) base of GETMEM primary work area ; A5 = (LIT,SBR) base of common work area ; All other register usages are local unless indicated otherwise ; Every line in this file has no more than 70 characters. ; This allows easy printing on any printer - please keep it that way! ; Edit history: ; (100) 1992-Nov - created ; (101) 1992-Nov - correct version number (decimal, not octal) ; - add free blocks to LIT display ; (102) 1992-Dec-12 - add device name to bitmap kaput message ; - CLOSE output file if LIT aborts ; (103) 1994-Apr-08 - add paging on displays COPY SYS SEARCH SYSSYM ; for PHDR symbols VMAJOR = 1 VMINOR = 0 VEDIT = 103. DSPLIN = 21. ; lines displayed between pauses PSECT .=0 ; No privileges, reentrant, reusable CHUNKS: PHDR -1,0,PH$REU!PH$REE ; Determine whether we are a LIT or an SBR ; Uses: A0,A1,A2 LEA A0,CHUNKS ; CHUNKS starting address SUB #2,A0 ; go back to extension CMPW @A0,#[LIT] ; are we a LIT? BEQ DOLIT ; yes - act like a LIT CMPW @A0,#[SBR] ; are we an SBR? JEQ DOSBR ; yes - act like an SBR ; If BASIC XCALL's an unloaded SBR and loads it, there is no header ;;; TYPECR ;;; EXIT JMP DOSBR ; assume SBR by default ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Routine to function like an AMOS xxxxxx.LIT module ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DOLIT: ; Get primary work area and save its base address in A3 GETIMP LITLTH,A3,LITEXT ; get impure work area ; GETIMP apparently clears the work area - but this is undocumented ; Clear work area with nulls ; MOV A3,A0 ; beginning of work area ; MOV #LITLTH,D0 ; counter ;LITCLR:CLRW (A0)+ ; clear one byte ; SUB #2,D0 ; decrement counter ; BHI LITCLR ; if not zero, do again MOV #6,LCHUNK(A3) ; default to 6 chunks ; Process input line - print file name CLRW PRTFLG(A3) ; default to no print file BYP ; bypass blanks,tabs LIN ; any user inputs? JEQ GOTLIN ; no - finished with inputs FSPEC PRTDDB(A3),LST ; process output filespec TSTW PRTDDB+D.FIL(A3) ; was there a filename? BNE YESPRT ; yes - skip following CMPB @A2,#'= ; "=" for default print name? BNE GOTPRT ; no - no print file at all PUSH A2 ; yes - save current line ptr LEA A2,PRTNAM ; and use default filename FSPEC PRTDDB(A3),LST ; this FSPEC always succeeds POP A2 ; restore AMOSL line ptr YESPRT: INIT PRTDDB(A3) ; get buffer for print file LOOKUP PRTDDB(A3) ; output file already there? BNE 10$ ; no - open it DSKDEL PRTDDB(A3) ; yes - delete it first 10$: OPENO PRTDDB(A3) ; open print file SETW PRTFLG(A3) ; set print file indicator BYP ; bypass blanks and tabs LIN ; anything left on line? BEQ GOTLIN ; no - finished with inputs CMPB @A2,#'= ; "="? BNE GOTPRT ; no - ok INC A2 ; yes - bypass GOTPRT: ; Process input line - /C option BYP ; bypass blanks and tabs LIN ; anything left on line? BEQ GOTLIN ; no - finished with inputs CMPB (A2)+,#'/ ; "/"? BNE LITFMT ; no - wrong syntax CMPB (A2)+,#'C ; "C"? BNE LITFMT ; no - wrong syntax CMPB (A2)+,#': ; ":"? BNE LITFMT ; no - wrong syntax GTDEC ; get number of chunks (D1) BMI LITFMT ; too large - error BYP ; bypass blanks and tabs LIN ; end of line? BNE LITFMT ; no - error MOV D1,LCHUNK(A3) ; remember number of chunks GOTLIN: BR LITMEM LITFMT: TTYI ASCII |CHUNKS.LIT format: CHUNKS {filnam} {=} {/C:n}| BYTE 15,0 EVEN EXIT ; Allocate second work area without going through formal GETMEM ; This is done parallel with the SBR section, which cannot do GETMEMs LITMEM: USRFRE A1 ; start of free memory USREND A2 ; end of free memory MOV A1,A5 ; A5 = user impure area base ADD #MEMLTH,A1 ; new start of free memory CMP A1,A2 ; out of room? BLO 10$ ; no - go ahead TYPECR EXIT ; abort 10$: MOV A1,LITFRE(A3) ; start of free memory MOV A2,LITEND(A3) ; end of free memory CLR LDEVIC(A3) ; disk number = 0 CRLF ; blank line LEA A2,TITLE1 ; title string #1 CALL PRTLIN ; print line LEA A2,TITLE2 ; title string #2 CALL PRTLIN ; print line CLRW LINES(A3) ; clear line count PRTLOP: CTRLC LITDON ; allow control-C INC LDEVIC(A3) ; next disk ; Initialize work area CALL CLRWRK ; clear work area ; Put in LIT variables CLRW MODFLG(A5) ; MODFLG=0 for LIT MOV LCHUNK(A3),NCHUNK(A5) ; number of chunks MOV LITFRE(A3),RAMFRE(A5) ; start of free memory MOV LITEND(A3),RAMEND(A5) ; end of free memory MOV LDEVIC(A3),NDEVIC(A5) ; disk number CALL LITCAL ; get chunk data CMP ERRCOD(A5),#2 ; end of disks? JEQ PRTTOT ; yes - done ; Prepare print/display lines ; Uses: D0,D1,A1,A2 LEA A2,PRTBUF(A3) ; start of print buffer ; Disk name LEA A1,DEVICE(A5) ; address of disk name MOV #6,D0 ; pad to 6 characters CALL MOVSTR ; move string to print buffer ; Total blocks MOV BLOCKS(A5),D1 ; get total disk blocks DCVT 9.,OT$MEM ! OT$ZER ; display 9 digits MOV D1,D0 ; save for percentages ADD D1,ALLBLX(A3) ; add to all-disk total ; Unmounted disks have special format CMP ERRCOD(A5),#1 ; disk mounted? JEQ PRTUNM ; no - different display ; Blocks in use MOV TOTUSE(A5),D1 ; get total used blocks DCVT 9.,OT$MEM ! OT$ZER ; output 9 digits ADD D1,ALLUSE(A3) ; add to all-disk total ; Percentage CALL PERCNT ; get percentage MOVB #32.,(A2)+ ; space MOVB #'(,(A2)+ ; ( DCVT 3,OT$MEM ! OT$ZER ; 3 digit percentage MOVB -(A2),D0 ; pick up tenths digit MOVB #'.,(A2)+ ; put in decimal point MOVB D0,(A2)+ ; then put in tenths digit MOVB #'%,(A2)+ ; % MOVB #'),(A2)+ ; ) ; Free blocks MOV BLOCKS(A5),D1 ; get total disk blocks SUB TOTUSE(A5),D1 ; get total used blocks DCVT 9.,OT$MEM ! OT$ZER ; display 9 digits MOVB #32.,(A2)+ ; space 1 MOVB #32.,(A2)+ ; space 2 MOVB #32.,(A2)+ ; space 3 ADD D1,ALLFRE(A3) ; add to all-disk total ; Chunks are potentially unlimited, so we flush buffer after each one CALL PRTBEG ; print from start of buffer MOV CHNKTB(A5),A0 ; get base of chunk table MOV NCHUNK(A5),D0 ; number of chunks to display BEQ PRTDON ; no chunks - skip display PRTCHN: CTRLC LITDON ; allow control-C MOV (A0)+,D1 ; get next chunk DCVT 0,OT$MEM ; output chunk DEC D0 ; chunk count down BEQ PRTDON ; no more chunks MOVB #',,(A2)+ ; separate chunks CALL PRTBEG ; print from start of buffer BR PRTCHN ; another chunk PRTDON: CALL PRTTRM ; print with terminators ; Pause between screens TSTW PRTFLG(A3) ; are we printing? BNE PRTPAG ; yes - skip pause INCW LINES(A3) ; increase line count CMPW LINES(A3),#DSPLIN ; is display limit reached? BLO PRTPAG ; no - skip pause TYPE KBD ; A2 points to result CRLF ; blank line LEA A2,TITLE1 ; title string #1 CALL PRTLIN ; print line LEA A2,TITLE2 ; title string #2 CALL PRTLIN ; print line CLRW LINES(A3) ; clear line count PRTPAG: JMP PRTLOP ; do another disk PRTUNM: LEA A1,FILL0 ; filler string #0 MOV #29.,D0 ; pad to 20 characters CALL MOVSTR ; move string to print buffer LEA A1,FILL1 ; filler string #1 MOV #13.,D0 ; pad to 13 characters CALL MOVSTR ; move string to print buffer BR PRTDON ; print this line PRTTOT: LEA A2,PRTBUF(A3) ; go back to start of line LEA A1,FILL3 ; filler string #3 MOV #6,D0 ; pad to 6 characters CALL MOVSTR ; move string to print buffer MOV ALLBLX(A3),D1 ; get total disk blocks DCVT 9.,OT$MEM ! OT$ZER ; display 9 digits MOV D1,D0 ; save for percentage MOV ALLUSE(A3),D1 ; get total used blocks DCVT 9.,OT$MEM ! OT$ZER ; display 9 digits CALL PERCNT ; get percentage MOVB #32.,(A2)+ ; space MOVB #'(,(A2)+ ; ( DCVT 3,OT$MEM ! OT$ZER ; 3 digit percentage MOVB -(A2),D0 ; pick up tenths digit MOVB #'.,(A2)+ ; put in decimal point MOVB D0,(A2)+ ; then put in tenths digit MOVB #'%,(A2)+ ; % MOVB #'),(A2)+ ; ) MOV ALLFRE(A3),D1 ; get total disk blocks DCVT 9.,OT$MEM ! OT$ZER ; display 9 digits CALL PRTTRM ; print with terminators LITDON: TSTW PRTFLG(A3) ; are we printing? BEQ LITEXT ; no - leave now CLOSE PRTDDB(A3) ; close output file LITEXT: EXIT ; end of CHUNKS.LIT ;LIT display format: ; ;DISK BLOCKS USED BLOCKS FREE LARGEST CONTIGUOUS CHUNKS ;------ ------ -------------- ------ ------------------------- ;DSK0: nnnnnn nnnnnn (nn.n%) nnnnnn nnnnn,nnnnn,nnnn,nnn,nn,n ;DSK1: nnnnnn nnnnnn (nn.n%) nnnnnn nnnnn,nnnnn,nnnn,nnn,nn,n ;DSK2: [not mounted] ;TOTAL nnnnnnn nnnnnnn (nn.n%) nnnnnnn ; Move filler into print buffer ; Input: A1 = ASCII source ; A2 = destination ; D0 = character count (pad with trailing spaces if necessary) MOVSTR: DEC D0 ; count 'em MOVB (A1)+,(A2)+ ; move next character BNE MOVSTR ; repeat until null moved DEC A2 ; go back to null INC D0 ; and reverse count BLE 20$ ; if zero, done 10$: MOVB #32.,(A2)+ ; space DEC D0 ; done? BHI 10$ ; no - space again 20$: RTN ; done ; Display and print output line ; Inputs: A2 = pointer to output line ; PRTFLG(A3) = 0 for display only , #0 for display and print ; Add termination (return + linefeed + null) to end of lines PRTTRM: MOVB #15,(A2)+ ; + return MOVB #12,(A2)+ ; + line feed PRTBEG: CLRB @A2 ; + null LEA A2,PRTBUF(A3) ; start of print buffer PRTLIN: MOVB (A2)+,D1 ; next byte for output BEQ 20$ ; null - done TSTW PRTFLG(A3) ; are we printing? BEQ 10$ ; no - skip it FILOTB PRTDDB(A3) ; output byte to print file 10$: TTY ; display byte BR PRTLIN ; do another 20$: LEA A2,PRTBUF(A3) ; go back to start of buffer RTN ; done ; Calculate percentage to .1%, accurately and efficiently ; Inputs: D0,D1 (both destroyed) ; Output: D1 = INT( D1/D0 * 1000 + 0.5) MIN 999 (ideally) ; = INT( (2000*d1+d0) / (2*d0) ) MIN 999 (CHUNKS) ; where d1=D1/2**c , d0=D0/2**c , 2*d1<65536 , c minimum ; Compare with the divide algorithm in UltraSoft DSKUSE ; = INT( INT(D1/1024)*100 / INT(D0/1024) ) (DSKUSE) ; Worst case is exemplified by 1023 blocks free of 9696 (on a Hawk); ; The exact answer is 10.55%, CHUNKS gives 10.6%, DSKUSE gives 0% (!) PERCNT: CMP D0,#32768. ; divisor too big? BLO PERDIV ; no - do divide ASR D0,#1 ; reduce by factor of 2 ASR D1,#1 ; reduce by factor of 2 BR PERCNT ; test again PERDIV: MUL D1,#2000. ; (2000*D1 ADD D0,D1 ; + D0) ASL D0,#1 ; (2*D0) DIV D1,D0 ; / AND #177777,D1 ; remove remainder CMP D1,#999. ; more than 999? BLOS PEREND ; no - done MOV #999.,D1 ; yes - limit to 999 PEREND: RTN PRTNAM: ASCII |CHUNKS.LST| BYTE 0 TITLE1: ASCII |DISK BLOCKS USED BLOCKS FREE | ASCII |LARGEST CONTIGUOUS CHUNKS| BYTE 15,12,0 TITLE2: ASCII |------ ------ -------------- ------ | ASCII |-------------------------| BYTE 15,12,0 FILL0: ASCII |*| BYTE 0 FILL1: ASCII |[not mounted]| BYTE 0 FILL2: ASCII |%)| BYTE 0 FILL3: ASCII |TOTAL| BYTE 0 EVEN ; Work area must be cleared (must especially clear garbage from DDBs) CLRWRK: MOV A5,A0 ; base of work area MOV #MEMLTH,D0 ; length of work area CLRLOP: CLRW (A0)+ ; clear SUB #2,D0 ; done? BHI CLRLOP ; no - clear another RTN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Routine to function like an AlphaBASIC xxxxxx.SBR module ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Inputs: (from AlphaBASIC): ; A3 = argument list ; A4 = beginning of free memory ; A5 = ending of free memory ; Contents of SBR parameter table sent from AlphaBASIC: ; ; # PARAMETER I/O TYPE(B2) ADDR(B4) SIZE(B4) COUNT(B2) ; - --------------- --- -------- -------- -------- --------- ; 0 00(A3) ; 1 NDISK (F6,Bn) I/O 02(A3) 04(A3) 10(A3) ; 2 DISK (Sn) I/O 14(A3) 16(A3) 22(A3) ; 3 BLOCKS (F6,Bn) OUT 26(A3) 30(A3) 34(A3) ; 4 USED (F6,Bn) OUT 40(A3) 42(A3) 46(A3) ; 5 CHUNK (F6,Bn) OUT 52(A3) 54(A3) 60(A3) ; 6 NCHUNKS (F6,Bn) IN 64(A3) 66(A3) 72(A3) ; 7 ERRCOD (F6,Bn) OUT 76(A3) 100(A3) 104(A3) ; ; TYPE code = 0 (Unformatted), 2 (String), 4 (Floating), 6 (Binary) DOSBR: ; Get initial work area memory MOV A5,A6 ; save temporarily MOV A4,A5 ; A5 = user impure area base ADD #MEMLTH,A4 ; start of free memory CMP A4,A6 ; out of room? BLO GOTWRK ; no - go ahead ; Out of memory MOV #102.,D0 ; error 102 (out of memory) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort GOTWRK: CALL CLRWRK ; clear work area MOV A4,RAMFRE(A5) ; start of free memory MOV A6,RAMEND(A5) ; end of free memory SETW MODFLG(A5) ; MODFLG#0 for SBR ; SBR parameter #0 - parameter count CMPW @A3,#5 ; minimum of 5 parameters JLO ERRPAR ; too few parameters CMPW @A3,#7 ; maximum of 7 parameters JHI ERRPAR ; too many parameters ; SBR parameter #1 - NDISK (F6,Bn) LEA A0,2(A3) ; address of parameter trio CALL GENINP ; get general input JLT ERRPAR ; negative input - error 101 MOV D0,NDEVIC(A5) ; save NDISK ; SBR parameter #2 - DISK (Sn) ; Must allow for no null at end of DISK (if string is full) ; Allow no colon at end of DISK ; Allow for a few extra zeros (DSK00001:) ; Allow empty DISK ; Uses: A0,A1,D0,D1 CMPW 14(A3),#2 ; string? JNE ERRPAR ; no - error code 101 MOV 16(A3),A0 ; origin of DISK string LEA A1,DEVICE(A5) ; destination of string MOV 22(A3),D0 ; LEN of input variable CLR D1 ; characters in result CLRB @A1 ; default to empty string MOVBYT: MOVB (A0)+,(A1)+ ; move one byte of disk name BEQ FINSTR ; if null - finish up INC D1 ; bump result count CMP D1,#10. ; too big? JHI ERRPAR ; yes - error code 101 SOB D0,MOVBYT ; repeat until end of string CLRB (A1)+ ; always end with a null FINSTR: TST D1 ; empty? BEQ GOTSTR ; yes - use login disk SUB #2,A1 ; go back to last non-null CMPB (A1)+,#': ; was it a colon? BEQ GOTSTR ; yes - ok MOVB #':,(A1)+ ; no - add colon GOTSTR: ; SBR parameter #6 - NCHUNKS (F6,Bn) MOV #1,NCHUNK(A5) ; default to one chunk CMPW @A3,#6 ; is NCHUNKS there? BLO GOTNCH ; no - use default value LEA A0,64(A3) ; address of parameter trio CALL GENINP ; get general input JLT ERRPAR ; negative or bad input GOTNCH: MOV D0,NCHUNK(A5) ; save NCHUNKS ; Determine calling method TST NDEVIC(A5) ; is NDISK zero? BEQ GETSPC ; yes - process DISK ; Entry point for LIT routine CALL ; From this point on, we must check for either LIT or SBR LITCAL: ; Lookup n-th disk in the (undocumented) AMOS device table ; Symbols used here are from disassembly of SYSSYM.UNV[7,7] ; Output: A1 = address of device table entry ; Uses: A2,D0,D1 TSTB DEVICE(A5) ; was DISK string also sent? JNE ERRPAR ; yes - error code 101 MOV DEVTBL,A1 ; get base of device table MOV NDEVIC(A5),D0 ; get NDISK CNTDEV: TST DV.BMP(A1) ; this device have a bitmap? BEQ 10$ ; no - not a disk DEC D0 ; is NDISK count down zero? BEQ NTHDEV ; yes - we this is n-th disk 10$: MOV DV.NXT(A1),A1 ; address of next DEVTBL entry CMP A1,#0 ; zero? (MOV An doesn't set Z) BNE CNTDEV ; no - skip following ; NDISK is beyond end of table MOV #2,ERRCOD(A5) ; error 2 (NDISK too high) JMP GETTBL ; delay until we output values ; put name of n-th disk into DDB NTHDEV: MOVW DV.DEV(A1),BITDDB+D.DEV(A5) ; put disk into DDB MOVW DV.UNT(A1),BITDDB+D.DRV(A5) ; put disk into DDB BR GOTDEV ; continue with this disk ; Process DISK string from calling program. ; If full flexibility is allowed, this is very tedious. ; If we allow CHUNKS to be an SBR, then: ; We are within BASIC, and there is no "free memory" for AMOS. ; Thus, we cannot do GETMEM. ; If we allow the disk driver to not be in memory then: ; We cannot do an INIT, because it loads any non-resident driver. ; Thus, CHUNKS must load the driver into memory. ; in addition, INIT also does a GETMEM, which is also not possible. ; If we allow a default (login) disk, then: ; We cannot do an FSPEC; it apparently loads non-resident drivers. ; This "feature" of FSPEC is apparently not documentated. ; All of the above tasks must be emulated by CHUNKS when necessary. ; Do FSPEC or default to login disk ; Uses: A1,A2 GETSPC: JOBIDX A1 ; get base of user's JCB MOVW JOBDEV(A1),BITDDB+D.DEV(A5) ; default = login disk MOVW JOBDRV(A1),BITDDB+D.DRV(A5) ; and drive # LEA A2,DEVICE(A5) ; address of DISK string TSTB @A2 ; blank? BEQ GOTSPC ; yes - use default FSPEC BITDDB(A5) ; set up DDB for this disk GOTSPC: ; filespec is processed ; Find disk in the (undocumented) AMOS device table ; Symbols used here are from disassembly of SYSSYM.UNV[7,7] ; Output: A1 = address of device table entry ; Uses: D0,D1 MOV DEVTBL,A1 ; get base of device table CLR D1 ; start disk count CMPDEV: TST DV.BMP(A1) ; this device have a bitmap? BEQ 10$ ; no - not a disk INC D1 ; bump disk count MOV D1,NDEVIC(A5) ; save disk count MOVW DV.DEV(A1),D0 ; get next disk name CMPW D0,BITDDB+D.DEV(A5) ; correct disk name? BNE 10$ ; no - try next device MOVW DV.UNT(A1),D0 ; get next disk drive # CMPW D0,BITDDB+D.DRV(A5) ; correct drive number? BEQ GOTDEV ; yes - we have found disk 10$: MOV DV.NXT(A1),A1 ; address of next DEVTBL entry CMP A1,#0 ; zero? (MOV An doesn't set Z) BNE CMPDEV ; no - skip following MOV #103.,D0 ; error 103 (device not found) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort ; We have located disk in AMOS device table, by counting or matching GOTDEV: MOV A1,DEVENT(A5) ; save address of DEVTBL entry ; Put ASCII name of disk into DISK string ; Input: A1 = address of device table entry ; Uses: A2,D1 LEA A1,DV.DEV(A1) ; disk name (RAD50) LEA A2,DEVICE(A5) ; destination (ASCII) UNPACK ; unpack disk name CLR D1 ; clear upper word MOV DEVENT(A5),A1 ; address of DEVTBL entry MOVW DV.UNT(A1),D1 ; disk drive # DCVT 0,OT$MEM ; output from D1 to A2 MOVB #':,(A2)+ ; finish with ":" CLRB @A2 ; terminate with null ; See if device driver is in (monitor or user) memory ; Output: A1 = address of device driver ; Uses: A2 MOV DEVENT(A5),A1 ; address of DEVTBL entry LEA A2,NAMBLK(A5) ; address of file name block MOVW DV.DEV(A1),@A2 ; put in driver name (RAD50) MOV ZSYDSK,A1 ; address defaults to DSK addr CMPW (A2)+,#[DSK] ; is disk "DSK" ? JEQ GOTDVR ; yes - skip search CLRW (A2)+ ; clear next 3 letters (RAD50) MOVW #[DVR],@A2 ; end with ".DVR" SUB #4,A2 ; go back to first address SRCH @A2,A1 ; find driver in memory JEQ GOTDVR ; if found - skip following ; Device driver not in memory - load it ; Output: A1 = address of disk driver ; Uses: A0,D0 ; Emulate FSPEC and INIT functions MOVW #[DSK],DVRDDB+D.DEV(A5) ; DSK CLRW DVRDDB+D.DRV(A5) ; 0: MOVW BITDDB+D.DEV(A5),DVRDDB+D.FIL(A5) ; dev CLRW DVRDDB+D.FIL+2(A5) ; --- MOVW #[DVR],DVRDDB+D.EXT(A5) ; .DVR MOVW #406,DVRDDB+D.PPN(A5) ; [1,6] MOVW #40000,DVRDDB(A5) ; set INITed flag MOV #512.,DVRDDB+D.SIZ(A5) ; temporary LOOKUP buffer size MOV RAMFRE(A5),A1 ; address of driver buffer MOV A1,DVRDDB+D.BUF(A5) ; put in DDB ; Get memory - temporary 512-byte LOOKUP buffer for driver ADD #512.,RAMFRE(A5) ; need more memory MOV RAMFRE(A5),A0 ; new start of free memory CMP A0,RAMEND(A5) ; out of room? BLO LOOKUP ; no - go on ; Out of memory MOV #102.,D0 ; error 102 (out of memory) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort LOOKUP: LOOKUP DVRDDB(A5) ; look it up BEQ DVRFND ; if found - ok ; Disk Device driver not found (error 104) ; Defer abort so we can return the offending disk name in DISK MOV #104.,ERRCOD(A5) ; delay error processing JMP ENDSCN DVRFND: MOV DVRDDB+D.WRK(A5),D0 ; (blocks SUB #1,D0 ; - 1 ) MUL D0,#510. ; x 510 ADD DVRDDB+D.WRK+4(A5),D0 ; + active SUB #2,D0 ; - 2 MOV D0,DVRDDB+D.SIZ(A5) ; = buffer size ; Get back memory from temporary LOOKUP buffer SUB #512.,RAMFRE(A5) ; deallocate memory ; Get memory - buffer for xxx.DVR ADD D0,RAMFRE(A5) ; need more memory MOV RAMFRE(A5),A0 ; new start of free memory CMP A0,RAMEND(A5) ; out of room in buffer? BLO LODDVR ; no - go on ; Out of memory MOV #102.,D0 ; error 102 (out of memory) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort LODDVR: FETCH DVRDDB(A5),,F.ABS ; load driver into memory ; Disk driver either (a) DSK (b) already in memory (c) loaded above ; Input: A1 = address of device driver (undocumented format) GOTDVR: MOV DD.DSZ(A1),D0 ; BLOCKS = total disk blocks MOV D0,BLOCKS(A5) ; save BLOCKS MOV A1,DEVDRV(A5) ; save disk driver address ; Check if disk is mounted - if not, cannot get bitmap (error 1) ; But find and load driver anyway, so we can return BLOCKS value MOV DEVENT(A5),A0 ; device table entry address MOVW DV.FLG(A0),D0 ; get device flags ANDW #DV$MNT,D0 ; is disk mounted? BNE 10$ ; yes - read bitmap MOV #1,ERRCOD(A5) ; no - delay error processing JMP ENDSCN ; bypass bitmap read and scan 10$: ; Emulate the INIT monitor call, and prepare DDB for bitmap read ; Input: A1 = address of disk device driver ; Uses: A0,D0 MOV DD.BMS(A1),D0 ; get # of words in bitmap ASL D0 ; convert to bytes ADD #4,D0 ; + 4 bytes for bitmap hash MOV D0,BITDDB+D.SIZ(A5) ; = size of bitmap DDB buffer MOV RAMFRE(A5),BITDDB+D.BUF(A5) ; bitmap buffer addr ; Get memory - buffer for bitmap ADD D0,RAMFRE(A5) ; need more memory MOV RAMFRE(A5),A0 ; new start of free memory CMP A0,RAMEND(A5) ; out of memory ? BLO INIT1 ; no - go on ; Out of memory MOV #102.,D0 ; error 102 (out of memory) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort INIT1: MOV A1,BITDDB+D.DVR(A5) ; put in driver address MOVW #40000,BITDDB(A5) ; set INITed flag MOV #2,BITDDB+D.REC(A5) ; bitmap at block #2 ; Bitmaps might be shared or paged - it is far easier to read it in READ BITDDB(A5) ; reads <512,=512,>512 bytes ; Get room for internal chunks table (if 0 chunks, reserve 1 chunk) ; Uses A0,D0 GETTBL: MOV NCHUNK(A5),D0 ; NCHUNKS BNE 10$ ; if >0, ok MOV #1,D0 ; if 0, get room for 1 chunk 10$: MUL D0,#4 ; need (4*NCHUNKS) more bytes MOV RAMFRE(A5),CHNKTB(A5) ; address of chunk table ; Get memory - chunk table ADD D0,RAMFRE(A5) ; need more memory MOV RAMFRE(A5),A0 ; new start of free memory CMP A0,RAMEND(A5) ; out of room in buffer? BLO GOTTBL ; no - go on ; Out of memory MOV #102.,D0 ; error 102 (out of memory) CALL ERRSET ; return in ERRCOD TYPECR JMP ABORT ; abort GOTTBL: MOV RAMFRE(A5),CHNKTE(A5) ; after last chunk in table ; Clear CHUNK table MOV CHNKTB(A5),A0 ; base of CHUNK table CLRTBL: CLR (A0)+ ; clear chunk CMP A0,CHNKTE(A5) ; done yet? BLO CLRTBL ; no - clear another ; Check for defered error 2 abort (no more disks) CMP ERRCOD(A5),#2 ; end of disks? JEQ ENDSCN ; yes - go to end ; Scan entire bitmap for used blocks and largest chunk(s) ; ; Usage of registers (initializing values given in parenthesis) ; D0 = current chunk (zero) A0 = bitmap address (first) ; D1 = n-th largest chunk so far (zero) A1 = [unused] ; D2 = total used blocks (zero) A2 = CHUNK table base ; D3 = 16-bit bitmap word A3 = [XCALL parameters] ; D4 = countdown for each 16 bits A4 = CHUNK table end ; D5 = block/bits left in scan (all) A5 = [user impure area] ; D6 = work area A6 = work area ; D7 = hash total (zero) A7 = *** DO NOT USE *** ; Set up initial conditions for bitmap scan INITS: CLR D0 ; current chunk = 0 CLR D1 ; largest n-th chunk so far =0 TST NCHUNK(A5) ; zero chunks? BNE 10$ ; no - go on ; If 0 chunks are requested, initialize largest n-th chunk to 2**32-1 ; This in effect ignores all chunks, since they are all "much smaller" MOV #-1,D1 ; largest n-th chunk huge ... 10$: CLR D2 ; used blocks = 0 MOV BLOCKS(A5),D5 ; blocks left = all CLR D7 ; clear hash total MOV BITDDB+D.BUF(A5),A0 ; address of bitmap area MOV CHNKTB(A5),A2 ; address of first CHUNK value MOV CHNKTE(A5),A4 ; after last chunk in table SUB #4,A4 ; last chunk in table ; Do bitmap scan ; This loop is coded using only (fast) direct register addressing NXTWRD: CLR D3 ; clear longword high word MOVW (A0)+,D3 ; get next bitmap word ADD D3,D7 ; add to hash total MOV #16.,D4 ; start 16-bit countdown NXTBIT: ADD #1,D0 ; increment current chunk size RORW D3 ; rotate next bit into Carry BCC GOTBIT ; if bit was 0, go to next bit ADD #1,D2 ; increment # of used blocks SUB #1,D0 ; chunks always 1 too large BEQ GOTBIT ; if zero, no chunk to add CALL GOTCHK ; process this chunk CLR D0 ; current chunk = 0 GOTBIT: SUB #1,D5 ; decrement bits/blocks left BEQ LSTCHK ; if none left, we are done SOB D4,NXTBIT ; more bits in word, continue BR NXTWRD ; otherwise, get next word ; Add next chunk to results (in D0) GOTCHK: CMP D0,D1 ; chunk > n-th largest chunk? BLOS ENDCHK ; no - don't need this chunk MOV A4,A6 ; start at smallest chunk CHKLOP: CMP A6,A2 ; are we at first chunk? BLOS FINCHK ; yes - insert chunk here SUB #4,A6 ; no - go to preceeding chunk MOV (A6)+,D6 ; get (next higher) chunk size CMP D0,D6 ; new chunk <= this chunk? BLOS FINCHK ; yes - end insert loop MOV D6,@A6 ; no - move chunk down SUB #4,A6 ; go to next insert position BR CHKLOP ; and test it FINCHK: MOV D0,@A6 ; put new chunk in CHUNK table MOV @A4,D1 ; D1 = new Nth chunk size ENDCHK: RTN ; Finish last pending chunk LSTCHK: CALL GOTCHK ; process last chunk MOV D2,TOTUSE(A5) ; save used blocks (for LIT) SWAP D7 ; swap hash words CMP D7,@A0 ; check vs disk value BEQ HSHOK2 ; same - ok ; Bitmap hash total kaput - store error code 105 for now, but continue MOV #105.,ERRCOD(A5) ; remember for later HSHOK2: ENDSCN: TSTW MODFLG(A5) ; are we a LIT or an SBR? JEQ FINISH ; LIT - finish up now ; SBR parameter #1 - NDISK (F6,Bn) ; If NDISK=0 is sent, CHUNKS returns NDISK = disk number MOV NDEVIC(A5),D0 ; # of disk in device table LEA A0,2(A3) ; addr of NDISK parameters CALL GENOUT ; output (destroys A1) JLT ERRPAR ; negative - bad input ; SBR parameter #2 - DISK (S5+) ; If disk device driver not found, CHUNKS still returns DISK ; Uses: A0,A1,D0,D1 LEA A0,DEVICE(A5) ; address of local DISK name MOV 16(A3),A1 ; address of BASIC DISK name MOV 22(A3),D0 ; DISK string size (BASIC) DEVMOV: MOVB (A0)+,(A1)+ ; copy next character BEQ DEVPAD ; it was null - pad with nulls DEC D0 ; BASIC variable full? BNE DEVMOV ; no - move some more TSTB @A0 ; full - do we have overflow? BEQ DEVDON ; no - done JMP ERRPAR ; yes - error code 101 DEVPAD: DEC D0 ; string full? BEQ DEVDON ; yes - done CLRB (A1)+ ; add another null BR DEVPAD ; keep going DEVDON: ; SBR parameter #3 - BLOCKS (F6,Bn) ; MOV DEVDRV(A5),A1 ; address of device driver MOV BLOCKS(A5),D0 ; BLOCKS = total disk blocks LEA A0,26(A3) ; address of BLOCKS parameters CALL GENOUT ; output JLT ERRPAR ; negative - bad input ; SBR parameter #4 - USED (F6,Bn) ; Uses A0,D0 MOV TOTUSE(A5),D0 ; total used blocks LEA A0,40(A3) ; address of USED parameters CALL GENOUT ; output JLT ERRPAR ; negative - bad input ; SBR parameter #5 - CHUNK(n) (F6,Bn) ; Uses: A0,A2,D2 ; If NCHUNKS=0, we still return one chunk with value zero ; If there is no chunk table at all, return all zero chunk values LEA A0,52(A3) ; address of CHUNK parameters MOV CHNKTB(A5),A2 ; base of chunk table MOV NCHUNK(A5),D2 ; number of chunks RTNCHK: CMP A2,#0 ; is there a chunk table? BNE 10$ ; yes - use it CLR D0 ; no - use zero chunk size BR 20$ ; return value 10$: MOV (A2)+,D0 ; next entry in CHUNK table 20$: CALL GENOUT ; output JLT ERRPAR ; error code 101 MOV 60(A3),D0 ; get size of variable ADD D0,54(A3) ; add to variable address DEC D2 ; any more chunks? BHI RTNCHK ; yes - do then too ; Program completed successfully FINISH: ; Check for various defered errors MOV ERRCOD(A5),D0 ; get any delayed errors TST D0 ; any errors? BNE GOTERR ; yes - check them ; Following may still get an error 101 (illegal ERRCOD parameter) CALL ERRSET ; return error code 0 JMP DONE ; normal exit GOTERR: CMP D0,#100. ; minor error? BHIS NOMIN ; no - check for major error CALL ERRSET ; return in ERRCOD JMP DONE ; normal exit NOMIN: CMP D0,#104. ; disk device driver found? BNE DRVOK ; yes - ok CALL ERRSET ; no - return in ERRCOD TYPE PFILE DVRDDB(A5) ; unfound driver name TYPECR < not found> JMP ABORT ; abort DRVOK: CMP D0,#105. ; bitmap hash kaput? BNE HSHOK ; no - ok CALL ERRSET ; return in ERRCOD TYPE TTYL DEVICE(A5) ; kaput device name TYPECR < - run DSKANA> JMP ABORT ; abort HSHOK: ; [future] add any additional major deferred errors here. ; Should never arrive here - if so, we have internal CHUNKS problem TYPECR EXIT ; bad problem ... ; Following are the only two exit points from DOSBR and LITCAL. ; EXIT #1: All calls with minor errors (codes <100) exit here ; All calls with trapped (not displayed) errors exit here DONE: RTN ; minor/trap errors exit here ; EXIT #2: All calls with major displayed errors (code >100) exit here ABORT: POP A0 ; bypass return to last CALL TSTW MODFLG(A5) ; are we a LIT or an SBR? JEQ LITDON ; LIT - exit elsewhere EXIT ; serious errors abort to dot ; Return error code in ERRCOD if possible ; Input: D0 = error code ; Output: Error code stored and converted ; May bypass return and go directly to exit points ; Uses: A0,D0 (and A1,D1 in GENOUT) ; ERRSET: MOV D0,ERRCOD(A5) ; save error code (for LIT) TSTW MODFLG(A5) ; are we a LIT or an SBR? BEQ ERRTYP ; LIT - done for now CMPW @A3,#7 ; 7 or more parameters? BLO ERRTYP ; no - can't return ERRCOD LEA A0,76(A3) ; address of ERRCOD parameters CALL GENOUT ; output D0 to ERRCOD BEQ ERRBYP ; succeeded - normal exit POP A0 ; bypass return to last CALL BR ERRPR1 ; instead, generate error 101 ERRTYP: CMP D0,#100. ; is error code >100? BHIS ENDERR ; yes - major error ; Use a POP to clear return address from last CALL ERRBYP: POP A0 ; bypass return to last CALL JMP DONE ; normal exit (minor error) ENDERR: RTN ; return to display message ; The most common error is invalid parameters (code 101) ERRPAR: MOV #101.,D0 ; error 101 (bad parameters) CALL ERRSET ; return in ERRCOD ERRPR1: TYPECR TYPE < XCALL CHUNKS,NDISK,DISK,BLOCKS,USED> TYPECR <,CHUNK{(1),NCHUNKS{,ERRCOD}}> JMP ABORT ; abort ; General input routine ; Inputs: A0 = address of parameter trio (F6,Bn) ; Outputs: D0 = value of input ; = -1 if error (and indicators set) ; Uses: A1,D1 GENINP: MOV 2(A0),A1 ; address of variable CMPW @A0,#4 ; floating variable? BNE INPBIN ; no - try binary CLR D1 ; yes - clear high bytes ; CAUTION: FFTOL assumes 32-bit integers are SIGNED, FFTOX is safer ; FFTOL converts floating to signed 32-bit (ok for unsigned 31 bits) ; FFTOX converts floating to signed 40-bit (ok for unsigned 32 bits) FFTOX @A1,D0,D1 ; convert F6 to B5 (D1[8]+D0) TST D1 ; high byte zero? BNE INPBAD ; no - error BR INPEND ; done INPBIN: CMPW @A0,#6 ; binary variable? BNE INPBAD ; no - error CLR D0 ; clear answer MOV 6(A0),D1 ; size of Bn variable ADD D1,A1 ; start past end of variable CMP D1,#5 ; B1,B2,B3,B4,B5? BHI INPBAD ; no - error BLO INPLOP ; B1,B2,B3,B4 goes to loop MOVB -(A1),D0 ; B5 - is MSB zero? BNE INPBAD ; no - error DEC D1 ; got one byte INPLOP: ASL D0,#8. ; move other bytes up MOVB -(A1),D0 ; get byte DEC D1 ; any more bytes? BEQ INPEND ; no - done BR INPLOP ; get another byte INPBAD: MOV #-1,D0 ; error INPEND: TST D0 ; set indicator (-,0,+) RTN ; return ; General output routine ; Input: D0 = output value (destroyed) ; A0 = address of parameter trio (F6,Bn) ; Output: @2(A0) = D0 converted to appropriate variable type ; D0 = 0 if ok, -1 if error (and indicators set) ; Uses: A1,D1 GENOUT: MOV 2(A0),A1 ; address of variable CMPW @A0,#4 ; floating variable? BNE OUTBIN ; no - try binary CLR D1 ; yes - clear high bytes ; CAUTION: FLTOF assumes 32-bit integers are SIGNED, FXTOF is safer ; FLTOF converts signed 32-bit to floating (ok for unsigned 31 bits) ; FXTOF converts signed 40-bit to floating (ok for unsigned 32 bits) FXTOF D0,D1,@A1 ; convert B5 to F6 CLR D0 ; set up zero test BR OUTEND ; done OUTBIN: CMPW @A0,#6 ; binary variable? BNE OUTBAD ; no - error MOV 6(A0),D1 ; size of Bn variable CMP D1,#5 ; B1,B2,B3,B4,B5? BHI OUTBAD ; no - error ; must output bytes in reverse order OUTLOP: MOVB D0,(A1)+ ; output byte ASR D0,#8. ; move other bytes down DEC D1 ; any more bytes? BHI OUTLOP ; yes - output another byte TST D0 ; is anything left (overflow)? BEQ OUTEND ; no - output ok OUTBAD: MOV #-1,D0 ; error OUTEND: TST D0 ; set Z bit RTN ; return ASECT .=0 ; LIT --- impure work area (A3 used as base) PRTDDB: BLKB D.DDB ; print file DDB PRTFLG: BLKW 1 ; print flag PRTBUF: BLKB 80. ; ASCII output line LCHUNK: BLKL 1 ; number of chunks from LITFRE: BLKL 1 ; start of free memory LITEND: BLKL 1 ; end of free memory LDEVIC: BLKL 1 ; disk number ALLBLX: BLKL 1 ; total blocks (all disks) ALLUSE: BLKL 1 ; used blocks (all disks) ALLFRE: BLKL 1 ; fre blocks (all disks) LINES: BLKW 1 ; lines on current screen page LITLTH: ; LIT --- other areas, allocated as needed ; (a) Print file buffer, LIT option, size = 512 bytes .=0 ; SBR --- main work area (A5 used as base) MODFLG: BLKW 1 ; 0 for LIT , #0 for SBR NAMBLK: BLKW 3 ; file name block for SRCH call RAMFRE: BLKL 1 ; start of free memory RAMEND: BLKL 1 ; end of free memory DEVENT: BLKL 1 ; address of device table entry DEVDRV: BLKL 1 ; address of device driver NDEVIC: BLKL 1 ; NDISK (input) DEVICE: BLKB 12. ; DISK ("devnn:" + null + spare) EVEN NCHUNK: BLKL 1 ; number of chunks requested by user BLOCKS: BLKL 1 ; total BLOCKS on disk TOTUSE: BLKL 1 ; total used blocks on disk (LIT) CHNKTB: BLKL 1 ; address of chunk table CHNKTE: BLKL 1 ; address of last chunk in table ERRCOD: BLKL 1 ; error code (LIT) DVRDDB: BLKB D.DDB ; disk device driver DDB BITDDB: BLKB D.DDB ; bitmap DDB MEMLTH: ; length of first initial allocation ; SBR --- other areas, allocated as needed ; (a) Initial DRV buffer for LOOKUP, size = 512 bytes ; (b) Final DVR buffer, size of xxx.DVR, replaces (a) ; (c) Bitmap buffer, size of bitmap ; (d) Chunks table, size = 4 x chunks END .