        NAME    VWTOWS
        PAGE    55,132
        TITLE   'VWTOWS --- FILTER VW TEXT FILE FOR WORDSTAR'

;
; VWTOWS -- CONVERT VOLKSWRITER FILE TO WORDSTAR DOCUMENT FILE.
; EXTRA SEQUENTIAL SPACES ARE CONVERTED TO "SOFT SPACES".
; PARAGRAPH MARKERS ARE CONVERTED TO "HARD CARRIAGE RETURNS".
; NORMAL CR'S ARE CONVERTED TO "SOFT CARRIAGE RETURNS".
; ALL OTHER CONTROL CHARACTERS EXCEPT FOR LINE FEEDS AND FORM
; FEEDS ARE DISCARDED.

; VERSION 1.0    10 DEC 83
;
; COPYRIGHT (C) 1983 BY RAY DUNCAN

CR      EQU     0DH             ;ASCII CARRIAGE RETURN
LF      EQU     0AH             ;ASCII LINE FEED
FF      EQU     0CH             ;ASCII FORM FEED
EOF     EQU     01AH            ;END OF FILE MARKER
TAB     EQU     09H             ;ASCII TAB CHARACTER

COMMAND EQU     80H             ;BUFFER FOR COMMAND TAIL

BLKSIZE EQU     1024            ;BLOCKING/DEBLOCKING SIZE


CSEG    SEGMENT PARA PUBLIC 'CODE'

        ASSUME  CS:CSEG,DS:DATA,ES:DATA,SS:STACK


CLEAN   PROC    FAR             ;ENTRY POINT FROM PC-DOS

        PUSH    DS              ;SAVE DS:0000 FOR FINAL
        XOR     AX,AX           ;RETURN TO PC-DOS
        PUSH    AX
        MOV     AX,DATA         ;MAKE OUR DATA SEGMENT
        MOV     ES,AX           ;ADDRESSABLE VIA ES REGISTER
        CALL    INFILE          ;GET PATH AND FILE SPEC.
                                ;FOR INPUT FILE
        MOV     AX,ES           ;SET DS=ES FOR REMAINDER
        MOV     DS,AX           ;OF PROGRAM
        JNC     CLEAN1          ;JUMP, GOT ACCEPTABLE NAME
        MOV     DX,OFFSET MSG4  ;MISSING OR ILLEGAL FILESPEC,
        JMP     CLEAN9          ;PRINT ERROR MESSAGE AND EXIT.

CLEAN1: CALL    OUTFILE         ;SET UP OUTPUT FILE NAME
        CALL    OPEN_INPUT      ;NOW TRY TO OPEN INPUT FILE
        JNC     CLEAN2          ;JUMP,OPENED INPUT OK
        MOV     DX,OFFSET MSG1  ;OPEN OF INPUT FILE FAILED,
        JMP     CLEAN9          ;PRINT ERROR MSG AND EXIT.

CLEAN2:
        CALL    OPEN_OUTPUT     ;TRY TO OPEN OUTPUT FILE.
        JNC     CLEAN25         ;JUMP,OPENED OK
        MOV     DX,OFFSET MSG2  ;OPEN OF OUTPUT FILE FAILED,
        JMP     CLEAN9          ;PRINT ERROR MESSAGE AND EXIT.

CLEAN25:                        ;ALL FILES OPENED SUCCESSFULLY,
        CALL    SIGN_ON         ;PRINT SIGN-ON MESSAGE AND
        CALL    INIT_BUFFS      ;SET UP BUFFERS.

CLEAN3:                         ;NOW FILTER THE FILE.
        CALL    GET_CHAR        ;READ 1 CHARACTER FROM INPUT.
        AND     AL,07FH         ;STRIP OFF THE HIGH BIT
        CMP     AL,20H          ;IS IT A CONTROL CODE OR SPACE?
        JA      CLEAN4          ;NO,WRITE IT TO NEW FILE
        JNE     CLEAN32         ;JUMP IF NOT A SPACE CODE
        MOV     AH,PREV_CHAR    ;CHECK IF LAST CHAR WAS A SPACE
        AND     AH,7FH
        CMP     AH,20H
        JNE     CLEAN4          ;NO,WRITE NORMAL SPACE
        OR      AL,80H          ;YES, CONVERT THIS SPACE TO "SOFT"
        JMP     CLEAN4          ;AND WRITE IT TO THE FILE
