Search code examples
fileassemblydosx86-16yasm

Working with strings from file YASM (8086)


I have one task to do. I will try to explain it as clearly as possible.

When you run the program, it asks to input (reading file and writing file).

Reading file has lines in range [1; 999]. Every line has six columns. Every column is separated by semicolon (;).

First and second columns contains text symbols in range [1; 20].

Third - fifth columns contains integers [-100; 100].

Last column contains floating number [-9.99; 9.99]. With two symbols after point.

FILE EXAMPLE:

firstA;lB;lC;lD;lE;lF
A11;bas hello;0;0;5;1.15
B12; good day;-100;11;78;1.33

TASK: Output: Only first and second columns, that doesn't contain numbers and symbols 'B', 'C'.

OUTPUT:

firstA, because only this column doesn't have 'B', 'C' and numbers.

Up to now I have written program, that just throws away numbers and symbols. I can't figure out the solution for the full TASK.

My program

%include 'yasmmac.inc'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
org 100h


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .text

    startas:

    macPutString 'Reading file name:', crlf, '$'      ;Input for reading file
    mov al, 128
    mov dx, readingFile
    call procGetStr
    macNewLine

    
    macPutString 'Writing file name: ', crlf, '$'     ;Input for writing file
    mov al, 128
    mov dx, writingFile
    call procGetStr
    macNewLine
    
    push readingFile
    push writingFile
    call function
    
    
    exit

;Main Function  
function:
    push bp
    mov bp, sp
    sub sp, 4
    push dx
    push bx
    push ax
    
    
    mov dx, [bp+6]
    call procFOpenForReading
    jnc .secondFOpen
    macPutString 'Error while opening file', crlf, '$'
    jmp .end
    
    .secondFOpen:
    mov [bp-2], bx
    
    mov dx, [bp+4]
    call procFCreateOrTruncate
    jnc .filter
    macPutString 'Error while opening writing file', crlf, '$'
    jmp .close
    
    .filter:
    mov [bp-4], bx
    
    .whileNotTheEnd:
    mov bx, [bp-2]
    call procFGetChar
    jnc .c1
    macPutString 'Error while reading file', crlf, '$'
    jmp .close
    
    .c1:
    cmp ax, 0       ; Checks if it is not the end of the file
    jne .check    
    jmp .close      ; If the end - close the file
    
    .check:
    mov al, cl
    cmp al, ';'     ; Checks if ';'
    jne .c2
    
    
    .c2:            
    cmp al, 30h     ; Number checking
    jge .c3
    jmp .c4
    
    .c3:
    cmp al, 39h     ; Number checking
    jle .checkEnd
    jmp .c4
    
    .c4:
    cmp al, 'B'
    jne .c5
    jmp .checkEnd
    
    .c5:
    cmp al, 'C'
    jne .writing
    jmp .checkEnd
    
    .writing:
    mov bx, [bp-4]
    call procFPutChar
    jnc .checkEnd
    macPutString 'Error while writing file', crlf, '$'
    jmp .close
    
    .acheckEnd:
    cmp ax, 0
    jne .nextIteration
    jmp .close
    
    .nextIteration:
    jmp .whileNotTheEnd
    
    
    .close:
    mov bx, [bp-4]
    call procFClose
    .closeReadingFile:
    mov bx, [bp-2]
    call procFClose
    
    .end:
    ret4
    
    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include 'yasmlib.asm'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .data

writingFile:
    times 255 db 00
    
readingFile:
    times 255 db 00
    
duomenys:
    times 255 db 00

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .bss


Solution

  • There's not much about "yasmmac.inc" to be found online, but I believe that my interpretation of what these procFGetChar and procFPutChar macros do will not be too far off...

    The task will seem too complicated if you don't use a modular approach. You need to delegate the simpler subtasks to separate subroutines. Further it would make sense to abstract from where the data is coming from. Don't read a single character from the file and immediately have it classified. No, fetch a complete line from the file and process the requested fields from the memory copy. It will be so much easier and less prone to errors related to file access.

    .whileNotTheEnd:
      ; Fetch one line from the file
      call .fgets       ; -> DI (AX BX CL)
      test di, di
      jz   .close       ; Normal EOF
    
      ; Process fields 1 and 2 from the memory copy
      mov  si, Buffer
      call .field       ; -> SI (AX BX)
      call .field       ; -> SI (AX BX)
      jmp  .whileNotTheEnd
    
    .close:
      mov  bx, [bp-4]
      call procFClose
      mov  bx, [bp-2]
      call procFClose
    

    The lines in the text file each end with a carriage return and linefeed pair (13,10). Finding the 10 therefore denotes the EOL.
    Receiving AX=0 from DOS means the end of the file. If this happens when we have not yet characters in our buffer, it signals a normal EOF.

    ; ----------------------
    ; IN (bp) OUT (di) MOD (ax,bx,cl)
    .fgets:
      mov  bx, [bp-2]   ; Handle
      xor  di, di       ; Counts characters
    .more:
      call procFGetChar ; -> AX CL CF
      jc   .err1
      test ax, ax       ; EOF ?
      jz   .eof
      mov  [Buffer + di], cl
      inc  di
      cmp  cl, 10
      jne  .more
    .ret:
      ret
    .eof:
      test di, di       ; If DI==0 then Normal EOF
      jz   .ret
    .err1:
      macPutString 'Error while reading file', crlf, '$'
      pop  ax           ; (*) Forget about `call fgets`
      jmp  .close
    ; ----------------------
    

    The field subroutine does the magic of this program but even it will delegate to a further subroutine the classification of the character. This will enhance readability. As a rule of thumb, I try to keep each subroutine within the bounds of one screen (25 rows). Because this code will end with SI pointing after the current field, it will be real easy to continue with the 2nd field.

    ; ----------------------
    ; IN (si) OUT (si) MOD (ax,bx)
    .field:
      mov  bx, si       ; Remember the start of this field
    .check:
      lodsb
      cmp  al, ";"
      je   .write
      call .test        ; -> CF
      jnc  .check
    .skip:
      lodsb             ; Skip remainder of this (bad) field
      cmp  al, ";"
      jne  .skip
      ret
    .write:
      push si           ; (1)
      mov  si, bx       ; Send this field to file
      lodsb
    .w1:
      call .fputc       ; -> (AX BX)
      lodsb
      cmp  al, ";"
      jne  .w1
      pop  si           ; (1) SI points after the ";"
      ret
    ; ----------------------
    ; IN (al) OUT (CF)
    .test:
      cmp  al, "0"
      jb   .OK
      cmp  al, "9"
      jbe  .NOK
      cmp  al, "B"
      je   .NOK
      cmp  al, "C"
      je   .NOK
    .OK:
      clc
      ret
    .NOK:
      stc
      ret
    ; ----------------------
    ; IN (al,bp) OUT () MOD (ax,bx)
    .fputc:
      mov  bx, [bp-4]   ; Handle
      call procFPutChar ; -> AX CF
      jc   .err2
      test ax, ax
      jz   .err2
      ret
    .err2:
      macPutString 'Error while writing file', crlf, '$'
      pop  ax           ; (*) Forget about `call .fputc`
      pop  ax           ; (*) Forget about `call .field`
      jmp  .close
    ; ----------------------
    

    (*) The error exits in .fgets and .fputc need to balance the stack! They need "to forget" about the calls that were made before the jmp to .close can pass in all safety.