assemblyx86-16dosbox

Replace digit with a word and write to a file using assembler 8086


I'm writing a program that converts digits into words and also writes every other character unchanged to output file. But for some reason the output buffer overwrites characters on top of each other.

Ex. input file looks like this: 0a

the output file should look like this: nulisa

Instead it looks like this: aulis

Here's what I have so far:

.model small         
.stack 100h            

.data
    msgHelp        DB "Usage: program.exe <input_file> <output_file>$"    
    msgFileError   DB "Error: File not found or cannot be opened.$"    
    input db 200 dup (0)           
    output  db 200 dup (0)          
    skBuf   db 20 dup (?)  
    raBuf   db 200 dup (?) 
    words db "nulis", 0, "vienas", 0, "du", 0, "trys", 0, "keturi", 0, "penki", 0, "sesi", 0, "septyni", 0, "astuoni", 0, "devyni", 0
    wordOffsets dw 0, 6, 13, 16, 21, 28, 34, 39, 47, 55
    dFail   dw ?     
    rFail   dw ?     
    raBufPos dw 0    

.code

start:
    MOV ax, @data          
    MOV ds, ax         
    MOV di, offset raBuf    

    MOV bx, 82h             
    MOV si, offset input    

    cmp byte ptr es:[80h], 0 
    je help                  
    cmp es:[82h], '?/'       
    jne check_param          
    cmp byte ptr es:[84h], 13
    je help
    jmp check_param 

help:
    mov ah, 9              
    mov dx, offset msgHelp 
    int 21h                
    jmp programEnd       

check_param:
    cmp byte ptr es:[bx], 13 
    je next_filename         
    cmp byte ptr es:[bx], 32 
    je next_filename
    mov dl, byte ptr es:[bx] 
    mov [si], dl             
    inc bx                   
    inc si                   
    jmp check_param          

next_filename:
    inc bx 

    mov si, offset output    

output_filename_loop:
    cmp byte ptr es:[bx], 13 
    je found                

    mov dl, byte ptr es:[bx]
    mov [si], dl             
    inc bx                    
    inc si                    
    jmp output_filename_loop 

found:
    MOV ah, 3Dh              
    MOV al, 00      
    MOV dx, offset input
    INT 21h 
    JC file_error       
    MOV dFail, ax           

    MOV ah, 3Ch   
    MOV cx, 0               
    MOV dx, offset output      
    INT 21h                   
    JC file_error 
    MOV rFail, ax           

read:
    MOV bx, dFail
    CALL ReadBuf            
    CMP ax, 0               
    JE closeInput

    MOV cx, ax              
    MOV si, offset skBuf    

processLoop:
    MOV dl, [si]   
    CMP dl, '0'
    JB notDigit  
    CMP dl, '9'   
    JA notDigit 

    PUSH cx                  
    PUSH si
    PUSH di
    CALL ConvertDigitToWord  
    POP di                   
    POP si
    POP cx
    JMP skip 

notDigit:
    MOV [di], dl            
    INC di  
    INC raBufPos            

skip:
    INC si                  
    DEC cx                  
    JNZ processLoop         

writeOutput:
    MOV cx, raBufPos        
    MOV bx, rFail           
    MOV dx, offset raBuf    
    CALL WriteBuf
    MOV di, offset raBuf    
    MOV raBufPos, 0         
    JMP read                

file_error:
    MOV ah, 09h  
    MOV dx, offset msgFileError
    INT 21h
    jmp programEnd

closeOutput:
    MOV ah, 3Eh     
    MOV bx, rFail   
    INT 21h     
    JC file_error   

closeInput:
    MOV ah, 3Eh      
    MOV bx, dFail   
    INT 21h        

programEnd:
    MOV ah, 4Ch 
    MOV al, 0    
    INT 21h 

ConvertDigitToWord PROC
    SUB dl, '0'              
    MOV cl, dl

    MOV si, offset wordOffsets  
    MOV bx, cx 
    SHL bx, 1                
    ADD si, bx               
    MOV ax, [si]             
    ADD ax, offset words     
    MOV si, ax               

copyWord:
    MOV al, [si]             
    CMP al, 0  
    JE doneCopy              
    MOV [di], al             
    INC di                   
    INC si                   
    INC raBufPos             
    JMP copyWord    

doneCopy:
    RET  
ConvertDigitToWord ENDP

ReadBuf PROC
    MOV ah, 3Fh   
    MOV bx, dFail            
    MOV cx, 20               
    MOV dx, offset skBuf     
    INT 21h  
    RET       
ReadBuf ENDP

WriteBuf PROC
    MOV ah, 40h    
    MOV bx, rFail            
    MOV cx, raBufPos         
    MOV dx, offset raBuf     
    INT 21h  
    RET   
WriteBuf ENDP

END start

Solution

  • PUSH cx                  
    PUSH si
    PUSH di
    CALL ConvertDigitToWord  
    POP di                   
    POP si
    POP cx
    

    The DI register is currently only input to the ConvertDigitToWord procedure. But for your program it should also be output from this procedure. Solve it quickly by removing the PUSH di and POP di instructions.

    Want it better? Then avoid redundancy.

    Both DI and raBufPos play a similar role. You don't need that raBufPos variable.
    Just setup DI like you're already doing in mov di, offset raBuf and where you need the output length you can replace MOV cx, raBufPos by lea cx, [di - raBuf]. If your assembler does not accept this, then write it as mov cx, di sub cx, offset raBuf.

    ps. Please edit your post and enclose your whole program between tripple backtick characters.