CLEAN32:                        ;IT IS CONTROL CODE,
        CMP     AL,EOF          ;IS IT END OF FILE MARKER?
        JE      CLEAN6          ;YES,JUMP TO CLOSE FILES.

        CMP     AL,14H          ;IS IT VW PARAGRAPH MARKER?
        JNE     CLEAN33         ;NO,JUMP
        CALL    GET_CHAR        ;DISCARD FOLLOWING CR-LF
        CALL    GET_CHAR
        MOV     AL,CR           ;CONVERT IT TO "HARD" RETURN
        CALL    PUT_CHAR        ;AND LINE FEED SEQUENCE.
        MOV     AL,LF
        JMP     CLEAN35         ;AND WRITE IT TO FILE.
CLEAN33:
        CMP     AL,TAB          ;IS IT A TAB COMMAND?
        JZ      CLEAN5          ;YES,JUMP TO SPECIAL PROCESSING.
        CMP     AL,CR           ;IF CARRIAGE RETURN AND THE LINE IS
        JNE     CLEAN34         ;NOT EMPTY, CONVERT IT TO "SOFT RETURN"
        MOV     BX,COLUMN
        OR      BX,BX
        JZ      CLEAN35
        OR      AL,80H          ;AND WRITE IT TO FILE
        JMP     CLEAN35
CLEAN34:
        CMP     AL,LF           ;IF LINE FEED WRITE IT TO FILE,
        JNE     CLEAN3          ;OTHERWISE DISCARD CONTROL CODE.
CLEAN35:                        ;IF IT IS ONE OF THOSE THREE,
        MOV     COLUMN,0        ;INCIDENTALLY INITIALIZE
        JMP     CLEAN45         ;COLUMN COUNT FOR TAB PROCESSOR.

CLEAN4:                         ;COUNT ALPHANUMERIC CHARS. SENT.
        INC     COLUMN

CLEAN45:                        ;WRITE THIS CHARACTER TO
        CALL    PUT_CHAR        ;OUTPUT FILE,
        JNC     CLEAN3          ;IF CY NOT SET, WRITE WAS
                                ;OK SO GO GET NEXT CHAR.
CLEAN47:
        CALL    CLOSE_INPUT     ;IF CY SET, DISK IS FULL
        CALL    CLOSE_OUTPUT    ;SO CLOSE FILES AND EXIT
        MOV     DX,OFFSET MSG5  ;WITH ERROR MESSAGE.
        JMP     CLEAN9

CLEAN5:                         ;PROCESS TAB CHARACTER
        MOV     AX,COLUMN       ;LET DX:AX=COLUMN COUNT
        CWD
        MOV     CX,8            ;DIVIDE IT BY EIGHT...
        IDIV    CX
        SUB     CX,DX           ;REMAINDER IS IN DX.
        ADD     COLUMN,CX       ;UPDATE COLUMN POINTER.
CLEAN55:                        ;8 MINUS THE REMAINDER
        PUSH    CX              ;GIVES US THE NUMBER OF
        MOV     AL,20H          ;SPACES TO SEND OUT TO
        CALL    PUT_CHAR        ;MOVE TO THE NEXT TAB POSITION
        POP     CX              ;RESTORE SPACE COUNT
        JC      CLEAN47         ;JUMP IF DISK IS FULL
        LOOP    CLEAN55
        JMP     CLEAN3          ;GET NEXT CHARACTER

CLEAN6:                         ;END OF FILE DETECTED,
        CALL    PUT_CHAR        ;WRITE END-OF-FILE MARKER,
        JC      CLEAN47         ;JUMP IF DISK WAS FULL
        CALL    FLUSH_BUFFS     ;WRITE REMAINING DATA TO DISK
        JC      CLEAN47         ;IF CY SET,DISK WAS FULL
                                ;OTHERWISE FILE WAS WRITTEN OK
        CALL    CLOSE_INPUT     ;CLOSE INPUT AND OUTPUT
        CALL    CLOSE_OUTPUT    ;FILES.
        MOV     DX,OFFSET MSG3  ;ADDR OF SUCCESS MESSAGE,

CLEAN9:                         ;PRINT MESSAGE AND RETURN
        MOV     AH,9            ;CONTROL TO PC-DOS
        INT     21H
        RET

CLEAN   ENDP


