home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
POINT Software Programming
/
PPROG1.ISO
/
misc
/
inter41
/
int.c
< prev
next >
Wrap
Text File
|
1994-04-11
|
20KB
|
724 lines
/*
* File: int.c
*
* Synopsis: PC interrupt caller, memory R/W, I/O port R/W
*
* System: MSDOS - Turbo C or Borland C (other compilers need some work)
*
* A utility for PC systems programmers to:
* - perform and visualise the results of interrupt calls
* - use buffers (inc files) to set register pairs
* - view the interrupt vector table
* - read & write I/O ports
*
* NB: This utility must be used with EXTREME CARE. Using bad interrupt and/or
* I/O calls can destroy data on your memory/disk(s) and might crash your
* machine.
*
* Compatible with int.c ditributed with Ralf Brown's interrupt lists
* Comments/suggestions welcome on the email address below.
*
*
* Copyright (c) 1992-1994 Angelo Haritsis <ah@doc.ic.ac.uk>
*
* Redistribution and use in source and binary forms are permitted provided
* that the above copyright notice and this paragraph are duplicated in all
* such forms and that any documentation, advertising materials, and other
* materials related to such distribution and use acknowledge that the
* software was developed by Angelo Haritsis.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* TODO:
* > -fbuf filename (write mem with vals from a file)
*/
#ifndef lint
static char rcsid[] = "$Header: E:/SRC/MISC\RCS\int.c 1.2 1994/04/11 20:11:36 ah Exp ah $";
#endif
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <dos.h>
#define PROG "int"
#define VERSION "1.0"
#define NOTREACHED 0
#define PARSE_JUSTARGS 0x01
#define PARSE_NOACTION 0x02
/*
* Types
*/
typedef int (*fun_ptr) (char **, int *);
typedef struct s_opt {
char *opts; /* option name */
int size; /* bytes for the ptr element (1, 2, 4);
* 0 means execute the function following
* parsing (argv, &opt_now)
*/
void *ptr; /* pointer to the data element to be loaded or a function if size = 0 */
} OPTION;
typedef struct s_dbuff {
unsigned *ptr_to_seg; /* the area where the segment is held */
unsigned *ptr_to_off; /* the area where the offset is held */
void far *addr; /* the address (when explicitly given) */
char logo[20];
#define DBUFF_HEX 0x01
#define DBUFF_ASC 0x02
#define DBUFF_BIN 0x04
int mode; /* display mode */
long bytes; /* # of bytes to show */
} DBUFFER;
typedef struct s_doserr {
int errno;
int class;
int action;
int locus;
} DOS_ERROR;
/*
* Globals
*/
char switchchar[] = "-+/";
char usage[] =
"usage: " PROG " [-h] [-q] [int=# | int# | -int #] [[-]reg[=]val] ...\n"
" [-buf sreg:reg=\"str\"]\n"
" [-d[a|b|hx] sreg:reg[:#bytes]] [-d[a|hx] faraddr[:#bytes]]\n"
" [-divt int#-int#]]\n"
" [-in[w] port#] [-out[w] port#,val]\n"
" str: printf fmt (%#s allowed to setup buffers)\n";
char help[] =
" eg.\n"
" o Interrupts (+ buffer setup)\n"
" " PROG " 0x10 -ax 0x12 same as: int int=0x10 ax=0x12 (set VGA graphics mode)\n"
" " PROG " 0x10 ah=0x10 al=1 bh=9 (set VGA border color; use to adjust display)\n"
" " PROG " 0x21 -ah 9 -buf ds:dx=\"hello world\\n$\" -q (INT 21,9 'hello world')\n"
" " PROG " an_int -buf es:bx=\"%512s\" (creates a scratch buffer of 512 bytes)\n"
" " PROG " 0x33 ax=9 bx=0 cx=0 -fbuf es:bx=cursor.dat (mouse cursor from file)\n"
" o Memory (-d: display: called after interrupt)\n"
" " PROG " -q -dhx 0x400049:18 (BIOS video area data)\n"
" " PROG " -q es=0x40 -bx 0x49 -dhx es:bx:18 (same as above)\n"
" " PROG " -q -db 0xF8000000:512 >file (binary dump of AMIBIOS serial data)\n"
" o IVT\n"
" " PROG " -q -divt (display vectors from interrupt vector table = IVT)\n"
" " PROG " -q -divt 0x10-0x1F (display IVT slots 0x10 to 0x1F)\n"
" o I/O Ports\n"
" " PROG " -out 0x70,0 -in 0x71 (read seconds from CMOS)\n"
" " PROG " -out 0x70,0 -out 0x71,0 (zero-out seconds in CMOS)\n";
char *int_error[] = {
NULL,
"Parse",
};
char str_flags[] = "0N..ODITSZ0A0P1C"; /* 80x86 flag register */
union REGS reg;
struct SREGS sreg;
unsigned int reg_bp, reg_sp, flags;
int int_call; /* the interrupt to be called */
int dos_errno;
int quiet_mode = 0; /* quiet means just execute int - no reg display * /* does not apply on display
* opts */
int buff_now = 0;
char *buff[10]; /* array of read buffer pointers */
int dbuff_now = 0;
DBUFFER dbuff[10]; /* the buffers to show at end of intr */
/* --- Prototypes --- */
OPTION *parse_an_opt(char *argv[], int *opt_now, int mode);
void doit(void);
void reg_display(void);
int set_int_val(char **argv, int *opt_now);
int ivt_display(char **argv, int *opt_now);
int ioport_out(char **argv, int *opt_now);
int ioport_in(char **argv, int *opt_now);
int read_escape(char **p);
int set_buff(char **argv, int *opt_now);
int set_fbuff(char **argv, int *opt_now);
int set_dbuff(char **argv, int *opt_now);
int dbuff_parse(void **ptr, char *tok);
int set_dbuff(char **argv, int *opt_now);
int show_help(char **argv, int *opt_now);
int set_quiet(char **argv, int *opt_now);
void error_exit(int err, char *s);
/* --- */
/*
* Structure with all the `action' to be done for an option
* NB: Care with matching prefixes (longer must come first)
*/
OPTION Opt[] = {
/* NB: put the longer strings first ! */
{"$DEFAULT", 0, (void *) set_int_val},
{"DIVT", 0, (void *) ivt_display}, /* display int vector table */
{"INT", 2, (void *) &int_call},
{"OUTW", 0, (void *) ioport_out}, /* I/O port write, read */
{"OUT", 0, (void *) ioport_out},
{"INW", 0, (void *) ioport_in},
{"IN", 0, (void *) ioport_in},
{"DBUF", 0, (void *) set_dbuff},
{"FBUF", 0, (void *) set_fbuff},
{"BUF", 0, (void *) set_buff},
{"DHX", 0, (void *) set_dbuff}, /* display mem contents (hex) */
{"FL", 2, (void *) &(reg.x.flags)}, /* set flags (won't affect anything) */
{"AX", 2, (void *) &(reg.x.ax)}, /* set general registers */
{"BX", 2, (void *) &(reg.x.bx)},
{"CX", 2, (void *) &(reg.x.cx)},
{"DX", 2, (void *) &(reg.x.dx)},
{"AH", 1, (void *) &(reg.h.ah)},
{"BH", 1, (void *) &(reg.h.bh)},
{"CH", 1, (void *) &(reg.h.ch)},
{"DH", 1, (void *) &(reg.h.dh)},
{"AL", 1, (void *) &(reg.h.al)},
{"BL", 1, (void *) &(reg.h.bl)},
{"CL", 1, (void *) &(reg.h.cl)},
{"DL", 1, (void *) &(reg.h.dl)},
{"SI", 2, (void *) &(reg.x.si)}, /* set index, stack registers */
{"DI", 2, (void *) &(reg.x.di)},
{"BP", 0, 0},
{"SP", 0, 0},
{"CS", 2, (void *) &(sreg.cs)}, /* set segment registers */
{"DS", 2, (void *) &(sreg.ds)},
{"ES", 2, (void *) &(sreg.es)},
{"SS", 2, (void *) &(sreg.ss)},
{"DA", 0, (void *) set_dbuff}, /* display mem contents (ascii) */
{"D", 0, (void *) set_dbuff}, /* display mem contents (ascii+hex) */
{"Q", 0, (void *) set_quiet}, /* quiet (no disply of reg contents) */
{"H", 0, (void *) show_help}, /* help */
};
int cdecl
main(int argc, char *argv[])
{
int opt_now;
if (argc == 1)
error_exit(0, NULL);
/* parse the arguments and do proper action */
for (opt_now = 1; opt_now < argc; opt_now++)
if (parse_an_opt(argv, &opt_now, 0) == NULL)
error_exit(1, NULL);
doit();
return (0);
}
/*
* Parses an argument and calls proper function or assigns numbers
* accordingly. Reentrant.
*/
OPTION *
parse_an_opt(char *argv[], int *opt_now, int mode)
{
int i, arg_len, get_next_arg;
char *opts, *optarg, *p;
long val;
opts = argv[*opt_now];
if (strchr(switchchar, opts[0])) /* option starts with a switch char, skip it */
opts++, argv[*opt_now]++;
for (i = 0; i < (sizeof(Opt) / sizeof(OPTION)); i++) {
arg_len = strlen(Opt[i].opts);
get_next_arg = opts[arg_len] == 0;
if (strncmpi(opts, Opt[i].opts, arg_len) == 0) {
if (mode & PARSE_NOACTION) /* do not perform action */
return (&Opt[i]); /* just return ptr to Opt slot */
switch (Opt[i].size) {
case 0: /* call the function */
if (!(mode & PARSE_JUSTARGS) && Opt[i].ptr != (void *) 0)
if ((*((fun_ptr) Opt[i].ptr)) (argv, opt_now) == 0)
return (NULL); /* error */
return (&Opt[i]);
case 1: /* ptr is a byte, short, int */
case 2:
case 4:
if (get_next_arg)
optarg = argv[++(*opt_now)]; /* get next option */
else
optarg = opts + arg_len + 1; /* skip a separator (eg =) */
val = strtol(optarg, &p, 0);
if (p == optarg)
return (NULL);
switch (Opt[i].size) {
case 1:
*((char *) Opt[i].ptr) = (char) val;
break;
case 2:
*((short *) Opt[i].ptr) = (short) val;
break;
case 4:
*((long *) Opt[i].ptr) = (long) val;
break;
}
return (&Opt[i]);
default:
assert(NOTREACHED);
}
}
}
if (mode & PARSE_JUSTARGS)
return (&Opt[0]); /* default */
else {
i = (*((fun_ptr) Opt[0].ptr)) (argv, opt_now); /* default */
return (i == 0 ? NULL : &Opt[0]);
}
}
/*
* Call the interrupt if asked and display the result buffers
*/
void
doit(void)
{
int i;
long j;
unsigned char far *ptr;
unsigned char b;
dos_errno = 0;
if (int_call != -1) {
reg_bp = _BP;
reg_sp = _SP;
flags = _FLAGS;
reg_display();
}
if (int_call > 0 && int_call < 256) {
quiet_mode || printf("\nINT: 0x%02X\n", int_call);
int86x(int_call, ®, ®, &sreg); /**/
if (reg.x.cflag != 0) /* error occured */
dos_errno = _doserrno;
reg_bp = _BP;
reg_sp = _SP;
quiet_mode || printf("\n");
flags = reg.x.flags;
reg_display();
if (!quiet_mode && (int_call == 0x21 || int_call == 0x24)) /* dos call */
printf("DOSERR: %04X (%u)\n", dos_errno, dos_errno);
}
/* display dbuffers */
for (i = 0; i < dbuff_now; i++) {
ptr = (unsigned char far *) MK_FP(*(dbuff[i].ptr_to_seg), *(dbuff[i].ptr_to_off));
if (dbuff[i].mode & DBUFF_BIN) /* binary */
setmode(1, O_BINARY);
else
printf("\n*<%s> {\n", dbuff[i].logo);
for (j = 0; j < dbuff[i].bytes; j++, ptr++) {
b = *ptr & 0x00FF; /* byte to display */
if (dbuff[i].mode & DBUFF_BIN) { /* binary */
putchar(b);
continue; /* nothing else */
}
if (dbuff[i].mode & DBUFF_HEX)
printf("%02X", b);
if (dbuff[i].mode == DBUFF_ASC)
putchar(iscntrl(b) ? '.' : b);
else if (dbuff[i].mode & DBUFF_ASC)
printf("(%c)", iscntrl(b) ? '.' : b);
if (dbuff[i].mode != DBUFF_ASC)
printf(" ");
}
fflush(stdout);
if (dbuff[i].mode & DBUFF_BIN) /* binary */
setmode(1, O_TEXT);
else
printf("}\n");
}
/* free the read buffers allocated */
for (i = 0; i < buff_now; i++)
free(buff[i]);
}
void
reg_display(void)
{
char s[32];
int i, bit_on;
if (quiet_mode)
return;
printf(
"AX=%04X BX=%04X CX=%04X DX=%04X\n"
"SI=%04X DI=%04X BP=%04X SP=%04X\n"
"CS=%04X DS=%04X ES=%04X SS=%04X",
reg.x.ax, reg.x.bx, reg.x.cx, reg.x.dx,
reg.x.si, reg.x.di, reg_bp, reg_sp,
sreg.cs, sreg.ds, sreg.es, sreg.ss);
strncpy(s, str_flags, 32); /* use a scratch copy */
/* and now the flags */
flags = reg.x.flags;
for (i = 0; i < 16; i++) {
bit_on = (flags & ((unsigned) 0x8000 >> i)) != 0;
if (s[i] == '.')
s[i] = bit_on ? '1' : '0';
else
s[i] = bit_on ? s[i] : tolower(s[i]);
}
printf(" CPU Flags: %16s\n", s);
}
/*
* 'default' argument function - see if it is an interrupt
*/
int
set_int_val(char **argv, int *opt_now)
{
long val;
char *p;
val = strtol(argv[*opt_now], &p, 0);
if (val <= 0 || val > 255 || p - argv[*opt_now] != strlen(argv[*opt_now]))
return (0); /* problems */
int_call = (int) val;
return (1);
}
/*
* Display a slot of the Interrupt Vector Table
*/
int
ivt_display(char **argv, int *opt_now)
{
char sfrom[20], sto[20];
int from, to, i;
void far *p;
if ((i = sscanf(argv[*opt_now + 1], "%[0-9xX]-%s", sfrom, sto)) == 2) { /* is a range given ? */
(*opt_now)++; /* consume next arg */
from = (int) strtol(sfrom, (char **) &sfrom, 0);
to = (int) strtol(sto, (char **) &sto, 0);
} else {
from = 0;
to = 255;
}
/* do it now */
printf("Interrupt Vector Table (0x%02X to 0x%02X)\n", from, to);
for (i = from; i <= to; i++) {
disable(); /* just in case ... */
p = (void far *) *((long far *) (4L * i));
enable();
printf(" * 0x%02X (%3u): %Fp\n", i, i, p);
}
printf("\n");
return (1);
}
int
ioport_out(char **argv, int *opt_now)
{
char *optarg, sport[10], sval[10];
int word_op, port, val;
optarg = argv[*opt_now];
word_op = (toupper(optarg[3]) == 'W') ? 1 : 0;
if (isdigit(optarg[3 + word_op])) /* arg follows with no delimiter */
optarg += 3 + word_op;
else
optarg = argv[++(*opt_now)];
if (sscanf(optarg, "%[^ ,;]%*[ ,;]%s", sport, sval) != 2)
return (0);
port = (int) strtol(sport, (char **) &sport, 0);
val = (int) strtol(sval, (char **) &sval, 0);
if (word_op)
outport(port, (unsigned) val);
else
outportb(port, val);
int_call = -1;
return (1);
}
int
ioport_in(char **argv, int *opt_now)
{
char *optarg, sport[10];
int word_op, port, val;
optarg = argv[*opt_now];
word_op = (toupper(optarg[2]) == 'W') ? 1 : 0;
if (isdigit(optarg[2 + word_op])) /* arg follows with no delimiter */
optarg += 2 + word_op;
else
optarg = argv[++(*opt_now)];
if (sscanf(optarg, "%s", sport) != 1)
return (0);
port = (int) strtol(sport, (char **) &sport, 0);
if (word_op) {
val = inport(port);
quiet_mode || printf("INW 0x%04X (%5u): 0x%04X (%5u)\n", port, port, val, val);
} else {
val = inportb(port);
quiet_mode || printf("IN 0x%04X (%5u): 0x%02X (%3u)\n", port, port, val, val);
}
quiet_mode || printf("\n");
int_call = -1;
return (1);
}
#define ESCAPES 10
static int esc_to_code[ESCAPES][2] = {
{'n', '\n'},
{'t', '\t'},
{'v', '\v'},
{'b', 'b'},
{'r', '\r'},
{'f', '\f'},
{'a', '\a'},
{'\\', '\\'},
{'\?', '?'},
{'\'', '\''},
};
/*
* returns with *p pointing to char after the one(s) consumed
*/
int
read_escape(char **p)
{
int i;
if (isdigit(**p)) /* octal */
return ((int) strtol(*p, p, 8));
else if (**p == 'x') /* hex */
return ((int) strtol(*p + 1, p, 16));
for (i = 0; i < ESCAPES; i++)
if (**p == esc_to_code[i][0]) {
(*p)++; /* consume it */
return (esc_to_code[i][1]);
}
/* otherwise, return the character un-translated */
(*p)++; /* consume it */
return (**p);
}
/*
* load seg register values to point ot the created buffer
*/
void
load_regpair(char *s_seg, char *s_off, void far *buff)
{
int len;
char stmp[50], *argv_fake[3];
/* load the regs */
argv_fake[0] = stmp;
argv_fake[1] = "";
len = 0;
sprintf(stmp, "-%s 0x%X", s_seg, FP_SEG(buff));
parse_an_opt(argv_fake, (int *) &len, PARSE_JUSTARGS); /* make it think it's an option */
sprintf(stmp, "-%s 0x%X", s_off, FP_OFF(buff));
parse_an_opt(argv_fake, (int *) &len, PARSE_JUSTARGS); /* and again for offs register */
}
/*
* set registers accordingly
*/
int
set_buff(char **argv, int *opt_now)
{
char *optarg, *p, *dp;
char s_seg[10], s_off[10];
char stmp[50];
static char dummy[] = ""; /* for case of %s in fmt str */
unsigned int len;
optarg = argv[++(*opt_now)]; /* s_off pair */
sscanf(optarg, "%[^:]:%s", &s_seg, &s_off);
if (s_off[2] == '=')
s_off[2] = 0;
optarg = argv[++(*opt_now)]; /* printf like string */
/* how big a buffer ? */
len = strlen(optarg);
/* add the %# lengths (extra buffer space) */
for (p = strchr(optarg, '%'); p != NULL; p = strchr(p + 1, '%'))
len += atoi(p + 1);
if ((buff[buff_now] = (char *) malloc(len)) == NULL)
return (0);
/* create escape chars again (since cmd processing makes \ into \\) */
p = optarg, dp = stmp;
while (*p)
if (*p == '\\') {
p++; /* consume \ */
*dp++ = read_escape(&p);
} else
*dp++ = *p++;
/* load the buffer; 5 % fields are enough (XXX ..f func problem if \0 appears before end of fmt) */
sprintf(buff[buff_now], stmp, dummy, dummy, dummy, dummy, dummy);
load_regpair(s_seg, s_off, (void far *) buff[buff_now]);
buff_now++;
return (1);
}
/*
* set register pair to point to buffer with data from a file
*/
int
set_fbuff(char **argv, int *opt_now)
{
char *optarg, *fname;
char s_seg[10], s_off[80];
long len;
FILE *f;
optarg = argv[++(*opt_now)]; /* s_off pair */
sscanf(optarg, "%[^:]:%s", &s_seg, &s_off);
if (s_off[2] == '=') {
s_off[2] = 0;
fname = &s_off[3];
} else
return (0);
if ((f = fopen(fname, "rb")) == NULL)
return (0);
len = filelength(fileno(f));
if (len > 65500L || (buff[buff_now] = (char *) malloc((unsigned int)len)) == NULL)
return (0);
if (fread(buff[buff_now], (int) len, 1, f) != 1)
return (0);
load_regpair(s_seg, s_off, (void far *) buff[buff_now]);
buff_now++;
fclose(f);
return (1);
}
int
dbuff_parse(void **ptr, char *tok)
{
char stmp[50], *argv_fake[3];
OPTION *p_opt;
int len = 0;
argv_fake[0] = stmp;
argv_fake[1] = "";
sprintf(stmp, "-%s 0", tok);
p_opt = parse_an_opt(argv_fake, &len, PARSE_JUSTARGS | PARSE_NOACTION);
if (p_opt == NULL || p_opt == &Opt[0])
return (0);
*ptr = (unsigned *) p_opt->ptr;
return (1);
}
/*
* add to the list of the buffers to be displayed at end
*/
int
set_dbuff(char **argv, int *opt_now)
{
char mode_char;
char *optarg, *p;
char tok[3][15]; /* max 3 tokens of 15 chars each */
int i;
long addr;
unsigned long num = 1L; /* number of bytes to display */
DBUFFER *dpb;
dpb = &dbuff[dbuff_now];
dpb->mode = DBUFF_HEX | DBUFF_ASC;
mode_char = toupper(argv[*opt_now][1]);
dpb->mode &= (mode_char == 'A') ? ~DBUFF_HEX : ~0;
dpb->mode &= (mode_char == 'H') ? ~DBUFF_ASC : ~0;
if (mode_char == 'B') { /* binary mode */
dpb->mode &= ~(DBUFF_HEX | DBUFF_ASC);
dpb->mode |= DBUFF_BIN;
}
optarg = argv[++(*opt_now)]; /* reg pair */
strncpy(dpb->logo, optarg, 20);
/* collect tokens */
for (i = 0, p = strtok(optarg, ":="); p; p = strtok(NULL, ":="))
strcpy(tok[i++], p);
if (i > 3)
return (0);
/* process them */
addr = strtoul(tok[0], &p, 0);
if ((p - tok[0]) > 0) { /* first is addr */
if (i > 1) { /* there's a 2nd token */
num = strtoul(tok[1], &p, 0);
if ((p - tok[1]) == 0 || num == 0)
return (0); /* wrong argument */
}
dpb->addr = (void far *) addr;
dpb->ptr_to_off = (unsigned *) &(dpb->addr);
dpb->ptr_to_seg = ((unsigned *) &(dpb->addr)) + 1;
} else { /* should be Reg:Reg[:#] format */
if (dbuff_parse((void **) &(dpb->ptr_to_seg), tok[0]) == 0)
return (0);
if (dbuff_parse((void **) &(dpb->ptr_to_off), tok[1]) == 0)
return (0);
if (i > 2) { /* num argument */
num = strtoul(tok[2], &p, 0);
if ((p - tok[2]) == 0 || num == 0)
return (0); /* wrong argument */
}
}
dpb->bytes = num;
dbuff_now++; /* have inserted an element */
return (1);
}
int
set_quiet(char **argv, int *opt_now)
{
argv = argv, opt_now = opt_now; /* eliminate warning */
return (quiet_mode = 1);
}
#define fmsg stdout /* DOS stderr cannot be redir'ed >:-{ */
int
show_help(char **argv, int *opt_now)
{
argv = argv, opt_now = opt_now; /* eliminate warning */
fprintf(fmsg,
PROG ": Execute and investigate interrupts/system data (ver " VERSION ")\n"
"Copyright (c) 1992-1994 A. Haritsis <ah@doc.ic.ac.uk>. Distribute freely.\n");
error_exit(0, help);
return (1);
}
void
error_exit(int err, char *s)
{
if (err > 0)
fprintf(fmsg, PROG ": %s error\n", int_error[err]);
fprintf(fmsg, "%s", usage);
if (s != NULL)
fprintf(fmsg, "%s", s);
exit(err);
}