home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ramdisk
/
adjram41.arc
/
ADJRAM.C
next >
Wrap
C/C++ Source or Header
|
1988-05-15
|
53KB
|
1,509 lines
/* Adjustable Ram Disk
(c) Copyright 1986,1987 by Gary Cramblitt. All Rights Reserved.
v2.2 1 Jul 86 Initial version
v2.3 24 Aug 86 Bug. FAT media byte not updated properly.
v2.4 29 Aug 86 If current drive is memory disk drive, reset current
directory on exit.
v2.5 30 Aug 86 Increase FAT to permit max size of 2043K;
Increase size of root directory to 128 entries;
Start code for /E option (Expanded Memory Support)
v3.0 30 Aug 86 Finish code for /E option.
v3.1 2 Oct 86 Fix shrink bug. Not packing subdirectories properly
v3.2 18 Oct 86 Allow SIZE= clause for AMDISK. Also allow drive letters
A through L.
v4.0 3 Jan 87 Add reserved conventional memory support as
R:xxxx:nn option.
Full support for expanded memory mixed with conventional
memory.
Max size of memory disk based upon available memory and
cluster size.
User requested expansion or shrinkage automatically
adjusted to within limits and rounded to nearest
memory block boundary.
In expanded memory, use larger block size (64K) to avoid
excessive usage of EMM handles.
Display Memory Block Table only if /t option specified.
Bug. Stop processing directory when first "never_used"
flag is encountered.
Bug in DeSmet free() routine. Causes memory overflo.
Calculate stack space manually instead.
Restrict program size to under 32K.
Clean up error reporting. Tell user if error has
corrupted the memory disk.
v4.1 15 May 88 Restrict reserved memory to above segment a000.
Initialize reserved memory when block is allocated.
For program usage, see the last routine.
A few definitions:
1. CONVENTIONAL MEMORY is that memory directly addressable
by the 8088 or 8086 CPU and controlled by DOS. On the
IBM PC, it falls below 640K.
2. RESERVED MEMORY is also directly addressable by the
8088 or 8086, but it is not controlled by DOS. On
the IBM PC, it falls above 640K and below 1MB. It
is usually reserved for use by the PC's BIOS. If your
machine has memory mapped to addresses between 640K and
1MB and that memory is not needed for any other purpose,
and it is directly addressable by the CPU (no special
"paging" or "mapping" required to access it), then
ADJRAM can use it via the R option. See .DOC
3. EXTENDED MEMORY is addressed above 1MB. It is only
available on AT type machines (80286 or 80386 CPUs).
It is not currently supported by ADJRAM.
4. EXPANDED MEMORY is memory conforming to the Lotus/
Intel/Microsoft Expanded Memory Specification. It
is supported by ADJRAM if you have the EMM loaded.
This program is coded in DeSmet C v2.4. Any function beginning
with underscore (_) is a non-standard routine from the DeSmet library.
They are:
_showcs()
Synopsis: unsigned _showcs()
Returns the current value of CS.
_showds()
Synopsis: unsigned _showds()
Returns the current value of DS (= SS in DeSmet C).
_showsp()
Synopsis: char *_showsp()
Returns the current value of SP.
_setsp()
Synopsis: void _setsp(newStackValue);
unsigned newStackValue;
Sets the SP register to the specified value.
_memory()
Synopsis: char *_memory()
Returns a pointer to the first byte of free memory past
the end of initialized memory (global data).
_peek()
Synopsis: char _peek(offset,segment)
char *offset;
unsigned segment;
Returns the byte at specified far address.
_doint()
Synopsis: set any or all of the externs
_rax,_rbx,_rcx,_rdx,_rsi,_rdi,_res,_rds
followed by
_doint(interrupt number);
Performs the specified interrupt with the specified
registers set from the externs.
After the call, _rax, etc. can be used. _carryf
and _zerof are extern char variables set to 1 if
the carry or zero flag is set.
In addition, the following routines are semi_standard, and may have
slightly different implementations with your compiler:
strncmp()
Synopsis: char *strncmp(leftstring,rightstring,max)
char *leftstring, *rightstring
int max
strncmp() compares up to a specified number of chars
in two strings. It returns 0 if the specified number
of characters in the string are the same.
*/
/* ==== Definitions ==== */
/* ---- Overall Definitions ---- */
#define true 1
#define false 0
/*
---- To compile without LOTUS/INTEL/Microsoft Expanded Memory
support, change the "1" to "0" in the next statement. Will
save about 1500 bytes in EXE file.
*/
#define em_support 1 /* compile for EM support */
/* ---- To compile without reserved memory support, change
the "1" to "0" in the next statement.
*/
#define rm_support 1 /* compile for reserved memory */
#define debug 0 /* compile with debug stmts */
/* ---- Other customizable symbols */
#define start_rm_addr 0xa000 /* start of reserved memory */
/*
The following symbols must correspond to the same symbols in
file amdisk.asm. If one is changed, so must the other.
This is because the first memory block may not be
deallocated.
*/
#define def_size_K 64 /* default to 64K RAM disk */
#define min_size_K (boot_sec.mem_blk_table[0].siz/sec_per_K)
#define min_size_sec boot_sec.mem_blk_table[0].siz
#define em_sec_per_blk 512 /* increment in 256K Expanded */
/* Memory blocks */
#define cnv_sec_per_blk 64 /* increment in 32K conventional*/
/* memory blocks */
/*
---- Disk definitions. Note: These constants should be made into
variables or functions if this program's algorithms need to
be generalized to disks of any type, especially high density disks.
Since this program works only in conjunction with amdisk.asm,
it is OK to make them constants here.
*/
#define bytes_per_sec 512 /* 512 bytes per sector */
#define par_per_sec (512/16)
/* 32 paragraphs per sector */
#define sec_per_K (1024/512)
/* sectors per 1024 bytes */
#define dir_per_sec (512/sizeof(struct dir_entry))
/* directory entries per sector */
#define K_per_blk (sec_per_blk/sec_per_K)
/* K per memory block */
#define em_pag_per_blk (K_per_blk/16)
/* 16K EM pages per mem block */
#define max_clusters 4086 /* max clusters per disk
(12-bit FAT entries) */
#define max_mem_blks (max_clusters/cnv_sec_per_blk)
/* Maximum number of memory blocks */
/* assuming cluster size of 1 */
#define bytes_per_fat (max_clusters*3/2)
#define sec_per_fat ((bytes_per_fat+bytes_per_sec-1)/bytes_per_sec)
#define fat_size (sec_per_fat*bytes_per_sec)
/* ---- Program Segment Prefix ---- */
#define environment_segment 0x2c /* segment address of the environment */
/* ---- DOS interrupts ---- */
#define dosi_dosf 0x21 /* DOS function interrupt */
#define dosi_dsk_read 0x25 /* DOS absolute disk read interrupt */
#define dosi_dsk_write 0x26 /* DOS absolute disk write interrupt */
/* ---- User interrupts ---- */
#define usri_emm 0x67 /* LOTUS/INTEL/Microsoft Expanded
Memory Manager */
/* ---- DOS functions ---- */
#define dosf_seldisk 0x0e /* set default disk */
#define dosf_getdisk 0x19 /* get current default disk */
#define dosf_getver 0x30 /* get DOS version number */
#define dosf_keepprc 0x31 /* keep process (term and stay resident) */
#define dosf_drvfre 0x36 /* get disk free space */
#define dosf_chdir 0x3b /* set default directory */
#define dosf_openh 0x3d /* open file handle */
#define dosf_closeh 0x3e /* close file handle */
#define dosf_ioctl 0x44 /* IOCTL */
#define dosf_cwd 0x47 /* get current directory */
#define dosf_alloc 0x48 /* allocate memory block */
#define dosf_dealloc 0x49 /* deallocate memory block */
#define dosf_setblk 0x4a /* modify memory block */
/* ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ---- */
#define emm_status 0x40 /* get manager status */
#define emm_get_PFseg 0x41 /* get page frame segment */
#define emm_get_pages 0x42 /* get number of pages */
#define emm_get_handle 0x43 /* get handle and allocate memory */
#define emm_map_memory 0x44 /* map memory */
#define emm_fre_handle 0x45 /* free handle and memory */
#define emm_get_ver 0x46 /* get EMM version */
#define emm_sav_map 0x47 /* save mapping context */
#define emm_res_map 0x48 /* restore mapping context */
#define emm_num_handles 0x4b /* get number of EMM handles */
#define emm_hdl_pages 0x4c /* get pages owned by handle */
#define emm_all_pages 0x4d /* get pages for all handles */
#define emm_pag_map 0x4e /* get or set page map */
#define nor_flg 0 /* memory block is in normal memory */
#define em_flg 1 /* memory block is in expanded memory */
#define rm_flg 2 /* memory block is in reserved memory */
/*
---- DOS Errors ----
These codes are returned by program and can be tested with DOS
IF statement.
*/
#define dose_noerr 0 /* no error */
#define dose_invfunc 1 /* invalid function */
#define dose_too_many_dir 4 /* too many directories */
#define dose_arena 7 /* arena trashed */
#define dose_noram 8 /* not enough memory */
#define dose_inv_env 10 /* invalid environment */
#define dose_invdrv 15 /* invalid drive */
/* ---- Directory definitions ---- */
#define never_used 0 /* directory entry never used */
#define erased 0xe5 /* file has been erased */
/* ---- Directory Attribute Byte ---- */
#define RO_bit 0x01 /* this bit indicates read-only */
#define hidn_bit 0x02 /* this bit indicates hidden file */
#define sys_bit 0x04 /* this bit indicates system file */
#define vol_bit 0x08 /* this bit indicates volume label */
#define dir_bit 0x10 /* this bit indicates subdirectory */
#define arc_bit 0x20 /* this bit indicates modified file */
/* ---- FAT definitions ---- */
#define available 0 /* cluster is available for use */
#define bad 0xff7 /* cluster is bad */
#define last_low 0xff8 /* last cluster for the file */
#define last_high 0xfff /* last cluster for the file */
extern unsigned _rax, _rbx, _rcx, _rdx, _rsi, _rdi, _res, _rds;
extern char _carryf, _zerof;
/* ---- Directory entry ---- */
struct dir_entry {
union dir_name {
char name[8];
unsigned char status;
} u_name;
char ext[3];
unsigned char attr;
unsigned char reserved[10];
unsigned int time;
unsigned int date;
unsigned int first_cluster;
unsigned long size;
};
/* ==== Global Data Storage ==== */
unsigned int pgm_seg; /* CS at start of program saved here */
int drive_number; /* memory disk drive number A=0, B=1, etc */
int mdisk_size_K; /* memory disk size in K */
int user_chg; /* user's desired change in sectors */
int mdisk_chg; /* the difference between disk's current size
and the final size (in sectors) */
int free_secs; /* unused sectors in the memory disk */
int first_dir_sector;
int last_dir_sector; /* loc of directory */
int first_fat_sector;
int last_fat_sector; /* loc of FAT */
int free_cluster; /* ptr to first free cluster */
int first_data_sector; /* first sector after the last FAT */
int avail_mem_blks; /* max expansion size in memory blocks */
int sec_per_blk; /* sectors per memory block */
int gbl_argc; /* command line argument count */
char *gbl_argv; /* command line argument vector table */
unsigned char expansion_type; /* 0 = expand using conventional memory */
/* 1 = expand using expanded memory */
/* 2 = expand using reserved memory */
unsigned int user_rm_addr; /* user-specified reserved memory */
/* starting paragraph address */
/* ---- Memory Disk Boot Record ---- */
struct boot_record {
unsigned char jmp[3]; /* non-bootable (no jump instruction ) */
char ident[8]; /* identification */
unsigned int bytes_in_sector;/* bytes/sector */
unsigned char sec_per_cluster;/* sectors/cluster */
unsigned int bpb_reserved; /* reserved sectors */
unsigned char bpb_fats; /* number of FAT's */
unsigned int bpb_root; /* directory entries in root */
unsigned int bpb_total; /* total number of sectors */
unsigned char bpb_media; /* media byte = number of mem blocks */
unsigned int bpb_fat_size; /* sectors/FAT */
unsigned int sec_per_track; /* sectors/track */
unsigned int heads; /* number of heads */
unsigned int hidden; /* hidden sectors */
struct mem_blk_table_entry {
unsigned char typ; /* type of blk: 0 = normal 1 = EM */
unsigned int par; /* paragraph address of block */
unsigned int siz; /* number of sectors in the memory block */
unsigned int hdl; /* expanded memory handle */
} mem_blk_table[max_mem_blks];
char rest_of_record[bytes_per_sec - sizeof(struct boot_record)];
} boot_sec;
/* ---- Memory disk default pathname ----- */
struct pathname {
char drv;
char colon;
char slash;
char dir[64];
} mdisk_pathname; /* Ram disk's current pathname */
/* ---- Default drive ---- */
unsigned char default_drive;
/* ---- Expanded Memory Manager ----- */
unsigned int em_PFseg; /* EMM's page frame segment */
char em_device_name[] = "EMMXXXX0"; /* EMM device name */
/* ---- Declare types of library functions which return non-integer values */
char *strncmp(); /* compares 2 strings of specified length */
unsigned _showcs(); /* returns current CS register */
unsigned _showds(); /* returns current DS = SS register */
char *_showsp(); /* returns current SP register */
void _setsp(); /* sets SP to specified value */
char *_memory(); /* returns pointer to end of initialized memory */
char _peek(); /* returns a byte given segment and offset */
/* ==== MAIN ROUTINE ==== */
main(argc, argv)
int argc;
char *argv[];
{
/*
---- Begin by saving the CS, which points to just after the 256-byte
PSP. By subtracting 16, we get the segment pointer for the
program segment. This is, of course, highly DeSmet C dependent
code. Hopefully, your compiler has some mechanism for getting
the program segment pointer.
*/
pgm_seg = _showcs() - 16;
gbl_argc = argc;
gbl_argv = argv;
/* ---- Now move the stack down below 32K, so that we don't clobber
DOS memory arena blocks we will be creating if expanding.
New stack pointer is computed so that sum of code, global
variables, and stack is less than 32K. DeSmet run-time memory
looks like this:
pgm_seg --> program segment prefix (256 bytes)
CS --> code
DS,SS --> initialized data
uninitialized data
_memory() --> (high water)
stack
SP -->
*/
if(_showsp() <
(((pgm_seg + (cnv_sec_per_blk*par_per_sec) - _showds()) * 16) - 0x80)) {
printf("Error -- ADJRAM requires 32K of free memory to run\n");
adjram_exit(dose_noram);
} else
_setsp(((pgm_seg + (cnv_sec_per_blk*par_per_sec) - _showds()) * 16) - 0x80);
#if debug
printf("CS = %4x next block = %4x\n",pgm_seg,
pgm_seg + (cnv_sec_per_blk*par_per_sec));
printf("DS = %4x end of dat = %4x\n",_showds(),
_showds() + ((_memory()+15)/16));
printf("mem= %4x\n",_memory());
printf("SP = %4x top of stk = %4x\n",_showsp(),
_showds() + (_showsp()/16)) + 0x8;
#endif
/* ---- Now call the real main routine. Never comes back from call. */
actual_main(gbl_argc, gbl_argv);
}
/* ==== ACTUAL_MAIN ROUTINE ==== */
int actual_main(argc, argv)
int argc;
char *argv[];
{
int err_code; /* exit error code ( 0 if no error ) */
int non_fatal_err; /* user errors not requiring reboot */
int j;
int k;
int m;
/* ---- File Allocation Table ----*/
unsigned char fat[fat_size];
printf("Adjustable RAM Disk v4.1 ");
printf(" (c) Copyright 1986, 1987, 1988 by Gary Cramblitt\n");
#if em_support
printf("(-- Expanded Memory Version)\n");
#endif
printf("\n");
err_code = dose_noerr; /* set no error */
non_fatal_err = dose_noerr;
/* ---- Make sure MS-DOS 2 or above ---- */
_rax = dosf_getver << 8;
_doint(dosi_dosf);
if (_rax & 0x00ff < 2) {
printf ("Error -- this program requires DOS version 2 or above.\n");
adjram_exit(dose_invfunc);
}
/* ---- If user wants to expand into LIM Expanded Memory or reserved
memory, set the memory block size appropriately. If user is
expanding into reserved memory, get the paragraph addresses
and verify.
*/
expansion_type = nor_flg;
sec_per_blk = cnv_sec_per_blk;
#if em_support
for (j = 1; j < argc; j++) if (toupper(*(argv[j]+1)) == 'E')
expansion_type = em_flg;
if (expansion_type == em_flg) sec_per_blk = em_sec_per_blk;
#endif
#if rm_support
for (j = 1; j < argc; j++) if (toupper(*argv[j]) == 'R') {
expansion_type = rm_flg;
user_rm_addr = htoi(argv[j]+2);
j = atoi(argv[j]+7);
if (j > 64 || j < 1 || user_rm_addr < start_rm_addr) {
printf("Error -- Bad reserved memory parameter.\n");
printf(" Must be in form R:hhhh:dd. Check documentation.\n");
adjram_exit(dose_invfunc);
}
sec_per_blk = j * sec_per_K;
/* --- Check that specified reserved memory is really there. --- */
_poke(0xed, 0, user_rm_addr);
_poke(0xed, (sec_per_blk*bytes_per_sec)-1, user_rm_addr);
if ( (_peek(0, user_rm_addr) != 0xed)
|| (_peek((sec_per_blk*bytes_per_sec) - 1, user_rm_addr) != 0xed)) {
printf("Error -- could not locate reserved memory block ");
printf("from %4x:0000 to %4x:%4x\n",
user_rm_addr, user_rm_addr, (sec_per_blk*bytes_per_sec)-1);
printf(" Probably caused by no such memory.\n");
adjram_exit(dose_arena);
}
}
#endif
/*
---- If using Expanded Memory, check to make sure that
EMM is available. Check is made by opening the EMM device.
If open fails, EMM is not loaded. Get page frame segment from
EMM. Raw DOS handle open is used here to avoid bringing in
huge DeSmet IO routines.
*/
#if em_support
if (expansion_type == em_flg) {
_rax = (dosf_openh << 8) + 0; /* open a handle function */
_rds = _showds();
_rdx = &em_device_name; /* DS:DX => "EMMXXXX0" */
_doint(dosi_dosf);
j = _rax; /* j = returned handle */
if (_carryf == 1) {
printf("Error -- Expanded Memory Manager is not available.\n");
printf(" Error code: %d\n",_rax);
adjram_exit(dose_invfunc);
} else {
_rax = (dosf_ioctl << 8) + 7; /* Get output status */
_rbx = j; /* BX = handle */
_doint(dosi_dosf);
_rbx = j; /* BX = handle */
j = _rax & 0xff; /* j = returned device status */
_rax = dosf_closeh << 8; /* close handle */
_doint(dosi_dosf);
if (j == 0xff) {
_rax = emm_get_PFseg << 8; /* get EM page frame segment */
_doint(usri_emm);
_rax = _rax >> 8; /* status in AH */
if (_rax == 0) em_PFseg = _rbx;
else {
printf("Error -- Expanded Memory Manager could not report page frame.\n");
printf(" Error code: %2xH\n",_rax);
adjram_exit(dose_noram);
}
} else {
printf("Error -- Expanded Memory Manager is not available.\n");
printf(" Error code: %2xH\n",j);
adjram_exit(dose_invfunc);
}
}
}
#endif
/*
---- Release our own environment block. It doesn't hurt to do so.
Done so we don't have to do it in the future when user requests
memory disk shrinkage.
*/
_res = peekw(environment_segment, pgm_seg);
_rax = dosf_dealloc << 8;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not free environment block. Error code %d\n",
_rax);
adjram_exit(dose_inv_env);
}
/*
---- Set default minimum size of RAM disk. Will be overwritten as
soon as boot sector is read in. Set here so that help message
will have something to display.
*/
min_size_sec = def_size_K*sec_per_K;
/*
---- Parse the command line drive letter.
If user makes an error, give him usage message and exit.
*/
if (argc < 2 ||
(drive_number = (toupper(*argv[1]) - 65)) < 0 || drive_number > 11) {
dsp_usage();
exit(dose_invfunc);
}
/*
---- Read the boot sector from the memory disk. Verify that we are
dealing with the correct memory disk.
*/
if ((err_code = readsec(drive_number, 0, &boot_sec)) != dose_noerr) {
printf("Error -- could not read boot sector. Error code %1d\n", err_code);
adjram_exit(dose_invdrv);
}
if (strcmp(boot_sec.ident,"AMDISK4 ") != 0) {
printf("Error -- that drive is not the adjustable RAM disk or invalid\n");
printf(" version of AMDISK for this version of ADJRAM!\n");
adjram_exit(dose_invdrv);
}
#if debug
if (fat_size != boot_sec.bpb_fat_size*bytes_per_sec)
printf("Coding Error -- FAT_SIZE does not match BPB_FAT_SIZE\n");
#endif
/* ---- Determine the drive's current size and free space. */
mdisk_size_K = boot_sec.bpb_total/sec_per_K;
_rax = dosf_drvfre << 8;
_rdx = drive_number + 1;
_doint(dosi_dosf);
if (_rax == 0xffff) {
printf("Error -- Invalid drive letter or RAM disk is not loaded.\n");
adjram_exit(dose_invdrv);
}
free_secs = _rbx * _rax; /* avail clusters * sectors per cluster */
printf("Drive %c: Starting Size: %dK Free Space: %dK\n",
drive_number + 65, mdisk_size_K, free_secs/sec_per_K);
/* ---- Obtain the default disk. ---- */
_rax = dosf_getdisk << 8;
_doint(dosi_dosf);
default_drive = _rax & 0x00ff;
/* ---- Get the memory disk's current directory. ---- */
mdisk_pathname.drv = drive_number + 'A';
mdisk_pathname.colon = ':';
mdisk_pathname.slash = '\\';
_rax = dosf_cwd << 8;
_rdx = drive_number + 1;
_rds = _showds();
_rsi = mdisk_pathname.dir;
_doint(dosi_dosf);
/*
---- Read in the FAT.
*/
first_fat_sector = boot_sec.bpb_reserved;
last_fat_sector = first_fat_sector + boot_sec.bpb_fat_size - 1;
k = 0;
for (j = first_fat_sector; j <= last_fat_sector; j++) {
readsec(drive_number, j, fat + k);
k = k + bytes_per_sec;
}
/*
---- Determine what user wants to do -- expand or shrink the memory
disk, and by how much.
*/
user_chg = 0;
if (argc >= 3) {
if (expansion_type == rm_flg) user_chg = sec_per_blk/sec_per_K;
else if (toupper(*argv[2]) == 'F')
user_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
else if (toupper(*argv[2]) == 'M') {
user_chg = atoi(argv[2]+1) - free_secs/sec_per_K;
if (user_chg < 0) user_chg = 0;
} else if (*argv[2] != '/') {
user_chg = atoi(argv[2]);
if (*argv[2] == '+' || *argv[2] == '-'); else
user_chg = user_chg - (boot_sec.bpb_total/sec_per_K);
}
}
user_chg = user_chg * sec_per_K;
/*
---- Display minimum shrinkage size (if shrink is possible at all.
*/
if (user_chg <= 0) {
if (boot_sec.bpb_media > 1)
printf(" Next Smaller Size: %dK Min Shrinkage: %dK\n",
(boot_sec.bpb_total -
boot_sec.mem_blk_table[boot_sec.bpb_media-1].siz)/sec_per_K,
boot_sec.mem_blk_table[boot_sec.bpb_media-1].siz/sec_per_K);
else
printf("RAM disk is as small as is possible.\n");
}
/*
---- Size our program to 32K if user is expanding the memory
disk. This 32K program segment will become the first new
memory block added to the memory disk's Memory Block Table.
*/
if (user_chg >=0) {
if (expansion_type == nor_flg) {
_rax = dosf_setblk << 8;
_res = pgm_seg;
_rbx = sec_per_blk * par_per_sec;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not modify memory block size. Error code %1d\n",
_rax);
printf(" Probably caused by not enough free memory.\n");
adjram_exit(dose_noram);
}
}
}
/*
---- Determine maximum expansion in terms of memory blocks.
For expanded memory, ask EMM how many pages are available.
Size available conventional memory in units of 32K memory
blocks. Always leave at least 32K of memory so that ADJRAM
has room to be run again.
For reserved memory, user specifies exactly one block and he
tells how big that block is.
In any case, maximum expansion is limited to available
clusters.
*/
if (user_chg >= 0) {
#if em_support
if (expansion_type == em_flg) {
_rax = emm_get_pages << 8; /* get number of EM pages */
_doint(usri_emm);
avail_mem_blks = _rbx/em_pag_per_blk;
}
#endif
if (expansion_type == nor_flg) {
_carryf == 0;
for (avail_mem_blks = 0; _carryf == 0; avail_mem_blks++) {
_rax = dosf_alloc << 8;
_rbx = sec_per_blk * par_per_sec;
_doint(dosi_dosf);
boot_sec.mem_blk_table[boot_sec.bpb_media + avail_mem_blks].par = _rax;
}
avail_mem_blks = avail_mem_blks - 1;
for (j = avail_mem_blks; j > 0; j--) {
_rax = dosf_dealloc << 8;
_res = boot_sec.mem_blk_table[boot_sec.bpb_media + j - 1].par;
_doint(dosi_dosf);
}
}
#if rm_support
if (expansion_type == rm_flg) avail_mem_blks = 1;
#endif
j = ((max_clusters * boot_sec.sec_per_cluster) -
boot_sec.bpb_total)/ sec_per_blk;
if (j < avail_mem_blks) avail_mem_blks = j;
printf(" Max Possible Size: %dK Max Expansion: %dK\n",
(avail_mem_blks*K_per_blk) + (boot_sec.bpb_total/sec_per_K),
avail_mem_blks*K_per_blk);
}
/*
---- Round user's request to the nearest memory block boundary.
If expanding, round up, but do not exceed available memory.
If shrinking, round up (less negative), and do not exceed
free sectors.
*/
mdisk_chg = user_chg;
if (mdisk_chg > 0) {
mdisk_chg = ((mdisk_chg + sec_per_blk - 1)/sec_per_blk) * sec_per_blk;
if (mdisk_chg/sec_per_blk > avail_mem_blks) {
mdisk_chg = avail_mem_blks * sec_per_blk;
non_fatal_err = dose_noram;
}
} else if (mdisk_chg < 0) {
if (-mdisk_chg > free_secs) {
printf("Warning -- Insufficient free space in disk to shrink that much.\n");
printf(" You must erase some files.\n");
mdisk_chg = -free_secs;
non_fatal_err = dose_noram;
}
for (j = boot_sec.bpb_media-1;
-mdisk_chg >= boot_sec.mem_blk_table[j].siz && j > 0; j--) {
mdisk_chg = mdisk_chg + boot_sec.mem_blk_table[j].siz;
}
mdisk_chg = 0;
for (j++; j < boot_sec.bpb_media; j++)
mdisk_chg = mdisk_chg - boot_sec.mem_blk_table[j].siz;
}
if (user_chg != mdisk_chg)
printf("Warning -- Rounding your requested change of %dK to %dK.\n",
user_chg/sec_per_K,mdisk_chg/sec_per_K);
mdisk_size_K = (mdisk_chg + boot_sec.bpb_total)/sec_per_K;
printf(" Final Size: %dK Amount Change: %dK\n",
mdisk_size_K, mdisk_chg/sec_per_K);
/*
---- From here on, any error that occurs would probably require the
user to reboot.
*/
/*
---- Expand or shrink as indicated.
*/
if (mdisk_chg < 0 ) err_code = shrink(fat);
else if (mdisk_chg > 0) err_code = expand();
/* ---- Write the new FAT back to disk */
for (m = 0; m < boot_sec.bpb_fats; m++) {
k = 0;
for (j = first_fat_sector; j <= last_fat_sector; j++) {
writesec(drive_number, j, fat + k);
k = k + bytes_per_sec;
}
first_fat_sector = first_fat_sector + boot_sec.bpb_fat_size;
last_fat_sector = last_fat_sector + boot_sec.bpb_fat_size;
}
/* ---- Write the updated boot sector back to memory disk ---- */
if ((err_code = writesec(drive_number, 0, &boot_sec)) != 0) {
printf("Error -- could not write boot sector back. Code: %1d\n", err_code);
err_code = dose_invdrv;
}
/*
---- Restore the memory disk's default directory, then reset default
drive.
*/
_rax = dosf_chdir << 8;
_rds = _showds();
_rdx = &mdisk_pathname;
_doint(dosi_dosf);
_rax = dosf_seldisk << 8;
_rdx = default_drive;
_doint(dosi_dosf);
/* ---- Display Memory Block Table if user requested it. ----- */
for (j = 1; j < argc; j++) if (toupper(*(argv[j]+1)) == 'T')
dsp_mem_blk_table();
/*
---- Tell user results.
*/
if (err_code != dose_noerr) {
printf("*** Sorry, RAM disk left corrupted. Recommend you check\n");
printf(" your files, save them if possible, and reboot.\n");
} else err_code = non_fatal_err;
/*
---- If expanding using conventional memory, terminate normally by
using terminate and stay resident function.
*/
if (expansion_type == nor_flg && mdisk_chg > 0) {
printf("ADJRAM exiting (code %d)\n",err_code);
_rax = (dosf_keepprc << 8) + dose_noerr;
_rdx = sec_per_blk * par_per_sec;
_doint(dosi_dosf); /* PROGRAM TERMINATES HERE */
}
adjram_exit(err_code);
} /* End of main routine */
/* ==== EXPAND ROUTINE ==== */
/* Expands the memory disk by allocating new memory blocks to it. */
int expand()
{
int result, j;
int mdisk_dif;
/*
*/
mdisk_dif = mdisk_chg;
result = dose_noerr;
/*
The first new memory block is ADJRAM's own program segment, which
has already been resized to 32K.
Increment the media byte to show an additional memory block has been
allocated. Set the starting paragraph and size (in sectors) for the
new block in the memory block table.
*/
if (expansion_type == nor_flg) {
mdisk_dif = mdisk_dif - sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = pgm_seg;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
/* Increase the total number of sectors. */
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
}
/* Now allocate new memory blocks for the remaining desired space. */
for (;mdisk_dif > 0; mdisk_dif = mdisk_dif - sec_per_blk) {
#if em_support
if (expansion_type == em_flg) {
_rax = emm_get_handle << 8;
_rbx = em_pag_per_blk;
_doint(usri_emm);
_rax = _rax >> 8;
if (_rax == 0) {
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = em_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = em_PFseg;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = _rdx;
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
} else {
printf("Error -- could not allocate new memory block from EMM.");
printf(" Error code %2x\n",_rax);
printf("Probably caused by not enough free memory.\n");
result = dose_arena;
break;
}
}
#endif
if (expansion_type == nor_flg) {
_rax = dosf_alloc << 8;
_rbx = sec_per_blk * par_per_sec;
_doint(dosi_dosf);
if (_carryf == 1) {
printf("Error -- could not allocate new memory block. Error code %1d\n",
_rax);
printf(" Probably caused by not enough free memory.\n");
result = dose_arena;
break;
} else {
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = nor_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = _rax;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
}
}
#if rm_support
/* --- Reserved memory must be initialized by zeroing it. --- */
if (expansion_type == rm_flg) {
for (j=0; j < sec_per_blk*bytes_per_sec; j++)
_poke(0x00, j, user_rm_addr);
boot_sec.mem_blk_table[boot_sec.bpb_media].typ = rm_flg;
boot_sec.mem_blk_table[boot_sec.bpb_media].par = user_rm_addr;
boot_sec.mem_blk_table[boot_sec.bpb_media].siz = sec_per_blk;
boot_sec.mem_blk_table[boot_sec.bpb_media].hdl = 0;
boot_sec.bpb_total = boot_sec.bpb_total + sec_per_blk;
boot_sec.bpb_media = boot_sec.bpb_media + 1;
}
#endif
}
return(result);
} /* end of expand routine */
/*
==== SHRINK ROUTINE ====
Shrinks the memory disk by moving all file sectors down
to lowest possible locations, then deallocating memory
blocks.
*/
int shrink(fat)
unsigned char fat[];
{
/* ==== Data Storage ==== */
/* ---- Root Directory ---- */
struct dir_entry root_dir[dir_per_sec];
/* ---- Other locals ---- */
int cur_dir; /* ptr to current directory */
int dir_sector; /* current directory sector */
int mdisk_dif; /* desired change in sectors */
int done_dir; /* true when root directory completed */
char tmpstr[20];
int j, result;
mdisk_dif = -mdisk_chg; /* make variable positive */
result = dose_noerr;
/* ---- Calculate the location of the directory sectors. */
first_dir_sector = boot_sec.bpb_reserved +
(boot_sec.bpb_fat_size * boot_sec.bpb_fats);
last_dir_sector = (boot_sec.bpb_root * sizeof(struct dir_entry) /
bytes_per_sec) + first_dir_sector - 1;
/* ---- First data sector follows the last root directory sector. */
first_data_sector = last_dir_sector + 1;
/*
---- Locate the first available cluster in the FAT. First FAT entry
is number 2, not 0, because the first two FAT entries are used
for the FAT ID and filler.
*/
for (free_cluster = 2; fatget(free_cluster, fat) != available;
free_cluster++);
/*
---- Loop through the root directory, packing the clusters of
each file to the lowest possible locations.
*/
done_dir = false;
for (dir_sector = first_dir_sector;
!done_dir && dir_sector <= last_dir_sector;
dir_sector++)
{
readsec(drive_number, dir_sector, &root_dir);
for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++)
if (root_dir[cur_dir].u_name.status == never_used) {
done_dir = true;
break;
} else
if ((result = pack_file(&(root_dir[cur_dir]), 0, fat)) != dose_noerr)
break;
writesec(drive_number, dir_sector, &root_dir);
if (result != dose_noerr) break;
}
/*
---- Free up memory. Loop backwards through the memory block table,
freeing allocated memory blocks. As we do so, decrement the
media byte (memory block counter) and total sectors.
*/
j = boot_sec.bpb_media - 1;
if (result == dose_noerr)
while (mdisk_dif >= boot_sec.mem_blk_table[j].siz) {
if (boot_sec.mem_blk_table[j].typ == em_flg) {
_rax = emm_fre_handle << 8;
_rdx = boot_sec.mem_blk_table[j].hdl;
_doint(usri_emm);
_rax = _rax >> 8;
if (_rax != 0) {
printf ("Error -- could not free EM pages for memory block #%d.",j);
printf (" Error code %2x\n",_rax);
result = dose_arena;
}
} else if (boot_sec.mem_blk_table[j].typ == nor_flg) {
_res = boot_sec.mem_blk_table[j].par;
_rax = dosf_dealloc << 8;
_doint(dosi_dosf);
if (_carryf == 1) {
printf ("Error -- could not free allocated memory block #%d.",j);
printf (" Error code %d\n",_rax);
result = dose_arena;
}
}
boot_sec.bpb_media = boot_sec.bpb_media - 1;
boot_sec.bpb_total = boot_sec.bpb_total -
boot_sec.mem_blk_table[j].siz;
mdisk_dif = mdisk_dif - boot_sec.mem_blk_table[j].siz;
j = j - 1;
}
return(result);
} /* end of shrink routine */
/*
==== PACK_FILE ROUTINE ====
Given the directory entry for a file, packs all the clusters
for that file to the lowest possible locations. If the file
is itself a subdirectory, then this routine will be called
recursively from itself.
Clusters are packed down by moving any cluster above
"free_cluster" down to "free_cluster". Then the next free
cluster is located and the next cluster is examined.
*/
int pack_file(dir, parent_cluster, fat)
struct dir_entry *dir;
int parent_cluster;
unsigned char fat[];
{
struct dir_entry a_sector[dir_per_sec];
int next_cluster;
int cur_cluster;
int new_cluster;
int result;
int cur_dir;
int result;
int cluster_start;
int done_dir;
int j;
/* Is this directory entry erased or never used? If so, do nothing. */
result = dose_noerr;
#if debug
printf("Considering file %s for packing.\n",dir->u_name.name);
#endif
if (dir->u_name.status != never_used &&
dir->u_name.status != erased) {
/*
If file is not 0 length or it is a subdirectory file,
we can pack it.
*/
if (dir->size > 0 || isdir(dir)) {
#if debug
printf(" Packing file %s\n",dir->u_name.name);
#endif
/*
Handle the first cluster number, which is stored in the
directory entry, not in the FAT.
*/
next_cluster = dir->first_cluster;
if (next_cluster > free_cluster) {
new_cluster = free_cluster;
fatput(fatget(next_cluster, fat), new_cluster, fat);
dir->first_cluster = new_cluster;
fatput(available, next_cluster, fat);
for (; fatget(free_cluster, fat) != available; free_cluster++);
result = move_cluster(drive_number, next_cluster, new_cluster,
&a_sector);
}
/* Handle rest of clusters stored in the FAT */
if (result == dose_noerr) for (cur_cluster = dir->first_cluster;
!islast(next_cluster = fatget(cur_cluster, fat));
cur_cluster = fatget(cur_cluster, fat)) {
if (next_cluster > free_cluster) {
new_cluster = free_cluster;
fatput(fatget(next_cluster, fat), new_cluster, fat);
fatput(new_cluster, cur_cluster, fat);
fatput(available, next_cluster, fat);
for (; fatget(free_cluster, fat) != available; free_cluster++);
result = move_cluster(drive_number, next_cluster, new_cluster,
&a_sector);
if (result != dose_noerr) break;
}
}
}
/*
If the directory entry is for a subdirectory, then process
it, packing each of its files. Process a cluster at a time...
*/
done_dir = false;
if (isdir(dir)) for (cur_cluster = dir->first_cluster;
!done_dir && !islast(cur_cluster);
cur_cluster = fatget(cur_cluster, fat)) {
if (result != dose_noerr) break;
cluster_start = (cur_cluster-2) * boot_sec.sec_per_cluster +
first_data_sector;
/* For each sector in the cluster... */
for (j = 0; !done_dir && j < boot_sec.sec_per_cluster; j++) {
result = readsec(drive_number, cluster_start + j, &a_sector);
if (result != dose_noerr) break;
/*
For each directory entry in the sector, pack it. The first
two entries are special files ".." and "."
*/
for (cur_dir = 0; cur_dir < dir_per_sec; cur_dir++) {
if (a_sector[cur_dir].u_name.name[0] == '.') {
if (a_sector[cur_dir].u_name.name[1] == '.')
a_sector[cur_dir].first_cluster = parent_cluster;
else a_sector[cur_dir].first_cluster = dir->first_cluster;
}
else if (a_sector[cur_dir].u_name.status == never_used) {
done_dir = true;
break;
} else {
/* In DeSmet C, local variables are allocated on the stack. */
/* Make sure there is sufficient stack space for the recursive */
/* call. If not, display message, return error code, and */
/* calling routines will attempt a graceful exit */
if ((_showsp() - _memory()) > 700)
result = pack_file(&(a_sector[cur_dir]), dir->first_cluster, fat);
else {
printf("Error -- Insufficient stack space to pack ");
printf("subdirectory %s\n", a_sector[cur_dir].u_name.name);
printf("Too many subdirectories. Shrink abandoned. ");
printf("Reboot recommended.\n");
result = dose_too_many_dir;
/* Note: Even though we can't handle the subdirectory, */
/* keep going anyway so as to maintain integrity of disk. */
}
}
}
if (result != dose_noerr) break;
writesec(drive_number, cluster_start + j, &a_sector);
}
}
}
if (result != dose_noerr)
printf("Error -- Error while packing file %s\n",
dir->u_name.name);
#if debug
printf("Done packing file %s\n",dir->u_name.name);
#endif
return(result);
}
/*
==== MOVE_CLUSTER ROUTINE ====
Moves all sectors of specified cluster.
*/
int move_cluster (drive, from, to, buffer)
int drive, from, to;
unsigned char *buffer;
{
int result;
int tmp_result;
int j;
from = (from-2) * boot_sec.sec_per_cluster + first_data_sector;
to = (to-2) * boot_sec.sec_per_cluster + first_data_sector;
for (j = 0; j < boot_sec.sec_per_cluster; j++) {
tmp_result = readsec(drive, from + j, buffer);
result = writesec(drive, to + j, buffer);
}
return(result + tmp_result);
}
/*
==== ISDIR ROUTINE ====
Given a directory entry, returns true if the entry is for a
subdirectory file.
*/
int isdir(dir)
struct dir_entry *dir;
{
if (dir->attr & dir_bit) return(true); else return(false);
}
/*
==== ISLAST ROUTINE ====
Given a FAT entry, returns true if it is an EOF marker.
*/
int islast(cluster)
int cluster;
{
int j;
int result;
result = false;
for (j = last_low; j <= last_high; j++)
if (cluster == j) result = true;
return(result);
}
/*
==== FATGET ROUTINE ====
Given cluster number and FAT, returns the 12-bit FAT entry in
and integer word, right-adjusted.
*/
int fatget (cluster, fat)
int cluster;
unsigned char fat[];
{
int clloc, clword;
clloc = 3*cluster/2;
clword = fat[clloc] + (fat[clloc+1] << 8);
if (cluster & 1) return (clword >> 4); else return (clword & 0x0fff);
}
/*
==== FATPUT ROUTINE ====
Given a 12-bit FAT entry, cluster number, and the FAT, stores
the entry into the FAT.
*/
int fatput (wd12bits, cluster, fat)
int cluster;
int wd12bits;
unsigned char fat[];
{
int clloc, clword;
clloc = 3*cluster/2;
clword = fat[clloc] + (fat[clloc+1] << 8);
if (cluster & 1)
clword = (clword & 0x000f) | (wd12bits << 4);
else
clword = (clword & 0xf000) | wd12bits;
fat[clloc] = clword & 0x00ff;
fat[clloc+1] = clword >> 8;
}
/*
==== DSP_MEM_BLK_TABLE ROUTINE ====
Displays the Memory Block Table on the screen.
*/
dsp_mem_blk_table()
{
int j;
printf("\n");
#if em_support
printf(" Memory Block Table\n");
printf("Block # Paragraph Size(K) EM Handle\n");
printf("------- --------- -------- ---------\n");
for (j = 0; j < boot_sec.bpb_media; ++j) {
printf(" %1d %4x %3d ",
j,boot_sec.mem_blk_table[j].par,
boot_sec.mem_blk_table[j].siz/sec_per_K);
if (boot_sec.mem_blk_table[j].typ == em_flg)
printf("%4x",boot_sec.mem_blk_table[j].hdl);
else printf(" --");
printf("\n");
}
#else
printf(" Memory Block Table\n");
printf("Block # Paragraph Size(K)\n");
printf("------- --------- --------\n");
for (j = 0; j < boot_sec.bpb_media; ++j) {
printf(" %1d %4x %3d\n",
j,boot_sec.mem_blk_table[j].par,
boot_sec.mem_blk_table[j].siz/sec_per_K);
}
#endif
printf("\n");
}
#if rm_support
/*
==== HTOI ROUTINE ====
Convert hexadecimal string to integer.
*/
int htoi(str)
char str[];
{
int temp;
int j;
int nibble;
temp = 0;
for (j=0; ishex(str[j]); j++) {
nibble = toupper(str[j]) - '0';
if (nibble > 9) nibble = nibble - ('A'-'9'-1);
if (nibble < 0 || nibble > 15) break;
temp = (temp << 4) + nibble;
}
return(temp);
}
/*
==== ISHEX ROUTINE ====
Returns true if character is hexadecimal (upper or lower case).
*/
int ishex(ch)
char ch;
{
if (isdigit(ch) || (toupper(ch) >='A' && toupper(ch) <= 'F'))
return(true);
else return(false);
}
#endif
/*
==== PEEKW ROUTINE ====
Given segment and offset, returns the word thereby addressed.
Written because DeSmet C uses small memory model and there are
no FAR data types.
*/
int peekw(off, seg)
unsigned char *off;
int seg;
{
return((_peek(off+1, seg) << 8) + _peek(off, seg));
}
/*
==== READSEC ====
Given drive number, desired logical sector number, and a sector
buffer, this routine reads the sector into the buffer.
NOTE: This routine uses DeSmet's "_doint" routine to perform absolute
disk read interrupt number 25H. This interrupt, unlike most
interrupts, leaves the flags register on the stack when it returns.
In DeSmet this is OK, because the called routine restores the
stack frame prior to RETurning. In some C compilers, this scheme
won't work. For example, in AZTEC C, it is the CALLING routine's
responsibility to restore the stack frame. In AZTEC, then, we
would RETurn to the wrong address and die. So look out, if you are
converting this program to some other version of C.
*/
int readsec (drive, sector, buffer)
int drive;
int sector;
char *buffer;
{
_rax = drive;
_rds = _showds(); /* ASSUMPTION: DS = SS in DeSmet C */
_rbx = buffer;
_rcx = 1;
_rdx = sector;
_doint(dosi_dsk_read);
if (_carryf == 1) return (_rax); else return (dose_noerr);
}
/*
==== WRITESEC ROUTINE ====
Given drive number, logical sector number, and a buffer, this
routine writes the sector to disk.
See the note under routine READSEC.
*/
int writesec (drive, sector, buffer)
int drive;
int sector;
char *buffer;
{
_rax = drive;
_rds = _showds(); /* ASSUMPTION: DS = SS in DeSmet C */
_rbx = buffer;
_rcx = 1;
_rdx = sector;
_doint(dosi_dsk_write);
if (_carryf == 1) return (_rax); else return (dose_noerr);
}
/*
==== ADJRAM_EXIT ROUTINE ====
Displays exit code and exits with that code to DOS.
*/
int adjram_exit(err_code)
int err_code;
{
printf("ADJRAM exiting (code %d)\n",err_code);
exit(err_code);
}
/*
==== DSP_USAGE ROUTINE ====
Displays message how to use program.
*/
int dsp_usage()
{
printf("WARNING: This program is distributed with documentation and ");
printf("source code.\n");
printf(" Using it without first reading the documentation ");
printf("is hazardous.\n");
printf("Usage: ADJRAM drive size option\nWhere:\n");
printf(" drive = RAM disk drive letter, A to L\n");
printf(" size = size of RAM disk in K (minimum %d) in following format:\n",
min_size_K);
printf(" xxx = set RAM disk size to xxx\n");
printf(" +xxx = increase RAM disk size by xxx\n");
printf(" -xxx = decrease RAM disk size by xxx\n");
printf(" Fxxx = adjust size so there is xxx free space\n");
printf(" Mxxx = ensure there is at least xxx of free space\n");
printf(" Size will be rounded to the next highest %dK\n",
K_per_blk);
printf(" If size is omitted, current drive size is displayed.\n");
printf(" option= optional /T to display Memory Block Table\n");
#if em_support
printf(" optional /E to expand using LIM Expanded Memory\n");
#endif
printf("Examples:\n");
printf(" ADJRAM C: 250\n");
printf(" will set RAM disk C to 256K\n");
#if em_support
printf(" ADJRAM i: +250 /e /t\n");
printf(" will increase RAM disk I by 256K using Expanded Memory\n");
#else
printf(" ADJRAM i: +60 /t\n");
printf(" will increase RAM disk I by 64K\n");
#endif
printf(" and will display the Memory Block Table\n");
printf(" ADJRAM C: m64\n");
printf(" will ensure there is at least 64K free space on drive C\n");
}