💾 Archived View for uscoffings.net › retro-computing › components › PC › interrupts › INT.C captured on 2023-01-29 at 06:53:20.
⬅️ Previous capture (2022-06-04)
-=-=-=-=-=-=-
/* * 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); }