INFILE  PROC    NEAR            ;PROCESS NAME OF INPUT FILE
                                ;DS:SI <- ADDR COMMAND LINE
        MOV     SI,OFFSET COMMAND
                                ;ES:DI <- ADDR FILESPEC BUFFER
        MOV     DI,OFFSET INPUT_NAME
        CLD
        LODSB                   ;ANY COMMAND LINE PRESENT?
        OR      AL,AL           ;RETURN ERROR STATUS IF NOT.
        JZ      INFILE4
INFILE1:                        ;SCAN OVER LEADING BLANKS
        LODSB                   ;TO FILE NAME
        CMP     AL,CR           ;IF WE HIT CARRIAGE RETURN
        JZ      INFILE4         ;FILENAME IS MISSING.
        CMP     AL,20H          ;IS THIS A BLANK?
        JZ      INFILE1         ;IF SO KEEP SCANNING.

INFILE2:                        ;FOUND FIRST CHAR OF NAME,
        STOSB                   ;MOVE LAST CHAR. TO OUTPUT
                                ;FILE NAME BUFFER.
        LODSB                   ;CHECK NEXT CHARACTER, FOUND
        CMP     AL,CR           ;CARRIAGE RETURN YET?
        JE      INFILE3         ;YES,EXIT WITH SUCCESS CODE
        CMP     AL,20H          ;IS THIS A BLANK?
        JNE     INFILE2         ;IF NOT KEEP MOVING CHARS.

INFILE3:                        ;EXIT WITH CARRY =0
        CLC                     ;FOR SUCCESS FLAG
        RET

INFILE4:                        ;EXIT WITH CARRY =1
        STC                     ;FOR ERROR FLAG
        RET
INFILE  ENDP

OUTFILE PROC    NEAR            ;SET UP PATH AND FILE
        CLD                     ;NAME FOR OUTPUT FILE.
        MOV     CX,64           ;LENGTH TO MOVE
        MOV     SI,OFFSET INPUT_NAME  ;SOURCE ADDR
        MOV     DI,OFFSET OUTPUT_NAME ;DEST ADDR
        REP MOVSB               ;TRANSFER THE STRING
        MOV     DI,OFFSET OUTPUT_NAME
OUTFILE1:                       ;SCAN STRING LOOKING FOR
        MOV     AL,[DI]         ;"." MARKING START OF EXTENSION
        OR      AL,AL           ;OR ZERO BYTE MARKING NAME END.
        JZ      OUTFILE2        ;IF EITHER IS FOUND,JUMP.
        CMP     AL,'.'
        JE      OUTFILE2        ;BUMP STRING POINTER, LOOP
        INC     DI              ;IF NEITHER '.' OR ZERO FOUND.
        JMP     OUTFILE1
OUTFILE2:                       ;FOUND ZERO OR '.',FORCE THE
                                ;EXTENSION OF OUTPUT FILE TO '.WS'
        MOV     SI,OFFSET OUTFILE_EXT
        MOV     CX,4
        REP MOVSB
        RET                     ;BACK TO CALLER
OUTFILE ENDP

OPEN_INPUT PROC NEAR            ;OPEN INPUT FILE
                                ;DS:DX=ADDR FILENAME
        MOV     DX,OFFSET INPUT_NAME
        MOV     AL,0            ;AL=0 FOR READ ONLY
        MOV     AH,3DH          ;FUNCTION 3DH=OPEN
        INT     21H             ;HANDLE RETURNED IN AX,
        MOV     INPUT_HANDLE,AX ;SAVE IT FOR LATER.
        RET                     ;CY IS SET IF ERROR
OPEN_INPUT ENDP

OPEN_OUTPUT PROC NEAR           ;OPEN OUTPUT FILE
                                ;DS:DX=ADDR FILENAME
        MOV     DX,OFFSET OUTPUT_NAME
        MOV     AL,1            ;AL=1 FOR WRITE ONLY
        MOV     AH,3CH          ;FUNCTION 3CH=MAKE OR
        INT     21H             ;TRUNCATE EXISTING FILE
                                ;HANDLE RETURNED IN AX
        MOV     OUTPUT_HANDLE,AX;SAVE IT FOR LATER.
        RET                     ;RETURN CY=TRUE IF ERROR
OPEN_OUTPUT ENDP

CLOSE_INPUT PROC NEAR           ;CLOSE INPUT FILE
        MOV     BX,INPUT_HANDLE ;BX=HANDLE
        MOV     AH,3EH
        INT     21H
        RET
CLOSE_INPUT ENDP

