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.