cramer@kontron.UUCP (Clayton Cramer) (01/20/87)
Several weeks ago I asked for help relating to building an interrupt service routine so that I can build a C program that takes the standard PC-DOS printer driver. A number of people provided very helpful advise. Consequently, I am posting the following code to provide a skeleton so that can build your own TSR programs that take over the printer ISR. It's not copyrighted, but when you get filthy rich writing the next 1-2-3, I sure hope you'll remember where you got your start. :-) The following files are included: make Makes the various components of the TSR and the testing filter. loadisr.c Loads the ISR and gets it started. isr.asm The actual ISR that dispatches control to the C function. filter.c A C function that is called by isr.asm. testfilt.c A program that simulates the functions of isr.asm so that you can debug filter.c without the extraneous junk involved with the interrupt service routine. Use Microsoft C V4.0 and MASM V4.0. It's not beautiful, and it's not perfect, but it does work. Also, can anyone tell me: 1. how to make a TSR program go away? 2. how to figure out the size of a .EXE file at link time? --------------make file---------------------------------------- .c.exe: cl -DLINTARGS /Zi /Od $*.c .asm.obj: masm $*.asm,$*.obj,$*.lst; # The filter written in C. filter.obj: filter.c cl /Gs /AL -DLINTARGS -c /Zi /Od $*.c # The interrupt service routine that invokes the filter written in C. isr.obj: isr.asm # The C program that loads the ISR and the filter. loadisr.obj: loadisr.c cl /AL -DLINTARGS -c /Zi /Od $*.c # The link step that links the ISR, the ISR loader, and the filter. filter.exe: loadisr.obj isr.obj filter.obj cl /AL /Zi /Od filter.obj loadisr.obj isr.obj # The C program that tests the filter. testfilt.obj: testfilt.c cl /AL -DLINTARGS -c /Zi /Od $*.c testfilt.exe: testfilt.obj filter.obj cl /AL /Zi /Od testfilt.obj filter.obj --------------------loadisr.c---------------------------------------- #include <stdio.h> #include <dos.h> #define _ProgramSize_ 25000 extern char far *DRIVERPTR; extern void far SETINTNBR (); extern void far SAVE_DS_SS (); int IntNbrToTakeOver = 0x61; main (Argc, Argv) int Argc; char *Argv[]; { int ExitCode = 0; union REGS Regs; struct SREGS SegRegs; SAVE_DS_SS (); switch (Argc) { case 1: break; case 2: if (1 != sscanf (Argv[1], "%d", &IntNbrToTakeOver)) { fprintf (stderr, "invalid interrupt number: %s\n", Argv[1]); ExitCode = 3; } break; default: fprintf (stderr, "too many arguments -- only an alternate interrupt number is valid\n"); ExitCode = 2; break; } if (0 == ExitCode) { Regs.h.ah = 0x35; /* Get ptr to any existing interrupt vector */ Regs.h.al = (char) IntNbrToTakeOver; int86x (0x21, &Regs, &Regs, &SegRegs); if ((0 == SegRegs.es) && (0 == Regs.x.bx)) { /* Means that the interrupt we intend to cannibalize isn't in use. */ fprintf (stderr, "installing print translator\n"); Regs.h.al = (char) 0x17; int86x (0x21, &Regs, &Regs, &SegRegs); /* Get ptr to existing printer driver */ SegRegs.ds = SegRegs.es; /* es:bx = addr of existing printer driver */ Regs.x.dx = Regs.x.bx; Regs.h.ah = 0x25; /* set ptr to interrupt driver */ Regs.h.al = (char) IntNbrToTakeOver; /* interrupt we are taking over */ int86x (0x21, &Regs, &Regs, &SegRegs); /* point printer driver to ouriver */ Regs.h.al = (char) 0x17; /* the original printer driver */ SegRegs.ds = FP_SEG ((char far*) DRIVERPTR); /* get addr of driver we are installing */ Regs.x.dx = FP_OFF ((char far*) DRIVERPTR); int86x (0x21, &Regs, &Regs, &SegRegs); SETINTNBR (IntNbrToTakeOver); printf ("test string for printer\n"); Regs.x.dx = _ProgramSize_; /* size of the program we want to remain resident */ int86x (0x27, &Regs, &Regs, &SegRegs); /* terminate and stay resident */ } } } --------------------isr.asm---------------------------------------- page 50,132 extrn _FILTER:far CODE_SEG SEGMENT ASSUME CS:CODE_SEG public _DRIVERPTR,_SETINTNBR, _SAVE_DS_SS _DRIVERPTR dd DRIVER filter_ds dw 0 filter_ss dw 0 isr_ds dw 0 isr_ss dw 0 Printer dw 0 Count dw 0 DRIVER proc far sti ; Save the ISR DS and SS registers so that I can retrieve later after the ; C function we call for filtering is finished. push bx mov bx,ds mov cs:isr_ds,bx mov bx,ss mov cs:isr_ss,bx pop bx push bx push cx push dx push di push si push es cmp ah,0 je PRT_CHAR ; This must be some other printer function -- let's just perform the function call PRT_INT jmp SAVE_RESULT ; save the output PRT_CHAR: mov cs:Printer,dx cli mov bx,filter_ds mov ds,bx mov bx,filter_ss mov ss,bx mov bx,offset Count ; Push ptr to Count on stack push cs push bx push ax ; Push character to print call _FILTER add sp,6 ; throw away arguments mov bx,isr_ds mov ds,bx mov bx,isr_ss mov ss,bx sti mov cx,cs:Count ; get count of characters to print cmp cx,0 ; any characters to print? je NoCharsToPrint ; no, return to caller mov es,dx mov bx,ax ; es:bx = ptr to buffer mov dx,cs:Printer ; get the Printer number NextChar: mov ah,0 mov al,es:[bx] ; get the character to send call PRT_INT ; print the character inc bx ; increment the char ptr dec cx ; decrement the char count ja NextChar ; get the next character NoCharsToPrint: mov ah,010h ; shows that we wrote it SAVE_RESULT: pop es pop si pop di pop dx pop cx pop bx iret DRIVER endp PRT_INT proc near NEW_PRT_INT: int 17h ; default prt interrupt nbr ret PRT_INT endp _SAVE_DS_SS proc far ; Stores the DS and SS of the C program into SAVE_DS and SAVE_SS so that ; can correctly invoke the C function from the ISR. push ax mov ax,ds mov cs:filter_ds,ax mov ax,ss mov cs:filter_ss,ax pop ax ret _SAVE_DS_SS endp _SETINTNBR proc far push bp mov bp,sp mov ax,[bp+6] mov cs:byte ptr NEW_PRT_INT+1,al mov sp,bp pop bp ret _SETINTNBR endp CODE_SEG ENDS END -------------------filter.c---------------------------------------- #include <stdio.h> char Hex[] = "0123456789ABCDEF"; char Buffer[1024]; char* FILTER(Ch, Count) int Ch, *Count; { /* Ch is the character to be translated, and *Count is the number of output characters put into Buffer. */ Buffer[*Count = 0] = Hex[(Ch >> 4) & 0xf]; Buffer[++(*Count)] = Hex[Ch & 0xf]; ++(*Count); return (Buffer); } --------------------testfilt.c---------------------------------------- #include <stdio.h> #include <fcntl.h> #include <io.h> extern char *FILTER (); main (Argc, Argv) int Argc; char *Argv[]; { int Ch, Count, I; char *String; setmode (fileno (stdout), O_BINARY); freopen (Argv[1], "rb", stdin); while (EOF != (Ch = getchar ())) { String = FILTER (Ch, &Count); if (Count > 0) for (I = 0; I < Count; I++) putchar (String[I]); } }