           jmp main
@read      equ    3fh                ;read file/device
@write     equ    40h                ;write file/device
@dosver    equ    30h                ;get dos version
@prnstr    equ    09h                ;print string
cr         equ    0dh                ;carriage return character
lf         equ    0ah                ;line feed character
tab        equ    09h                ;horizontal tab character
stdin      equ    0000h              ;standard input
stdout     equ    0001h              ;standard output
buf_size   equ    1024       ;size of input and output buffers
max_line   equ    255        ;maximum input line length
yes        equ    0ffh       ;boolean true
no         equ    00h        ;boolean false
para_c     equ    b[80h]
para_a     equ    [81h]
innum      dw 0000h      ;number of characters in input buffer
outnum     dw 0000h      ;number of characters in output buffer
column     dw 0000h      ;current column in line
baddos     db cr,lf,'Dieses Programm ben”tigt DOS >= 2.00',cr,lf,'$'
nomem      db cr,lf,'Zuwenig Speicher * Programm-Abbruch *',cr,lf,'$'
help       db cr,lf
           db 'TABWEG              Version 1.01           August 1988',cr,lf,lf
           db '* KEIN *  Copyright by ms         Public-Domain',cr,lf,lf
           db 'angelehnt an DETAB von D. Whitman ( chasm )  ',cr,lf
           db 'Erstellt mit A86    Public-Domain ',cr,lf,lf
           db 'syntax: tabweg  [?] < ein.txt > aus.txt',cr,lf
           db cr,lf
           db 'TABWEG liest ber Standard-Input ein Textfile,',cr,lf
           db 'ersetzt alle Tabs (ascii 09 ) durch 8 Blanks ( ascii 32 ).',cr,lf
           db 'Die Ausgabe erfolgt ber Standard-Output in ein Textfile.',cr,lf,lf
           db 'Anstelle eines Textfiles kann auch CON oder PRN verwendet werden.'
           db cr,lf,lf,lf
           db 'Der Aufruf : TABWEG ? oder nur TABWEG bringt diese Meldung'
           db cr,lf,lf,lf,'$'

main:      cmp para_c,0
           jg mai1
           mov ah,040
           mov bx,0002
           mov cx,offset main
           sub cx,offset help
           sub cx,1
           mov dx,offset help
           int 021h
           mov ah,04ch
           int 021h

mai1:      call    setup           ;check dos,parse options
           call    stops           ;fill tab stop array
           call    process         ;expand tabs
           mov     ah,04ch
           int     021h            ;and return to dos

;======================================
; subroutine setup
; checks for proper dos,parses options
;======================================
setup:     mov     ah,@dosver     ;what dos are we under?
           int     21h
           cmp     al,2           ;2.0 or over?
           jae     a_help         ;yes,skip
           mov     ah,@prnstr     ;no,bitch
           mov     dx,offset baddos
           int     21h
           mov     ah,04ch
           int     021h

a_help:    xor     ch,ch           ;cx <== param count
           mov     cl,para_c ;  "
           cmp     cl,00h            ;any params?
           je      a_exit            ;return if none
           mov     di,offset para_a  ;scan for help request
           mov     al,'?'
           repnz                   ;repeat until matched or end
           scasb
           jnz     a_exit          ;reached end,no match? skip
           mov ah,040
           mov bx,0002
           mov cx,offset main
           sub cx,offset help
           sub cx,1
           mov dx,offset help
           int     21h
           mov    ah,04ch
           int     021h
a_exit:    ret


;=========================================
; subroutine stops
;
; set tab stops in array.  following the system
; standard,stops are set every 8 columns.
;=========================================

stops:     mov   di,offset tabstops       ;point to array
           mov   cx,1                     ;count columns
           mov   dl,8                     ;stops every 8 columns
s1:        cmp   cx,max_line              ;are we done?
           jg    s_exit                   ;yes,exit
           mov   ax,cx                    ;no,continue: get count
           div   dl                       ;ah gets remainder
           cmp   ah,0                     ;multiple of eight?
           jne   s2                       ;branch if not
           mov  [di],yes                  ;yes,so mark tab
           jmp short  s3                  ;and skip
s2:        mov  [di],no                   ;no,mark no tab
s3:        inc   cx                       ;bump counts
           inc   di                       ; "     "
           jmp short  s1                  ;and loop till done
s_exit:    ret

;=========================================
; subroutine process
;
;   1. load input buffer
;   2. convert each char,pass to output buffer
;   3. dump output buffer
;   4. repeat until eof
;==========================================

process:   mov     di,offset buf_out  ;point to output buffer
           mov    outnum,0            ;output buffer is empty
           mov    column,0            ;start in column 0

bu1:       mov    ah,@read            ;read
           mov     bx,stdin           ;from stdin
           mov     cx,buf_size        ;one buffer's worth
           mov     dx,offset buf_in
           int     21h
           cmp     ax,00h             ;test for eof
           jz      budone             ;if so,done
           mov     cx,ax              ;cx <== number of chars read
           mov     si,offset buf_in   ;source is input buffer

bu2:       lodsb                      ;al <== next char from buffer
           mov     bx,column          ;bx <== current column
           cmp     al,tab             ;test if tab character
           jne     bu3                ;nope,skip
           mov     al,' '             ;yes,replace with spaces

bu2a:      call    putchar             ;send blank to stdout
           inc     bx                  ;bump column count
           cmp     bx,max_line         ;line overflowing?
           jge     bu2b                ;yes,so assume we're at a stop
           cmp    (offset tabstops+[bx]),yes    ;are we at a stop?
           jne     bu2a                     ;no,keep emitting spaces
bu2b:      jmp short    bu5                 ;yes,so done expanding

bu3:       cmp     al,lf              ;eol?
           jne     bu4                ;nope,skip
           mov     bx,0               ;reset column counter
           call    putchar            ;print char
           jmp short    bu5           ;and skip

bu4:       call    putchar             ;otherwise,just send char to stdout
           inc     bx                  ;and bump column count

bu5:       mov     column,bx          ;save column count
           loop    bu2                ;loop until buffer processed
           jmp short    bu1           ;and loop until eof

budone:    cmp   w[outnum],0         ;input done.  any outstanding output?
           jle     buexit            ;no,exit
           call    dumpbuf            ;yes,empty buffer

buexit:    ret

;===================================================
; subroutine putchar
;
; moves the character in al into the output buffer.
; if the buffer is now full,it is dumped to disk.
;===================================================
putchar:   stosb                   ;move character into buffer
           inc  w[outnum]          ;bump count of chars in buffer
           cmp w[outnum],buf_size  ;if buffer full?
           jl  p_exit              ;no,skip
           call dumpbuf            ;yes,dump buffer to disk
p_exit:    ret

;==================================================
; subroutine dumpbuf
;
; dumps the output buffer to stdout.
;==================================================
dumpbuf:   push    ax            ;save active registers
           push    bx            ; "      "      "
           push    cx            ; "      "      "
           mov     ah,@write     ;write
           mov     bx,stdout     ;to stdout
           mov     cx,outnum     ;number of chars for output
           mov     dx,offset buf_out
           int     21h
           mov     outnum,0            ;reset buffer
           mov     di,offset buf_out   ;  "     "
           pop     cx             ;restore active registers
           pop     bx             ;   "       "       "
           pop     ax             ;   "       "       "
           ret

tabstops   db 0                ;tab stop array
           org $+max_line
buf_in     db 0                ;input buffer
           org $+buf_size
buf_out    db 0                ;output buffer
