NewAtC
NewAtC

Reputation: 55

How to scan symbols from file with 16-bit DOS calls

I have a task to calculate how many characters are in a .txt file whose name the user enters and edit characters if needed. I am new at Assembly x86 so I need some help with file reading and symbols reading in a file.

As my code below shows I use int 21,3d to open the file and int 21,3f to read the file. But I don't understand how to read symbols from file correctly, because if I have 100 random symbols in my txt file, how to read one by one and count them all?

My code:

.data
fname_input db 255,?,255 dup("$")
buff db 255,?,255 dup("$")
endl     db 13,10,"$"
handle dw ?

.code
start:

    mov dx, @data
    mov ds, dx

    mov ah, 0Ah
    mov dx, offset fname_input ;input put in to buffer
    int 21h
    
    mov ah, 3fh 
    mov al, 00 ;only read
    mov dx, offset fname_input ; name of the file to open
    int 21h
    
    mov ah,3fh 
    mov bx,[handle]
    mov cx,4             
    mov dx,offset buff
    int 21h
    
    mov ax, 4c00h ;exit
    int 21h 

end start

Upvotes: 1

Views: 330

Answers (1)

Sep Roland
Sep Roland

Reputation: 39341

Corrections to the code.

mov ah, 3fh 
mov al, 00 ;only read
mov dx, offset fname_input ; name of the file to open
int 21h

mov ah,3fh 
mov bx,[handle]
mov cx,4             
mov dx,offset buff
int 21h
  • It's maybe a typo, but the DOS.OpenFile function is 3Dh (so not 3fh)
  • The filename is not at the address of offset fname_input. That's where you defined the input structure for the DOS.BufferedInput function 0Ah.
    The actual filename starts 2 bytes higher up in memory, and for now is terminated by the code 13. You must change this code to 0 before you can present this to the DOS.OpenFile function.
  • You must never omit checking for any errors reported by DOS!
  • Your DOS.ReadFile function 3Fh uses the handle variable even before you initialized it!

Way to solve the task

The simplest(1) solution will read the file one byte at a time, until the read function reports it could not fulfil the request. That will happen at file's end.
For every byte you receive, you can increment a counter for establishing the file length, and if you find that the byte needs changing, then you can set the file pointer one position back and write the new character code to the file. Because you not only need read access to the file, you'll have to ask DOS for read/write access when you open the file.

    mov  si, offset TheBuffer
    mov  word ptr [si], 0050h    ; Set both lengths for DOS.BufferedInput
    mov  dx, si
    mov  ah, 0Ah                 ; DOS.BufferedInput
    int  21h
    xor  bx, bx
    mov  bl, [si + 1]            ; Length of the filename
    mov  [si + 2 + bx], bh       ; Changing carriage return 13 into zero-terminator 0

    lea  dx, [si + 2]            ; ASCIIZ Filename
    mov  ax, 3D02h               ; DOS.OpenFile for read/write
    int  21h                     ; -> AX CF
    jc   ERROR
    mov  [handle], ax

MainLoop:
    mov  dx, offset TheBuffer
    mov  cx, 1
    mov  bx, [handle]
    mov  ah, 3Fh                 ; DOS.ReadFile
    int  21h                     ; -> AX CF
    jc   ERROR
    cmp  ax, cx
    jb   EOF

    ...

    jmp  MainLoop

EOF:
    mov  bx, [handle]
    mov  ah, 3Eh                 ; DOS.CloseFile
    int  21h                     ; -> AX CF
    
    mov  ax, 4C00h               ; DOS.Terminate
    int  21h 

TheBuffer db 512 dup (0)

At the ellipsis in the above code snippet, you can do anything you need to do with that one byte that you received.
In order to set the filepointer one position back so you can update the file with the new character that you prepared in TheBuffer, you need to use the DOS.MoveFilepointer function 42h. Use it with a 32-bit offset of -1 in CX:DX.

    mov  dx, -1
    mov  cx, -1
    mov  bx, [handle]
    mov  ax, 4201h               ; DOS.MoveFilepointer from current position
    int  21h                     ; -> DX:AX CF
    jc   ERROR

    mov  dx, offset TheBuffer
    mov  cx, 1
    mov  bx, [handle]
    mov  ah, 40h                 ; DOS.WriteFile
    int  21h                     ; -> AX CF
    jc   ERROR

(1) A solution that reads more than 1 byte at a time will be more efficient, albeit somewhat more involved. In such case defining a buffer of 512 bytes is best. It nicely matches the disk sector size and the buffers that DOS maintains.

Upvotes: 1

Related Questions