CLOSE_OUTPUT PROC NEAR          ;CLOSE OUTPUT FILE
        MOV     BX,OUTPUT_HANDLE;BX=HANDLE
        MOV     AH,3EH
        INT     21H
        RET
CLOSE_OUTPUT ENDP

GET_CHAR PROC   NEAR            ;GET ONE CHARACTER FROM INPUT BUFFER
        MOV     BX,INPUT_PTR
        CMP     BX,BLKSIZE
        JNE     GET_CHAR1
        CALL    READ_BLOCK
        MOV     BX,0
GET_CHAR1:
        MOV     AL,[INPUT_BUFFER+BX]
        INC     BX
        MOV     INPUT_PTR,BX
        RET
GET_CHAR ENDP

PUT_CHAR PROC   NEAR            ;PUT ONE CHARACTER INTO OUTPUT BUFFER
        MOV     PREV_CHAR,AL    ;SAVE COPY OF MOST RECENT OUTPUT
        MOV     BX,OUTPUT_PTR
        MOV     [OUTPUT_BUFFER+BX],AL
        INC     BX
        MOV     OUTPUT_PTR,BX
        CMP     BX,BLKSIZE      ;BUFFER FULL YET?
        JNE     PUT_CHAR1       ;NO,JUMP
        CALL    WRITE_BLOCK     ;YES,WRITE THE BLOCK
        RET                     ;RETURN CY AS STATUS CODE
PUT_CHAR1:
        CLC                     ;RETURN CY CLEAR FOR OK STATUS
        RET
PUT_CHAR ENDP

READ_BLOCK PROC NEAR
        MOV     BX,INPUT_HANDLE ;READ FIRST BLOCK OF INPUT
        MOV     CX,BLKSIZE
        MOV     DX,OFFSET INPUT_BUFFER
        MOV     AH,3FH
        INT     21H
        JNC     READ_BLOCK1     ;JUMP IF NO ERROR STATUS
        MOV     AX,0            ;SIMULATE A ZERO LENGTH READ IF ERROR
READ_BLOCK1:
        CMP     AX,BLKSIZE      ;WAS FULL BUFFER READ IN?
        JE      READ_BLOCK2     ;YES,JUMP
        MOV     BX,AX           ;NO, STORE END-OF-FILE MARK
        MOV     BYTE PTR [INPUT_BUFFER+BX],EOF
READ_BLOCK2:
        XOR     AX,AX           ;INITIALIZE INPUT BUFFER POINTER
        MOV     INPUT_PTR,AX
        RET
READ_BLOCK ENDP

WRITE_BLOCK PROC NEAR           ;WRITE BLOCKED OUTPUT (BLKSIZE BYTES)
        MOV     DX,OFFSET OUTPUT_BUFFER
        MOV     CX,BLKSIZE
        MOV     BX,OUTPUT_HANDLE
        MOV     AH,40H
        INT     21H
        XOR     BX,BX           ;INITIALIZE POINTER TO BLOCKING BUFFER
        MOV     OUTPUT_PTR,BX
        CMP     AX,BLKSIZE      ;WAS CORRECT LENGTH WRITTEN?
        JNE     WRITE_BLOCK1    ;NO,DISK MUST BE FULL
        CLC                     ;YES,RETURN CY=0 INDICATING ALL OK
        RET
WRITE_BLOCK1:                   ;DISK IS FULL, RETURN CY =1
        STC                     ;AS ERROR CODE
        RET
WRITE_BLOCK ENDP

INIT_BUFFS PROC NEAR
        CALL    READ_BLOCK      ;READ 1ST BLOCK OF INPUT
        XOR     AX,AX           ;INITIALIZE POINTER TO OUTPUT
        MOV     OUTPUT_PTR,AX   ;OUTPUT BLOCKING BUFFER
        RET
INIT_BUFFS ENDP

FLUSH_BUFFS PROC NEAR           ;WRITE ANY DATA IN OUTPUT BUFFER TO DISK
        MOV     CX,OUTPUT_PTR
        OR      CX,CX
        JZ      FLUSH_BUFFS1    ;JUMP,BUFFER IS EMPTY
        MOV     BX,OUTPUT_HANDLE
        MOV     DX,OFFSET OUTPUT_BUFFER
        MOV     AH,40H
        INT     21H
        CMP     AX,OUTPUT_PTR   ;WAS WRITE SUCCESSFUL?
        JNZ     FLUSH_BUFFS2    ;NO,JUMP
