home *** CD-ROM | disk | FTP | other *** search
- _UNTANGLING SMARTDRIVE_
- by Geoff Chappell
-
- [LISTING ONE]
-
- /*ioctl.c-functions to support IOCTL to & from devices under DOS all functions*
- * return 0 if successful, having stored an integer at an address provided as *
- * the last argument failure is indicated by returning a non-0 DOS error code */
- unsigned _cdecl _dos_gethandlestatus (int handle, unsigned *status)
- {
- _asm {
- mov ax,4400h
- mov bx,handle
- int 21h
- jc done
- mov bx,status
- mov [bx],ax
- xor ax,ax
- done:
- }
- }
- unsigned _cdecl _dos_ioctlread (int handle, void _far *buffer,
- unsigned count, unsigned *numread)
- {
- _asm {
- push ds
- mov ax,4402h
- mov bx,handle
- mov cx,count
- lds dx,buffer
- int 21h
- pop ds
- jc done
- mov bx,numread
- mov [bx],ax
- xor ax,ax
- done:
- }
- }
- unsigned _cdecl _dos_ioctlwrite (int handle, void _far *buffer,
- unsigned count, unsigned *numwrt)
- {
- _asm {
- push ds
- mov ax,4403h
- mov bx,handle
- mov cx,count
- lds dx,buffer
- int 21h
- pop ds
- jc done
- mov bx,numwrt
- mov [bx],ax
- xor ax,ax
- done:
- }
- }
-
-
-
- [LISTING TWO]
-
- /*** ioctl.h-function prototypes for IOCTL to & from devices under DOS ***/
- unsigned _dos_gethandlestatus (int, unsigned *);
- unsigned _dos_ioctlread (int, void _far *, unsigned, unsigned *);
- unsigned _dos_ioctlwrite (int, void _far *, unsigned, unsigned *);
-
-
-
- [LISTING THREE]
-
- /* smartdrv.h - structures and definitions relating to smartdrv.sys */
- #pragma pack (1)
-
- /* The data packet returned by performing an IOCTL Read from SMARTDrive */
- struct SD_READ {
- char unused_1;
- char unused_2;
- char IsActive;
- char MemoryType;
- unsigned Ticks;
- char IsLocked;
- char FlushOnReboot;
- char unused_3;
- char DoubleBuffer;
- void _far *OrgInt13;
- char MinorVersion;
- char MajorVersion;
- char unused_4;
- char unused_5;
- unsigned SectorsRead;
- unsigned SectorsHit;
- unsigned SectorsBuffered;
- char HitRatio;
- char BufferRatio;
- unsigned TracksInCache;
- unsigned CurrentTracks;
- unsigned LockedTracks;
- unsigned DirtyTracks;
- unsigned CurrentSize;
- unsigned ConfiguredSize;
- unsigned MinimumSize;
- char _far *GlobalLockFlag;
- };
- struct SD_WRITE {
- char command;
- union {
- char subcommand;
- int size;
- char _far *address;
- };
- };
- #pragma pack ()
-
-
-
- [LISTING FOUR]
-
- /* smartchk.c-main source file for program smartchk.exe. Compile under small*
- * or tiny memory models in Microsoft C 6.00 and link with ioctl.obj */
- #include <bios.h>
- #include <dos.h>
- #include <fcntl.h>
- #include <process.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "ioctl.h"
- #include "smartdrv.h"
- #define AND &&
- #define NOT !
- #define OR ||
-
- /**** Function prototypes ***/
- void get_configuration (void);
- void show_configuration (void);
- char ** get_range (int, char **);
- unsigned is_str_zero (char *);
- void format_command (char *, char **);
- void run_test (char *);
- int yes_or_no (void);
- int convert_to_seconds (long, long *, int *);
- void resize_cache (unsigned, unsigned);
- void reset_cache (void);
- void increment_cache (void);
-
- void set_traps (void);
- void quit (char *);
- void cleanup (void);
- void _far CtrlC_trap (void);
- int _far critfail (void);
-
- /*** Data ***/
- /* The string describing the program's syntax */
- const char syntax [] = "\
- \nGathers information about the SMARTDrive disk cache.\
- \n\
- \nSMARTCHK [/min [/max [/inc]]] command [arguments]\
- \n\
- \n times the execution of the designated command,\
- \n using different sizes for the SMARTDrive cache\
- \n\
- \n min, max and inc should be multiples of 16KB\
- \n\
- \nType SMARTCHK without parameters to display information\
- \nabout SMARTDrive's configuration and performance.";
- /* Structures for SMARTDrive IOCTL */
- struct SD_READ sd_read;
- struct SD_WRITE sd_write;
- /* Various pieces of data which must be shared between functions */
- char terminating = 0;
- char sd_cache_changed = 0;
- unsigned sd_cache_size;
- int sd_handle = -1;
- unsigned min;
- unsigned max;
- unsigned inc;
-
- /*** Code ***/
- int main (register int argc, register char *argv [])
- {
- char buffer [128];
- /* Install clean up routines and exception handlers to ensure termination. */
- set_traps ();
- /* Verify that SMARTDrive has been installed and perform an IOCTL Read to
- obtain configuration data. Error in this operation is fatal to program. */
- get_configuration ();
- /* If no argument has been supplied on the command line, then describe the
- configuration & report SMARTDrive's statistical estimates of its performance.
- If the only argument on the command line is "/?", then display a help
- message. The most complicated option involves executing and timing a command
- repeatedly, using different sizes for the SMARTDrive cache. Up to 3 command
- line arguments may specify range of cache size to use in test-all arguments
- after these are presumed to be part of the command and are formatted
- into a buffer for passing to the system () function. */
- argv ++;
- argc --;
- if (NOT argc) show_configuration ();
- else if (argc == 1 AND **argv == '/' AND *(*argv + 1) == '?'
- AND *(*argv + 2) == '\0') printf (syntax);
- else {
- argv = get_range (argc, argv);
- format_command (buffer, argv);
- run_test (buffer);
- }
- exit (0);
- }
- void get_configuration (void)
- {
- register unsigned exitcode = 0;
- unsigned status;
- unsigned count;
- /* If smartdrv.sys has been loaded, it may be found in memory as a device
- driver with name SMARTAAR. Use _dos_open (), which is simply a front-end to
- the int 21h function 3Dh. */
- exitcode = _dos_open ("SMARTAAR", O_RDWR, &sd_handle);
- /* On success, call int 21h function 4400h (not supported in MS-C library)
- to verify that the handle corresponds to a character device driver capable
- of IOCTL. DOS returns a 16-bit flag whose low byte it takes from the System
- File Table and high byte from the device driver attribute word. Status bit
- for a device is masked by 0x0080 and for IOCTL support by 0x4000 - both
- bits must be set. */
- if (NOT exitcode) {
- exitcode = _dos_gethandlestatus (sd_handle, &status);
- if (NOT exitcode) {
- if ((status & 0x4080) != 0x4080) exitcode = 0xFFFF;
- }
- }
- /* Any error encountered so far can be explained by a common message. Note
- that all errors occurring in this function are fatal to the program. */
- if (exitcode) quit ("cannot open SMARTDrive device");
- /* Perform IOCTL Read to get configuration info about SMARTDrive. Not only
- should read be successful but should return as many bytes as requested. */
- exitcode = _dos_ioctlread (sd_handle, &sd_read, sizeof (sd_read), &count);
- if (exitcode OR count != sizeof (sd_read))
- quit ("cannot read data from SMARTDrive device");
- }
- void show_configuration (void)
- {
- printf ("\nSMARTDrive Version %u.%02u has been configured to use %uKB",
- sd_read.MajorVersion, sd_read.MinorVersion,
- sd_read.ConfiguredSize << 4);
- printf ("\nof %s memory with %uKB set as the minimum size.",
- (sd_read.MemoryType == 1 ? "extended" : "expanded"),
- sd_read.MinimumSize << 4);
- printf ("\n\nIts present capacity is %uKB, corresponding to %u tracks.",
- sd_read.CurrentSize << 4, sd_read.TracksInCache);
- printf ("\nOf these, %u tracks are in use.", sd_read.CurrentTracks);
- printf ("\n\nDuring this session, %u%% of sector reads have been filled",
- sd_read.HitRatio);
- printf ("\nfrom the cache and %u%% from the intermediate buffer.",
- sd_read.BufferRatio);
- }
- char ** get_range (int argc, register char *argv [])
- {
- register unsigned temp;
- /* Getting numerical arguments for the testing range is a little tedious,
- but unavoidable if program is to be anything other than a pointless toy.
- This function returns a value for the argv variable, advanced past any
- arguments that specify range. Begin by setting default values for range.
- Change these only after establishing validity of command line value. */
- min = sd_read.MinimumSize;
- max = sd_read.ConfiguredSize;
- inc = 2;
- if (**argv == '/') {
- temp = atoi (*argv + 1) >> 4;
- if (temp == 0 OR temp < min OR temp >= max) {
- if (NOT is_str_zero (*argv + 1)) quit ("invalid minimum size");
- }
- min = temp;
- argv ++;
- argc --;
- if (argc AND **argv == '/') {
- temp = atoi (*argv + 1) >> 4;
- if (temp <= min OR temp > max) quit ("invalid maximum size");
- max = temp;
- argv ++;
- argc --;
- if (argc AND **argv == '/') {
- temp = atoi (*argv + 1);
- if (NOT temp OR temp & 15 OR (temp >> 4 > max - min))
- quit ("invalid increment");
- inc = temp >> 4;
- argv ++;
- argc --;
- if (min == 0 AND inc == 1)
- quit ("parameters rejected to avoid SMARTDrive bug!");
- }
- }
- }
- /* Name of a command to execute is mandatory. If no arguments remain,
- processing can't continue. Otherwise, return adjusted value for argv. */
- if (NOT argc) quit ("program name not supplied");
- return (argv);
- }
- /* The following simple function returns a TRUE value iff the string at
- address ptr is composed entirely of the character '0'. */
- unsigned is_str_zero (register char *ptr)
- {
- unsigned ch;
- while ((ch = *ptr ++) AND (ch == '0')) {
- }
- return (ch ? 0 : 1);
- }
- /* Given an array of pointers to character strings, the following function
- concatenates all strings, separating them with spaces and copying them to
- specified buffer. Its role is to piece together a command string for system ()
- function. Note that it could be developed to strip double-quotes. */
- void format_command (register char *buffer, char *argv [])
- {
- register char *ptr;
- while (ptr = *argv ++) {
- while (*buffer ++ = *ptr ++) {
- }
- *(buffer - 1) = ' ';
- }
- *buffer = '\0';
- }
- void run_test (char *command_string)
- {
- long start, end;
- long seconds;
- unsigned hundredths;
- /* Shrink the cache to the range's minimum. */
- resize_cache (min, sd_read.CurrentSize);
- sd_cache_size = min;
- sd_cache_changed = -1;
- for (;;) {
- /* Before executing the command, flush the disk cache and discard its
- contents. Each test is therefore started under the same conditions (at
- least with respect to SMARTDrive, but note that test is not entirely
- fair, because data from disk is also held in DOS BUFFERS system). */
- reset_cache ();
- /* Use BIOS functions to get the system clock count, in spite of slight
- problem with the "midnight" flag. Calling MS-C library functions to obtain
- the time and convert the difference to seconds brings in floating point
- arithmetic & a great increase in code size if using an emulator library. */
- _bios_timeofday (_TIME_GETCLOCK, &start);
- system (command_string);
- _bios_timeofday (_TIME_GETCLOCK, &end);
- convert_to_seconds (end - start, &seconds, &hundredths);
- /* Report the cache size and execution time of the test program,
- then give the user a chance to leave the testing cycle. */
- printf ("\n\nExecution time with %uKB disk cache was %lu.%02u seconds",
- sd_cache_size << 4, seconds, hundredths);
- printf ("\nContinue (y/n)? ");
- if (NOT yes_or_no ()) break;
- printf ("\n");
- /* Increase the cache size for the next round of testing. */
- if (sd_cache_size + inc > max) break;
- increment_cache ();
- sd_cache_size += inc;
- }
- }
- /* The following is a simple function which waits at stdin, returning
- a true value on receipt of 'y' or a false value for 'n'. */
- int yes_or_no (void)
- {
- unsigned ch;
- for (;;) {
- ch = getch ();
- if (NOT ch) getch ();
- else if (ch == 'N' OR ch == 'n') return (0);
- else if (ch == 'Y' OR ch == 'y') return (1);
- }
- }
- convert_to_seconds (long clocks, long *seconds, int *hundredths)
- {
- /* Clock ticks at approximately 18.2/second, most easily rounded to 91 / 5. */
- *seconds = (clocks * 5) / 91;
- *hundredths = (((clocks * 5) % 91) * 100) / 91;
- }
- /* === Cache manipulation ==== */
- /* Next three functions perform IOCTL Write operations to change SMARTDrive
- cache. In this particular program, error-reporting simplified by regarding all
- errors as fatal. */
- void resize_cache (unsigned new, unsigned old)
- {
- unsigned exitcode, count;
- sd_write.command = 0x0B;
- sd_write.size = old - new;
- if (sd_write.size == 0) return;
- if (sd_write.size < 0) {
- sd_write.size = - sd_write.size;
- sd_write.command = 0x0C;
- }
- exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 3, &count);
- if (exitcode OR count != 3) quit ("cannot resize SMARTDrive cache");
- }
- void reset_cache (void)
- {
- unsigned exitcode, count;
- sd_write.command = 0x01;
- exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 1, &count);
- if (exitcode OR count != 1) quit ("cannot flush SMARTDrive cache");
- }
- void increment_cache (void)
- {
- unsigned exitcode, count;
- sd_write.command = 0x0C;
- sd_write.size = inc;
- exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 3, &count);
- if (exitcode OR count != 3) quit ("cannot increase SMARTDrive cache");
- }
- /* === Code relating to termination - error messages and cleanup === */
- void set_traps (void)
- {
- /* Direct C run-time to call a cleanup routine before it exits program. */
- atexit (cleanup);
- /* Trap Ctrl-C to ensure proper cleanup rather than let DOS terminate
- program pre-emptively. Critical errors (which might also cause premature
- termination) may be failed automatically. An advantage to using _harderr ()
- is that it allows compilation under tiny memory model. Were error handler
- installed directly, it would have to be declared using the _interrupt
- keyword, which is (perhaps surprisingly) incompatible with tiny memory
- model. Even so, coercion to far addresses will generate unwanted segment
- references in the tiny memory model without some additional manipulation.*/
- #define FAR_FUNCTION (void (_far *)())
- #define FAR_INTERRUPT (void (_interrupt _far *)())
- #define CODE_SEG (void _based (_segname ("_CODE")) *)
- _dos_setvect (0x23, FAR_INTERRUPT CODE_SEG CtrlC_trap);
- _harderr (FAR_FUNCTION CODE_SEG critfail);
- }
- void cleanup (void)
- {
- terminating = 1;
- printf ("\n");
- if (sd_handle != -1) {
- if (sd_cache_changed) {
- sd_cache_changed = 0;
- reset_cache ();
- resize_cache (sd_read.CurrentSize, sd_cache_size);
- }
- _dos_close (sd_handle);
- sd_handle = -1;
- }
- }
- void _far CtrlC_trap (void)
- {
- if (NOT terminating) {
- terminating ++;
- quit ("terminated by user");
- }
- }
- int _far critfail (void)
- {
- return (_HARDERR_FAIL);
- }
- void quit (char *errmsg)
- {
- printf ("\nUnable to continue - \n%s", errmsg);
- exit (0xFF);
- }
-
-
-
-