Building a Filter

Creating a new filter for MS-DOS is a straightforward process. In its simplest form, a filter need only use the handle-oriented read (Interrupt 21H Function 3FH) and write (Interrupt 21H Function 40H) functions to get characters or lines from standard input and send them to standard output, performing any desired alterations on the text stream on a character-by-character or line-by-line basis.

Figures 15-1 and 15-2 contain prototype character-oriented filters in both assembly language and C. In these examples, the translate routine, which is called for each character transferred from the standard input to the standard output, does nothing at all. As a result, both filters function rather like a very slow COPY command. You can quickly turn these primitive filters into useful programs by substituting your own translate routine.

If you try out these programs, you'll notice that the C prototype filter runs much faster than its MASM equivalent. This is because the C runtime library is performing hidden blocking and deblocking of the input and output stream, whereas the MASM filter is doing exactly what it appears to be doing: making two calls to MS-DOS for each character processed. You can easily restore the MASM filter's expected speed advantage by adapting it to read and write lines instead of single characters.

name proto

page 55,132

title PROTO.ASM--prototype filter

;

; PROTO.ASM: prototype character-oriented filter

;

; Copyright 1988 Ray Duncan

;

stdin equ 0 ; standard input handle

stdout equ 1 ; standard output handle

stderr equ 2 ; standard error handle

cr equ 0dh ; ASCII carriage return

lf equ 0ah ; ASCII linefeed

_TEXT segment word public 'CODE'

assume cs:_TEXT,ds:_DATA,ss:STACK

main proc far ; entry point from MS-DOS

mov ax,_DATA ; set DS = our data segment

mov ds,ax

main1: ; read char from stdin...

mov dx,offset char ; DS:DX = buffer address

mov cx,1 ; CX = length to read

mov bx,stdin ; BX = standard input handle

mov ah,3fh ; function 3fh = read

int 21h ; transfer to MS-DOS

jc main3 ; if error, terminate

cmp ax,1 ; any character read?

jne main2 ; if end of file, terminate

call translate ; translate character

; write char to stdout...

mov dx,offset char ; DS:DX = buffer address

mov cx,1 ; CX = length to write

mov bx,stdout ; BX = standard output handle

mov ah,40h ; function 40h = write

int 21h ; transfer to MS-DOS

jc main3 ; if error, terminate

cmp ax,1 ; was character written?

jne main3 ; if disk full, terminate

jmp main1 ; get another character

main2: ; end of file reached

mov ax,4c00h ; function 4ch = terminate

; return code = 0

int 21h ; transfer to MS-DOS

main3: ; error or disk full

mov ax,4c01h ; function 4ch = terminate

; return code = 1

int 21h ; transfer to MS-DOS

main endp

;

; Perform any necessary translation on character

; from standard input stored in variable 'char'.

; This example simply leaves character unchanged.

;

translate proc near

ret ; does nothing

translate endp

_TEXT ends

_DATA segment word public 'DATA'

char db 0 ; storage for input character

_DATA ends

STACK segment para stack 'STACK'

dw 64 dup (?)

STACK ends

end main ; defines program entry point

Figure 15-1. PROTO.ASM, the source code for a prototype character-oriented MASM filter.

/*

PROTO.C: prototype character-oriented filter

Copyright 1988 Ray Duncan

*/

#include <stdio.h>

main(int argc, char *argv[])

{

char ch;

while((ch=getchar()) != EOF) /* read a character */

{

ch = translate(ch); /* translate it if necessary */

putchar(ch); /* write the character */

}

exit(0); /* terminate at end of file */

}

/*

Perform any necessary translation on character

from input file. This example simply returns

the same character.

*/

int translate(char ch)

{

return (ch);

}

Figure 15-2. PROTO.C, the source code for a prototype character-oriented C filter.