; Вариант 19
; Версия 2. Данные из файла 2.txt
; Еникеев И.Р.  2084/1
.286
cseg segment byte public 'CODE'
        assume cs:cseg, ds:dseg, es:dseg
start:  
        mov ax, seg dseg
        mov es, ax
        mov ds, ax
 ;------------------------------
        mov ax, 3d00h
        mov dx, offset fname
        int 21h
        jnc $+5
        jmp no_file

        push ax
        mov bp, 1               ; BP will hold ifWORK
line_loop:
        pop bx
        mov cx, 1
        mov dx, offset from
char_loop:
        mov ah, 3fh
        int 21h
        mov si, dx
        test ax, ax
        jz eof
        cmp byte ptr ds:[si], 0ah
        je eol
        inc dx
        jmp char_loop

eol:    mov byte ptr ds:[si-1], 0
        push bx
        jmp work

eof:    mov byte ptr ds:[si], 0
        xor bp, bp        
        mov ah, 3eh
        int 21h
work:   
        cmp byte ptr [from], 0  ; Test input string to non-empty
        jnz $+5
        jmp no_string

        mov di, offset to
        add di, [need_len]
        mov [max_len], di
        
        xor cx, cx              ; Begin of work
        not cx
        mov di, offset from     
        mov al, ' '
        repz scasb              ; Skip all spaces at the beginning
        dec di
        mov real_ofs, di        ; Save offset of first non-space character

        xor ax, ax
        xor cx, cx
        not cx
        repnz scasb             ; Search for tailing zero

        dec di
        dec di                  ; di - points to last non-zero char

        not cx
        dec cx
        push cx                 ; Save full length of string

        std                     ; Change direction flag to backwards
        mov al, ' '
        xor cx, cx
        not cx
        repz scasb              ; Search for non-space charecter at the end of string

        inc di
        inc di                  ; di points to next after last non-space
        mov byte ptr [di], 0    ; Discard tailing spaces

        not cx
        dec cx                  ; Get count of tailing spaces

        pop ax                  
        sub ax, cx              ; Calculate length of trimmed string (without leading and tailing spaces)
        jg  $+5                 ; If length is positive - countinue work
        jmp no_string

        cld                     ; Restore normal direction flag
        mov cx, ax              ; Set trimmed length
        mov di, real_ofs        ; Set pointer to the beginng of string


        xor bx, bx              ; bx holds non-space chars count
        xor dx, dx              ; dx holds word count-1

word_loop:                      ; loop for calculate word and non-space char count in sentence
        mov al, [di]            ; get char from string
        test al, al             ; test for EOS
        jz next_1               ; If so - we are reached end of string
        cmp al, ' '             ; Compare with space
        jz inloop1              ; if so - skip all following spaces and inc word count
        inc bx                  ; Increment letters count
        inc di                  ; advance pointer
        dec cx                  ; decrement residuary string length
        jnz word_loop           ; if we got non-tested chars - continue loop
        jmp next_1              ; if not - end our loop

inloop1:                        ; If we've found space in input string
        inc dx                  ; Increment word count
        inc cx                  ; Inc string's length for next line's right work
        repz scasb              ; Skip all following spaces in input string
        dec di                  ; Adjust pointer to point to first non-space char
        jmp word_loop           ; Countinue loop
                
next_1: mov ax, need_len        ; Get our required output string length
        sub ax, bx              ; Decrease this length by total count of letters
                                ; to get count of spaces, that we need to arrange
                                ; between words

        cmp ax, dx              ; if that count is less than spaces we've got to put
                                ; betten words to save them separated then
        jge need_spaces         ; Show error message

        mov toins, 1            ; We'll have to crop input string, so leave we'll 
                                ; only one space as gaps
        xor dx, dx              ; And don't need for additional spaces
        jmp just_copy

need_spaces:        
        mov bx, dx              ; bx will hold count of gaps between words
        test bx, bx             ; if we've got only one word - so we don't need any gaps
        jz just_copy

        xor dx, dx              ; clear dx for normal div's work
        div bx                  ; AX - holds count of spaces, bx - count of gaps, so
        mov toins, ax           ; new ax - holds space count in each gap, and 
                                ; dx - remainder of division

just_copy:                      ; Prepare of our main loop of creation of output string
        mov si, real_ofs        ; Source - input string
        mov di, offset to       ; Destination - memory to hold output string
        
main_loop:                      ; Here comes the main loop
        cmp di, [max_len]
        jl $+4
        jmp wr_out

        mov al, [si]            ; get character
        test al, al             ; if we're reached end of string
        jz wr_out               ; goto write output

        cmp al, ' '             ; if we got space - 
        jz space                ; work with this gap

        movsb                   ; else just copy character from input to output
        jmp main_loop           ; continue loop

space:                          ; if we've got space - skip all following in input 
                                ; and add needed space count in gap in output
        xor cx, cx
        not cx
        xchg si, di             ; scas works with di - so we need to put si there
        repz scasb              ; al - hold ' ' (by that reason we are here)
        dec di                  ; so we've skipped all following spaces in input

        xchg di, si             ; return poiners at their normal state 

        mov cx, toins           ; put needed space count in output string
        rep stosb               ; in al we're still got ' '

        test dx, dx             ; if our remainder are zero
        jz main_loop            ; - continue loop
        stosb                   ; else - put one more space in current gap
        dec dx                  ; and decrease remainder
        jmp main_loop           ; and of course continue our loop
        
wr_out:                         ; Now we're reached final touches to our prog 
        mov byte ptr [di], 0
        xor ax, ax              ; We're going to search for tailing null char
                                ; (we could skip this line because the only way to get here
                                ;  is only from testing al to zero, so it DEFINITLY zero)
        mov di, offset to       ; point to output string
        xor cx, cx              ; Search for FFFF chars
        not cx                  ;
        repnz scasb             ; Search tailing zero
        dec di                  ; And put
        mov al, '$'             ; '$'
        stosb                   ; instead of it

        mov ah, 9               ; Use DOS's help
        mov dx, offset to       ; to write output 
        int 21h                 ; to user's eyes
        mov dx, offset newline 
        int 21h                 
outta:        
        test bp, bp
        jz norm_out
        jmp line_loop; output is OK!
bad_len:
        jmp short norm_out
no_string:
        mov ah, 09h
        mov dx, offset newline
        int 21h
        jmp short outta
no_file:
        mov ah, 09h
        mov dx, offset no_file_str
        int 21h
        jmp norm_out
err_read:
        mov ah, 09h
        mov dx, offset err_read_str
        int 21h
        jmp norm_out

norm_out:
        mov ah, 4ch
        int 21h
ends
dseg segment para public 'DATA'
fname   db '2.txt',0
toins   dw 0
divis   dw 0
wordcnt dw 0
need_len dw 80
real_ofs dw 0
max_len dw 0
newline db 0dh, 0ah, '$'
no_file_str db 'Couldn''t open file 2.txt$'
err_read_str db 'Error while reading 2.txt$'
        dw 0    ; front border of string
from    db 200 dup (?)
to      db 100 dup (?)
ends
sseg segment stack
 db 200 dup (?)
ends
end start