FLUSH_BUFFS1:                   ;SUCCESSFUL DISK WRITE,
        CLC                     ;RETURN CY=0 FOR
        RET                     ;SUCCESS FLAG
FLUSH_BUFFS2:                   ;DISK WAS FULL SO WRITE FAILED,
        STC                     ;RETURN CY=1 AS ERROR FLAG
        RET
FLUSH_BUFFS ENDP

SIGN_ON PROC    NEAR            ;PRINT SIGN-ON MESSAGE
        MOV     DX,OFFSET MSG6  ;TITLE...
        MOV     AH,9
        INT     21H
        MOV     DX,OFFSET MSG7  ;INPUT FILE:
        MOV     AH,9
        INT     21H
        MOV     DX,OFFSET INPUT_NAME
        CALL    PASCIIZ
        MOV     DX,OFFSET MSG8  ;OUTPUT FILE:
        MOV     AH,9
        INT     21H
        MOV     DX,OFFSET OUTPUT_NAME
        CALL    PASCIIZ
        MOV     DX,OFFSET MSG9
        MOV     AH,9
        INT     21H
        RET
SIGN_ON ENDP

PASCIIZ PROC    NEAR            ;CALL DX=OFFSET OF ASCIIZ STRING
        MOV     BX,DX           ;WHICH WILL BE PRINTED ON STANDARD OUTPUT
PASCIIZ1:
        MOV     DL,[BX]
        OR      DL,DL
        JZ      PASCIIZ9
        CMP     DL,'A'
        JB      PASCIIZ2
        CMP     DL,'Z'
        JA      PASCIIZ2
        OR      DL,20H
PASCIIZ2:
        MOV     AH,2
        INT     21H
        INC     BX
        JMP     PASCIIZ1
PASCIIZ9:
        RET
PASCIIZ ENDP

CSEG    ENDS


DATA    SEGMENT PARA PUBLIC 'DATA'

INPUT_NAME      DB      64 DUP (0)      ;BUFFER FOR INPUT FILESPEC
OUTPUT_NAME     DB      64 DUP (0)      ;BUFFER FOR OUTPUT FILESPEC

INPUT_HANDLE    DW      0               ;TOKEN RETURNED BY PCDOS
OUTPUT_HANDLE   DW      0               ;TOKEN RETURNED BY PCDOS

INPUT_PTR       DW      0               ;POINTER TO INPUT BLOCKING BUFFER
OUTPUT_PTR      DW      0               ;POINTER TO OUTPUT BLOCKING BUFFER

OUTFILE_EXT     DB      '.WS',0         ;EXTENSION FOR FILTERED FILE

COLUMN          DW      0               ;COLUMN COUNT FOR TAB PROCESSING

PREV_CHAR       DB      0               ;LAST CHARACTER WRITTEN TO OUTPUT

MSG1            DB      CR,LF
                DB      'CANNOT FIND INPUT FILE.'
                DB      CR,LF,'$'

MSG2            DB      CR,LF
                DB      'FAILED TO OPEN OUTPUT FILE.'
                DB      CR,LF,'$'

MSG3            DB      CR,LF
                DB      'FILE PROCESSING COMPLETED'
                DB      CR,LF,'$'

MSG4            DB      CR,LF
                DB      'MISSING FILE NAME.'
                DB      CR,LF,'$'

MSG5            DB      CR,LF
                DB      'DISK IS FULL.'
                DB      CR,LF,'$'

MSG6            DB      CR,LF
                DB      'CONVERT VOLKSWRITER FILE TO WORDSTAR DOCUMENT'
                DB      CR,LF
                DB      'COPYRIGHT (C) 1983 LABORATORY MICROSYSTEMS INC.'
                DB      CR,LF,'$'

MSG7            DB      CR,LF,'INPUT FILE:   $'

MSG8            DB      CR,LF,'OUTPUT FILE:  $'

MSG9            DB      CR,LF,'$'


INPUT_BUFFER    DB      BLKSIZE DUP (?) ;BUFFER FOR DEBLOCKING OF DATA
                                        ;FROM INPUT FILE

OUTPUT_BUFFER   DB      BLKSIZE DUP (?) ;BUFFER FOR BLOCKING OF DATA
                                        ;SENT TO OUTPUT FILE

DATA    ENDS


STACK   SEGMENT PARA STACK 'STACK'
        DB      64 DUP (?)
STACK   ENDS

        END     CLEAN
                  ;
;                                                            