jnw@mcnc.UUCP (John White) (01/30/85)
# use sh to extract
echo extracting README
cat >README <<xyzzy
The following utilities should be made with the DeSmet C package.
All are for MS-DOS 2.x and were written on a Tandy 2000 by me and
will work on the IBM-PC. Int.a is set up for the IBM-PC as sent.
ram.sys, setram.exe -- a ramdisk whose memory can be allocated and freed
without rebooting. Make with makeram.bat
int.com -- a terminate-and-stay-resident program that allows a program
to be interupted when in a CPU loop.
This program will not port to other MS-DOS machines without
modification (see int_port.doc). Make with makeint.bat
ffind.exe -- a find program that is fast and can search a large number
of files easily. Compile and link normally.
Bennet Todd has agreed to supply binaries to anyone who doesn't have
the DeSmet package and who can't find anyone who does. He can be reached at
Bennett Todd -- The Happy Hacker
...{decvax,ihnp4,akgua}!mcnc!ecsvax!bet
I hope you find these programs useful. If you find any undocumented
"features" you can mail them to me at mcnc!jnw
John N. White
xyzzy
echo extracting ram.a
cat >ram.a <<xyzzy
; RAM.A MS-DOS V2.0 RAM DISK PROGRAM
; assemble with the DeSmet assembler
; Like the IBM ramdisk but with a setram feature. - John N. White
SRH_LEN EQU 13 ;LENGTH
SRH_STA_FLD EQU 3 ;STATUS FIELD
DTA EQU SRH_LEN+1 ;DATA TRANSFER ADDRESS
COUNT EQU DTA+4
SSN EQU COUNT+2 ;START SECTOR NUMBER
RET_BYTE EQU DTA ;RETURN BYTE
BPBA_PTR EQU COUNT ;POINTER TO BPB
; INITIALIZE
UNITS EQU SRH_LEN
BR_ADDR_0 EQU SRH_LEN+1
BR_ADDR_1 EQU BR_ADDR_0+2
BPB_PTR_OFF EQU BR_ADDR_0+4
BPB_PTR_SEG EQU BPB_PTR_OFF+2
CSEG
; DEVICE HEADER
NEXT_DEV DW -1,-1
ATTRIBUTE DW 6000H ;IOCTL supported
STRATEGY DW DEV_STRATEGY
INTERRUPT DW DEV_INT
DEV_NAME DB 1 ;NUMBER OF DEVICES
DB 0,0,0,0,0,0,0
RH_OFF DW 0 ;REQUEST HEADER OFFSET
RH_SEG DW 0 ;SEGMENT
; CURRENT INFORMATION
TOTAL DW 0 ;NUMBER OF SECTORS TO TRANSFER
START_SEC DW 0 ;FIRST SECTOR
VDISK_PTR DW 0
USER_DTA DW 0,0 ;USERS DATA TRANSFER ADDRESS
BPB DW 512 ;PER SECTOR
DB 1 ;ALLOCATION UNIT
DW 0 ;0 RESERVED
DB 1 ;1 FAT
DW 32 ;DIRECTORY ENTRIES
SECTORS DW 10H ;SECTORS TOTAL
MEDIA_TYPE DB 0FCH ;MEDIA BYTE
FAT_SIZE DW 3 ;SECTORS IN FATS
BPB_PTR DW BPB
MEDIA_IN DB -1 ;MEDIA IN DRIVE IF 1
FUNTAB:
DW INIT
DW MEDIA_CHECK
DW BUILD_BPB
DW IOCTLI
DW INPUT
DW EXIT
DW EXIT
DW EXIT
DW OUTPUT
DW OUTPUT ;DONT BOTHER VERIFYING
DW EXIT
DW EXIT
DW IOCTLO
IN_SAVE:MOV AX,ES:WORD [BX+DTA] ;SAVE CALLERS DTA
MOV DS:WORD USER_DTA,AX
MOV AX,ES:WORD [BX+DTA+2]
MOV DS:WORD USER_DTA+2,AX
MOV AX,ES:WORD [BX+SSN] ;START SECTOR
MOV DS:START_SEC,AX
MOV AX,ES:WORD [BX+COUNT] ;COUNT OF SECTORS
MOV AH,0
MOV DS:WORD TOTAL,AX
RET
CALC_ADDR:
MOV AX,DS:START_SEC
MOV CL,5 ;TURN SECTORS INTO PARAGRAPHS
SHL AX,CL
ADD AX,DS:VDISK_PTR
MOV CH,DS:BYTE TOTAL ;CX=LENGTH IN WORDS
MOV CL,0
MOV DS,AX ;MOVE ADDRESS TO DS:SI
XOR SI,SI
RET
; STRATEGY ENTRY
DEV_STRATEGY:
MOV CS:RH_SEG,ES ;SAVE ES:BX
MOV CS:RH_OFF,BX
LRET
; DEVICE INTERRUPT HANDLER
DEV_INT:
PUSH DS ;SAVE STATE
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
; SET STATUS TO ALL OK
MOV ES:WORD [BX+SRH_STA_FLD],100H
MOV AL,ES:[BX+2] ;GET FUNCTION BYTE
SHL AL,1 ;TIMES 2
MOV AH,0
MOV DI,AX ;FUNCTION OFFSET
PUSH CS
POP DS
JMP WORD FUNTAB[DI]
; MEDIA CHECK
;media status is -1 after release of memory
MEDIA_CHECK:
MOV AL,DS:MEDIA_IN
MOV ES:BYTE [BX+RET_BYTE],AL
JMP EXIT
; BUILD BIOS PARAMETER BLOCK
BUILD_BPB:
MOV ES:WORD [BX+BPBA_PTR],OFFSET BPB
MOV ES:WORD [BX+BPBA_PTR+2],CS
CMP DS:MEDIA_IN,1
JE EXIT
CMP DS:VDISK_PTR,0
JZ EXIT
MOV DS:MEDIA_IN,1
CLD
MOV ES,DS:VDISK_PTR
MOV DI,0 ;ZERO OUT FAT
MOV AL,DS:MEDIA_TYPE
XOR AL,1 ;FORCE MS-DOS TO LOOK AT SECTOR COUNT
MOV DS:MEDIA_TYPE,AL
STOSB ;SET FIRST 3 BYTES OF FAT
MOV AX,0FFFFH
STOSW
MOV CX,16*512-3 ;SIZE OF REST OF FAT + DIRECTORY
XOR AL,AL
REP STOSB ;FAT IS ZERO
JMP EXIT
; READ A SECTOR
INPUT:
CMP DS:MEDIA_IN,1 ;MAKE SURE MEDIA IN DRIVE
JNE BAD_EXIT
CALL IN_SAVE
CALL CALC_ADDR ;GET SECTOR ADDRESS
LES DI,CS:USER_DTA ;LOAD DESTINATION ADDRESS
CLD
REP MOVSW ;MOVE READ DATA
JMP EXIT
; WRITE A SECTOR
OUTPUT:
CMP DS:MEDIA_IN,1 ;MAKE SURE MEDIA IN DRIVE
JNE BAD_EXIT
CALL IN_SAVE
CALL CALC_ADDR ;GET DESTINATION ADDRESS
PUSH DS
POP ES ;PUT DESTINATION INTO ES:DI
MOV DI,SI
LDS SI,CS:USER_DTA ;LOAD SOURCE ADDRESS
CLD
REP MOVSW
JMP EXIT
BAD_EXIT:
MOV ES:WORD [BX+SRH_STA_FLD],8002H
JMP EXIT
; IOCTLI
IOCTLI:
CALL IN_SAVE
MOV AX,DS:VDISK_PTR
LDS DI,DS:USER_DTA
MOV DS:[DI],AX
JMP EXIT
; IOCTLO
IOCTLO:
CALL IN_SAVE
PUSH DS
LDS DI,CS:USER_DTA
MOV AX,DS:[DI]
MOV CX,DS:[DI+2]
POP DS
MOV DS:VDISK_PTR,AX
MOV AX,CX
MOV DS:SECTORS,AX
SHR AX,1 ;FIND FAT SIZE
ADD AX,CX
MOV CL,9
SHR AX,CL
INC AX
MOV DS:WORD FAT_SIZE,AX
MOV DS:BYTE MEDIA_IN,-1
; COMMON EXIT
EXIT: POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
LRET
; INTILLIZATION
INIT:
MOV AX,OFFSET INIT ;RAM IS AT END OF THIS CODE
MOV CL,4 ;MAKE PARAS
SHR AX,CL
INC AX ;1 FOR ROUND UP
PUSH CS
POP DI
ADD AX,DI ;ADD CURRENT CS
PUSH ES
POP DS
MOV DS:WORD [BX+BR_ADDR_0],0 ;SET FREE ADDRESS
MOV DS:WORD [BX+BR_ADDR_1],AX
MOV DS:BYTE [BX+UNITS],1 ;ONE UNIT
MOV DS:WORD [BX+BPB_PTR_OFF],OFFSET BPB_PTR
MOV DS:WORD [BX+BPB_PTR_SEG],CS
JMP EXIT
END
xyzzy
echo extracting ram.doc
cat >ram.doc <<xyzzy
This ramdisk starts out with no memory. To allocate memory for it use:
setram drive kbytes
where drive is the drive letter and kbytes is the number of kbytes to alloc.
Memory can only be allocated when there is currently no memory allocated
to the ramdisk.
example, if the ramdisk is drive e and a 68k ramdisk is desired
setram e 68
To deallocate the memory that was allocated for the ramdisk, use:
setram drive
where drive is the drive letter.
example, if the ramdisk is drive e
setram e
This ramdisk allows up to 32 directory entries in the root directory.
To use it, there must be a line in the config.sys file
device=ram.sys
and if an initial ramdisk is desired, the line
setram drive kbytes
can be put in the autoexec.bat file.
Don't allocate memory if there is currently in memory a program that will
terminate and free up its memory, as the setram memory will be allocated after
this program and memory will become segmented. Also, don't run a
terminate-and-stay-resident program when memory is allocated for the ramdisk
because when the ramdisk memory is deallocated memory will be segmented.
If memory is deallocated in a .bat file, then memory will be segmented until
the .bat file terminates, so don't do anything in that bat file after
a deallocation if this segmentation could cause a problem (such as
reallocating memory with setram or running a terminate and stay resident
program).
xyzzy
echo extracting setram.c
cat >setram.c <<xyzzy
/* Set the ramdisk to the specified number of kbytes */
/* must be linked with -s1000 */
/* written by John N. White */
unsigned size;
unsigned zero[2],loci[256];
int err,drive,loc,old;
main(argc,argv)
char **argv;{
int i;
if(argc<2){
usage: puts("setram - program to set size of ramdisk in kbytes\n");
puts("usage:\n");
puts("setram drive kbytes to allocate memory for ramdisk\n");
puts("setram drive to free memory from ramdisk");
exit(1);
}
i=argv[1][0];
if(i>='a') i-=32; /* set to upper case */
drive=i-'A'+1;
if(drive<0 || drive>31) goto usage;
getold();
if(old){
if(argc>2){
puts("ramdisk already has memory");
exit(1);
}
clear();
exit(0);
}
if(argc<3){
puts("no memory allocated to ramdisk");
exit(1);
}
size=atoi(argv[2]);
if(size<8 || size>800) goto usage;
loci[1]=size<<1;
size<<=6;
#asm
mov ah,4ah ;modify programs allocated block
mov cx,cs
sub cx,10h
mov word loc_,cx
mov word loci_,cx
mov es,cx ;start of partition
mov bx,word size_ ;new size
int 21h
jc err
mov ax,cs ;get pointer to environment block
sub ax,10h
mov es,ax
mov es,es:[2Ch]
mov ah,49h ;dealloc environment block
int 21h
jc err
mov ax,4405h ;IOCTL call to ramdisk
mov bx,word drive_
mov dx,offset loci_
mov cx,4
int 21h
jnc ok
err:
mov word err_,ax
jmp main_error_
ok:
mov ax,3100h
mov dx,word size_
int 21h
jmp err
#
error:
puts("error number ");
err&=63;
if(err>9) putchar(err/10+'0');
putchar(err%10+'0');
puts(" (decimal)");
exit(1);
}
getold(){
#asm
mov ax,4404h ;IOCTL call to ramdisk
mov bx,word drive_
mov dx,offset loci_
mov cx,2
int 21h
jc err
#
old=loci[0];
}
/* Clear the ramdisk */
clear(){
#asm
mov ah,49h ;free mem
mov es,word old_
int 21h
jc err
mov ax,4405h ;IOCTL call to ramdisk
mov bx,word drive_
mov dx,offset zero_
mov cx,4
int 21h
jc err
#
return;
}
xyzzy
echo extracting makeram.bat
cat >makeram.bat <<xyzzy
echo off
rem make ram.sys and setram.exe using the DeSmet assembler
later setram.c setram.exe
if not errorlevel 1 goto nosetram
c88 setram
if errorlevel 1 goto stop
echo
bind setram -s1000
if errorlevel 1 goto stop
echo
del setram.o
:nosetram
later ram.a ram.sys
if not errorlevel 1 goto stop
asm88 ram
if errorlevel 1 goto stop
echo
bind ram -a -sFFFF
echo
del ram.o
exe2bin ram.exe ram.sys
del ram.exe
:stop
xyzzy
echo extracting int.a
cat >int.a <<xyzzy
;Program to allow a program to be interupted at any time.
;assemble with bat file makeint, for tandy 2000
;Assemble with the DeSmet assembler using the make file makeint.bat
;Written by John N. White last mod 1/25/85
;Note that some tricks were used to get a .com file with an interupt handler
;with the DeSmet package which normally produces .exe files.
;The timer interupt is intercepted and every 20 ticks this code is entered.
;If the cs:ip that was being executed is greater than the end of this
;program (which is terminate and stay resident) then a check is made on
;the keyboard status. If both shifts and the alt key are depresed then
;the program is terminated by putting an int 21h into its code and jumping to
;it. The address that was interupted will be displayed. If cs:ip is less
;than the end of this program then every tick will be checked instead of every
;20. The normal clock tick point is entered after this program finishes.
DAT equ +100h ;The DeSmet stuff assumes CS:0 is the
;start while in a .com file CS:100 is.
EXTRA equ 0Ch ;This is the extra bytes on the stack
;over what a simple interupt should
;have. Tandy 2000 = 1Ah, IBM = 0Ch
;(for dos 2.x)
cseg
jmp init
check:
sti
push bp
push ax
push cx
mov bp,sp
mov ax,[bp+6+EXTRA] ;1A extera stuff on stack over raw int
mov cl,4
shr ax,cl
add ax,[bp+8+EXTRA]
cmp ax,cs:addr DAT
jbe next_tick
cmp ax,0C000h
jae next_tick
mov ah,2
int 16h
not al
and al,0Bh ;see if both shifts and alt key pressed
jz interupt_process
next_tick:
pop cx
pop ax
pop bp
tick: db 0EAh,0,0,0,0 ;will be set by init
interupt_process:
push bx
push si
;grab old cs:ip of doomed program
mov bx,[bp+6+EXTRA]
push bx
mov bx,[bp+8+EXTRA]
;cause return to die instead of return to doomed program
mov word [bp+6+EXTRA],offset die DAT
mov word [bp+8+EXTRA],cs
mov si,offset csnum DAT
mov cx,4
csloop:
call cdigit
loop csloop
mov si,offset ipnum DAT
pop bx
mov cx,4
iploop:
call cdigit
loop iploop
mov si,offset dstring DAT ;display dstring
dloop:
mov bl,0
mov al,cs:[si]
inc si
and al,al
jz done
mov ah,14
int 10h ;display char
jmp dloop
done:
pop si
pop bx
jmp next_tick
die:
mov ax,4c01h ;terminate current process
int 21h
;convert lowest 4 bits in bx (>>=4) to hex digit at [si--]
cdigit:
mov al,bl
and al,0Fh
shr bx,1
shr bx,1
shr bx,1
shr bx,1
cmp al,9
jle noadd
add al,7
noadd: add al,30h
mov cs:byte [si],al
dec si
ret
addr: dw 0FFFFh ;segment addr at end of this code
dstring:db 0Dh,0Ah,'c','s','=',0,0,0
csnum: db 0,',',' ','i','p','=',0,0,0
ipnum: db 0,0Dh,0Ah,0
init:
mov ax,351Ch ;get tick interupt vector
int 21h
mov ds:word tick+1 DAT,bx
mov ds:word tick+3 DAT,es
mov ax,251Ch ;set new tick vector
mov dx,offset check DAT
int 21h
mov ax,cs
mov dx,offset init+0Fh DAT
mov cl,4
shr dx,cl
add ax,dx
mov ds:addr DAT,ax
mov ax,3100h ;terminate and stay resident
int 21h
end
xyzzy
echo extracting int.doc
cat >int.doc <<xyzzy
int.com --- written by John N. White for the Tandy 2000 with ms-dos 2.x
int.com allows a program to be interupted at any time (not just when a
function call is being processed).
To use, just run int.com (note: only run it one time after a bootup).
Then, to interupt a program, hold down both shift keys and the alt key all
at the same time untill the program terminates. The cs and ip registers
will be displayed.
This is a terminate and stay resident program. Every time an int 1C (hex)
occures, (from the timer, about 20 times a second) the location of the
routine that was being executed is examined. If the code follows the
end of the int.com code then a bios call (16h with ah=2) is done to check
the shift status of the keyboard. If the appropriate keys are being held
down, then the return address to the program is modified to point to
a program terminate function call in int.com .
If more than one terminate-and-stay-resident programs will be run, this
should be the last one because it may interupt any program that follows it.
int.com will not interupt code preceeding it so dos function calls and
system interupt handlers will not be interupted. This means that no interupt
will occur if a program is waiting for keyboard input as a bios call handles
that. Also, no interupt will occur if the keyboard hold is on.
Interupts must be enabled or the timer tick won't be seen. Normally programs
start with interupts enabled but in debug they start disabled. Interupts
can be enabled in debug by typeing rf followed by ei .
xyzzy
echo extracting int_port.doc
cat >int_port.doc <<xyzzy
The program int.com is not necessarily portable between different
ms-dos machines. The unportability is due to the value of EXTRA in the source.
If int 1Ch was a hardware interupt, the value of EXTRA is 0. But int 1Ch is
usually a software interupt invoked from the timer tick interupt handler
(int 8) and thus int.com must reach deeper into the stack to find the programs
cs:ip values. The value of extra can be found in two ways.
The debugger can be used to look through the interupt code that calls
int 1Ch and the value of EXTRA can be guessed. There are 6 bytes for an
extra interupt, 2 bytes for each push, 16 bytes for a pushall on the 186.
The Tandy 2000 has int 8, a pushall, and 2 pushes in effect so
6+16+(2*2)=26 (=1Ah) is the value of EXTRA.
The IBM has int 8, and 3 pushes in effect so
6+(2*3)=12 (=0Ch) is the value of EXTRA.
Another way is to enter the debugger and type: (The int3 should end up at 113)
a100
cli
mov ax,0
mov ds,ax
mov word [70],113
mov [72],cs
sti
jmp 111
int3
r
g
Now, if everything is working properly, you will have two register dumps
to compare. Subtracting the SP value of the second from the first gives the
number of bytes added to the stack by the interupts. Subtract 6 from this
value to get the value EXTRA should have. (Be sure to do the arithmetic
carefully, The SP values are in hex). By the way, your machine will need
to be rebooted now.
Example:
For the Tandy 2000 the SP=FFEE and FFCE. (hex)
EEh-CEh=20h, 20h-6=1Ah and the value for EXTRA is 1Ah
For the IBM the SP=FFEE and FFDC. (hex)
EEh-DCh=12h, 12h-6=0Ch and the value for EXTRA is 0Ch
Note that int.com only interupts a program when the cs:ip values are
larger than the end of the int.com code and smaller than C0000 (hex).
I don't know whether this is appropriate for all ms-dos machines.
xyzzy
echo extracting makeint.bat
cat >makeint.bat <<xyzzy
echo off
rem make int.com
asm88 int
echo
bind int -a -sFFFF
echo
del int.o
exe2bin int.exe int.com
del int.exe
xyzzy
echo extracting ffind.c
cat >ffind.c <<xyzzy
/* Fast find for searching large source for a string - John N. White */
/* The first arg is the string, any additional args must be on the same line
* (of the source file) in the correct order for the line to be printed.
* The names of files to be searched are read from stdin untill EOF */
#define stdin 0 /* DeSmet value for stdin */
int f,curflag,fs,fe;
char *fname,fbuf[2048],line[258],curfile[128];
char *le,*lend= &line[256];
main(argc,argv)
char **argv;{
int i,j;
if(argc<2){
puts("need string to search for");
exit(1);
}
findall(argc,argv);
}
/* find next file and set f to it */
nextfile(){
int c,i;
if(f>0) close(f);
curflag=1;
fe=fs=2048;
loop:
while((c=getc(stdin))<=' ' || c==',') if(c<0) exit(0);
i=0;
do{ if(i<127) curfile[i++]=c; }
while((c=getc(stdin))>' ' && c!=',');
curfile[i]=0;
f=open(curfile,0);
if(f<=0){
puts("******** can't open file ");
puts(curfile);
puts("\n");
goto loop;
}
}
/* find all lines in the current file containing string */
findall(argc,argv)
char **argv;{
char *p,*q,*r,*string;
int c;
string=argv[1];
c= *string;
loop:
getline();
for(p=line;p<le;p++) if(*p==c){
for(q=p,r=string;*r && *r== *q;q++,r++);
if(!*r && cmpnext(argc,argv,q)){
if(curflag){
puts("---- file: ");
puts(curfile);
puts(" ----\n");
curflag=0;
}
puts(line);
puts("\n");
goto loop;
}
}
goto loop;
}
/* compare next arg with remainder of string, ret true if ok */
cmpnext(argc,argv,cline)
char **argv, *cline;{
char *p,*q,*r,*string;
int c;
if(argc<3) return(1); /* if no more args */
string=argv[2];
c= *string;
for(p=cline;p<le;p++) if(*p==c){
for(q=p,r=string;*r && *r== *q;q++,r++);
if(!*r && cmpnext(argc-1,argv+1,q)) return(1);
}
return(0);
}
/* get the next line of the current file into line */
getline(){
int j;
loop:
if(fe<1) nextfile();
for(le=line;
(j=fs<fe?fbuf[fs++]:((fs=1,(fe=read(f,fbuf,2048))<1)?'\n':*fbuf))!='\n'
&& le<lend;
le++) *le=j;
*le=0;
if(! *line) goto loop;
}
xyzzy
echo extracting ffind.doc
cat >ffind.doc <<xyzzy
ffind - written by John N. White
ffind scans files whose names are entered through stdin (until end-of-file).
If the args match a line in a file, then the name of the file and all lines
with matches are printed.
The args are considered to match a line if all the args are contained in the
line and in the correct order.
The filenames fed in may be separated by spaces, tabs, commas, or new-lines.
Example:
ffind if ( ){
matches:
if(i==0){
if ( i == 0 ){
but not
if(i==0) {
(i==0){ if
Note that if a set of files are to be searched many times, The names of those
files can be put in a file (say, "list") and a macro can be
defined (say, "f.bat") that scans those files. f.bat whould contain:
ffind %1 %2 %3 %4 %5 %6 %7 %8 %9 < list
When entering filenames by hand remember end-of-file is CTRL Z.
xyzzy