Authors
Publication
Pub Details
Date
Pages
Filter programs are common in the UNIX world, but they are found to a varying extent on other platforms (such as QDOS) as well. In QDOS filters were introduced in 1984 with the QL-Toolkit (which later turned into Toolkit II). A filter program is typically a program that reads a stream of characters and makes some simple operation on it before passing it on. This function is not too different from what a filter attached to a water pipe will accomplish, thus the name filter. Filters may be connected in chains to perform more complex operations. Examples of what filters can do are converting the character stream to uppercase, encrypting it with a supplied key, etc. From QDOS a filter is started like this:
EX UPCASE,ram1_infile,ram2_outfile
The EX command is an enhanced version of EXEC that can pass arguments to the started programs. In this case the files ‘ram1_infile’ and ‘ram2_outfile’ are opened and the channel ID’s are passed to the program (placed on the (A7) stack).
Our example filter will strip the input stream from ANSI ESC-codes. ESC-codes are produced by software generating output to a terminal. The name ESC-code is due to all codes starting with the ESC char, chr$(27). After that comes a varying number of argument chars. ESC-codes will, when recieved by the terminal, alter some of it’s settings. One code will for example switch to underline mode( chr$(27); ‘[4m’; ), while another will turn this mode off. This program will only remove some of the ESC-codes.
*
* A filter to strip _log files from ESC sequences
* Current version only strips codes generated by the UNIX
* 'nn' program Release 6.5.0 #8 (NOV),
* Kim F. Storm, 1991
* This program is (C) 1995 Per-Erik Forssen
* Should assemble with Qmac, other assemblers will need
* other directives.
*
* QDOS standard equates
*
myself equ -1
forever equ -1
err.nc equ -1
err.ef equ -10
mt.frjob equ $05
io.fstrg equ $03
io.sbyte equ $05
ut.err0 equ $ca
*
* equates special for this program
*
bufsz equ 8000
ch_lf equ $0A
ch_cr equ $0D
ch_esc equ $1B
ch_brace equ $5B
*
* Macro to step through the read buffer
*
NEXTCHAR MACRO
[.lab] move.b (a2)+,d1 ; get next char
subq.w #1,d2 ; decrement count
ENDM
section program
data 8192 ; 8K data space
*
bra.s ESCstrip
dc.b '1.00' ; program version
dc.w $4afb ; standard job
dc.w 8 ; program name
dc.b 'ESCstrip'
ds.w 0 ; word align
ESCstrip lea.l 0(a6,a4.l),a6 ; set buffer pointer
move.w (sp)+,d7 ; pop no of chan id's
subq.w #2,d7 ; should be be two
bne.s ESC_exit
move.l (sp)+,a0 ; input chan
move.l (sp)+,a4 ; output chan
*
ESC_fstrg moveq #io.fstrg,d0 ; fetch a string
move.w #bufsz,d2 ; maximum size
moveq #10,d3 ; wait up to a fifth of
a second
move.l a6,a1 ; place it in buffer
trap #3
move.l d0,d4 ; keep error code
move.w d1,d2 ; and string length
beq.s ESC_errtst ; null string?
*
move.l a6,a2 ; reset buffer pointer
ESC_loop NEXTCHAR
cmp.b #ch_cr,d1 ; ignore CR
beq.s ESC_loop
cmp.b #ch_esc,d1 ; at an ESC-code ?
bne.s ESC_send
NEXTCHAR
cmp.b #ch_brace,d1
bne.s ESC_psend
NEXTCHAR
bsr.s SKIP_NUM
cmp.b #'m',d1
beq.s ESC_lfsend
cmp.b #'H',d1
beq.s ESC_loop
cmp.b #'J',d1
beq.s ESC_loop
cmp.b #'K',d1
beq.s ESC_loop
cmp.b #';',d1
bne.s ESC_psend
bsr.s SKIP1_NUM
*
ESC_lfsend moveq #ch_lf,d1 ; send a line feed instead
bsr.s BPUT
bra.s ESC_next
ESC_psend move.b d1,d5 ; esc code unresolved;
moveq #ch_esc,d1 ;let it pass through filter
bsr.s BPUT
move.b d5,d1
ESC_send bsr.s BPUT
*
ESC_next tst.w d2
bgt.s ESC_loop
*
ESC_errtst move.l d4,d0 ; test the return from read
beq.s ESC_fstrg
addq.l #-err.nc,d4 ;not enough time to finish?
beq.s ESC_fstrg
cmp.l #err.ef-err.nc,d4 ; end of file?
bne.s ESC_exit
moveq #0,d0 ; yes; exit without errors
*
ESC_exit move.w ut.err0,a2 ; signal error in
SuperBASIC #0
jsr (a2)
move.l d0,d3 ; return status to owner
moveq #mt.frjob,d0 ; force remove
moveq #myself,d1 ; myself
trap #1
*
* Subroutines
* SKIP_NUM skips 0 or more digits in string
* SKIP1_NUM skips 1 or more
*
SKIP_NUM
SN_loop cmp.b #'0',d1
blt.s SN_end
cmp.b #'9',d1
bgt.s SN_end
SKIP1_NUM NEXTCHAR
bra.s SN_loop
SN_end rts ; number is now skipped
*
* BPUT sends char in d1.b to output
*
BPUT movem.l a1/d1,-(a7)
exg a4,a0 ; use output chan
moveq #forever,d3 ; timeout for write
moveq #io.sbyte,d0
trap #3
exg a4,a0 ; use input chan
movem.l (a7)+,a1/d1
rts
end
The program includes many of the programming features used in assembler. For example the equ statements:
myself equ -1
This corresponds to the C #define statements, i.e. all occurrences of ‘myself’ will be replaced with ‘-1’ at assembly. Then we have macros:
NEXTCHAR MACRO
[.lab] move.b (a2)+,d1 ; get next char
subq.w #1,d2 ; decrement count
ENDM
This corresponds to the C++ inline functions (or perhaps some more advanced C #define directives). All occurrences of NEXTCHAR in the code will be replaced by the instructions between MACRO and ENDM. A macro can have arguments, but this feature is not used here.
Then we have the BPUT, SKIP_NUM and SKIP1_NUM subroutines. They work much like functions, only that the argument passing is implicit. You will have to decide for each subroutine that for example registers D1 to D4 are arguments, and the result is returned in D0.
For those who have never seen assembler before, see this as a brief introduction to how assembler programming works. For others, here is a neat way to write filters for QDOS.