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

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

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.

Related

Printing an array but specific values highlighted {emu8086}

I want to print this array but also highlight specific characters from said array. This is my code:
include 'emu8086.inc'
.data
animales1 db 'BUBRPQYFODFZXIQ'
db 'MSVDJVQDTLOEATF'
db 'RCZPIFYGAZLPMFN'
db 'LVWKFFBKDXHFIUW'
db 'AOSEFQEMOOTGQUR'
db 'ELLWTGNJJKAJISJ'
db 'OVCOXLUEQTSDDSP'
db 'UEAEMTNYOLVYMGI'
db 'ORREPOMJZGYPHAI'
db 'IFTLCBJFVOYHLUB'
db 'WTOWZQFRAXQRLMR'
db 'KGNYIIHHHKFUKIJ'
db 'XMLSACGMVXEYSIT'
db 'TSOESQVSEQRFNPU'
db 'ODDQMDFWRGETDLY'
lenguajes2 db 'SLKFMBCULKVYUIM'
db 'TWCDQFYIVIKUXKB'
db 'GNIWEQBOSYEMDTJ'
db 'WDHFZZPUIEDERYQ'
db 'KMTGTKAKROMUSUV'
db 'BELBLLTUVJQHCRW'
db 'UPLUBYJKNUXORLF'
db 'SGMAOOEENBOGIWR'
db 'JVSTLPTEGPPNPJW'
db 'YINCASSEMBLYTTU'
db 'CHWTIOIWORDZDRV'
db 'BRZCNRAVRWAMUNU'
db 'KOMCOUKNGQEPQVS'
db 'XXRXJUJUBEXVGGA'
db 'MNKJQKZAACVCLDW'
char db 'a'
flag db 0
;Pez= 12,27,42
;Ave= 76,92,108
indx db 5,11,26,41,45 dup (' ')
.stack
.code
.start up
mov ax,0000h
mov bx,0000h
mov cx,0000h
mov dx,0000h
lea si, animales1
;call valueInArray
call printArrays
printArrays proc
mov bx,0000h
printArray:
mov dx,0000h
mov ah, 02h
mov dl,byte ptr[si+bx]
call valueinArray
cmp flag,1
jz printSpecial
int 21h
mov dl, 20h
int 21h
add bx, 1
add cl, 1
cmp cl,15
jz nextRow
jnz printArray
nextRow:
call nwLine
add ch,1
mov cl, 00h
cmp ch,15
jz finproc
jnz printArray
printSpecial:
lea di,char
mov byte ptr[di],dl
push ax
push bx
push cx
push dx
mov bp, offset char
mov dh,00h
mov al, 1
mov bh, 0
mov bl, 1111_0000b
mov cx, 1; calculate message size.
mov dx,0000h
mov ah, 13h
int 10h
mov dl, 20h
mov ah, 02h
int 21h
pop dx
pop cx
pop bx
pop ax
add bx, 1
add cl, 1
cmp cl,15
jz nextRow
jnz printArray
finproc:
ret
printArrays endp
nwLine PROC
MOV AH,02h
MOV DX,0Ah; imprime asscii 10 (nueva linea)
INT 21h
MOV DX,0Dh; imprime asscii 13 (retorno de carro)
INT 21h
RET
nwLine ENDP
valueInArray proc
mov flag, 0
mov di,0001h
iterar:
mov dh, indx[di]
cmp dh,20h
jz finvalueproc
cmp bl,dh
jz found
add di,1
jmp iterar
found:
mov flag,1
jmp finvalueproc:
finvalueproc:
ret
valueInArray endp
exit:
end
As you can see, the array 'indx' have the positions of the chars (The first number is the effective lenght of said array) that I want to highlight so I check if bx is in the position of said character to print it highlighted.
I have two problems at the moment, the first one is the highlighted characters are printing in a kinda random position and that messes up the rest, but I think I know why the position of the highlighted character that prints is wrong, because the interrupt that I use uses dh and dl as positioners but I do not know how to make it so it continues the flow of the rest. The second problem is that the character that prints is wrong, if you run it and check the variable char, it has an 'Z' but it prints a 'T'. I really need help. Pretty please. :(
indx db 5,11,26,41,45 dup (' ')
It could be that this line is not what you think it is!
Reading your explanation I think you really meant:
indx db 5, 11, 26, 41, 45, ' '
That is the length of the array which is 5, then followed by 4 offsets to the highlighted characters and 1 space character as a list terminator.
the first one is the highlighted characters are printing in a kinda random position and that messes up the rest, but I think I know why the position of the highlighted character that prints is wrong, because the interrupt that I use uses dh and dl as positioners but I do not know how to make it so it continues the flow of the rest.
Because your code to output the highlighted character uses mov dx,0000h, the BIOS function 13h places the character in the upperleft corner of the screen and the other, 'normal' characters will continue from there.
All you have to do is to ask BIOS where the cursor is at that moment using function 03h:
Replace
mov cx, 1; calculate message size.
mov dx,0000h
mov ah, 13h
int 10h
by
mov ah, 03h ; BIOS.GetCursorAndConfiguration
int 10h ; -> CX DX
mov cx, 1 ; message size
mov ah, 13h ; BIOS.WriteString
int 10h
Function 13h fetches from ES:BP. Did you setup the ES segment register?
The second problem is that the character that prints is wrong, if you run it and check the variable char, it has an 'Z' but it prints a 'T'.
Probably resolving the first problem will also resolve the second one.
There's a better, less convoluted way to output your highlighted character.
printSpecial:
push ax
push bx
push cx
mov cx, 1 ; ReplicationCount
mov bx, 00F0h ; DisplayPage 0, Attribute BlackOnBrightWhite (11110000b)
mov al, dl ; Character
mov ah, 09h ; BIOS.WriteCharacterAndAttribute
int 10h
mov ah, 0Eh ; BIOS.TeletypeCharacter
int 10h
pop cx
pop bx
pop ax
...
Function 09h will write your character with the colors that you need, and then function 0Eh will advance the cursor normally.

Read a line from File and pass it to variable, then proceed to next line (Assembly 8086)

this is my first post so I'm sorry in advance if I did it wrongly (in any ways).
So what I am facing now is, I have a a file (name.txt) and I want to pass the first line to buffer1 , second line to buffer2 and so on.
Extra Info :
The maximum line of the file is going to be 10.
The maximum length of the string is 27(data)+1(dollar sign)+2(CR LF) or so I think.
I created 5 buffer for this example
The example of the string is "1washingmachine"
name.txt is already created
.
.model small
.stack 64
.data
filename db "name.txt",0
handler dw ?
strPrompt db 10,13,"enter:$"
strInput label byte
max db 28 ;max name is 20 , + 1 for index , 6 for time , 1 for $
act db ?
data db 28 dup ('$')
buffer1 db 30 dup ('$')
buffer2 db 30 dup ('$')
buffer3 db 30 dup ('$')
buffer4 db 30 dup ('$')
buffer5 db 30 dup ('$')
newLine db 10,13
.code
main proc far
;INITIALIZE DATA SEGMENT.
mov ax,#data
mov ds,ax
;CREATE FILE.
mov ah, 3dh
mov al, 2
lea dx, filename
int 21h
;PRESERVE FILE HANDLER RETURNED.
mov handler, ax
;READ STRING
MOV AH, 3FH ; read file
MOV BX, handler ; copy handle into BX
MOV CX,30 ; set count to read 28+2
lea dx,buffer1
INT 21H ; AX = bytes read
MOV AH, 3FH ; read file
MOV BX, handler ; copy handle into BX
MOV CX,30 ; set count to read 28+2
lea dx,buffer2
INT 21H
MOV AH, 3FH ; read file
MOV BX, handler ; copy handle into BX
MOV CX,30 ; set count to read 28+2
lea dx,buffer3
INT 21H
MOV AH, 3FH ; read file
MOV BX, handler ; copy handle into BX
MOV CX,30 ; set count to read 28+2
lea dx,buffer4
INT 21H
MOV AH, 3FH ; read file
MOV BX, handler ; copy handle into BX
MOV CX,30 ; set count to read 28+2
lea dx,buffer5
INT 21H
mov ah,09h
lea dx,buffer1
int 21h
mov ah,09h
lea dx,buffer2
int 21h
mov ah,09h
lea dx,buffer3
int 21h
mov ah,09h
lea dx,buffer4
int 21h
mov ah,09h
lea dx,buffer5
int 21h
;input string to write in file
mov ah,09h
lea dx,strPrompt
int 21h
mov ah,0ah
lea dx,strInput
int 21h
;WRITE STRING.
mov ah, 40h
mov bx, handler
mov ch,0
mov cl, act ;STRING LENGTH.
inc cx
mov dx, offset data
int 21h
lea dx,newLine
mov bx,handler
mov cx,2
mov ah,40h
int 21h
;CLOSE FILE (OR DATA WILL BE LOST).
mov ah, 3eh
mov bx, handler
int 21h
;FINISH THE PROGRAM.
mov ax,4c00h
int 21h
main endp
end main
Hopefully someone from StackOverflow can help me with this, either guidance or solution will do !

Character counting from big files in assembly

This should be counting every character in file but its working only for smaller files and i need it to work for files containing more than 32768 characters
fn DB strsize DUP ('$');
bsize equ 32768
buff db bsize dup ('$')
read proc
mov ah,3dh ;open
mov al,0 ; read only mode
mov dx,offset fn ; offset of file name
int 21h ; file handle
jc jmpchyba
ret
endp
BITY proc
mov full,00
mov actual,00
jmpback:
call read
mov actual,00
mov bx,ax;offset buff
mov ah,3fh
mov cx,bsize ; count bytes
mov dx,offset buff
int 21h ;
mov actual,ax
mov bx,actual
add full,bx
cmp actual,bsize
je jmpback
vypis kolko
mov ax,full
call convasci;this is working convert from binary to asci
ret
endp
Firstly define full as a dword. Then use this code
mov actual,ax
mov bx,actual
add full,bx
adc full+2,0
Obviously the conversion routine convasci should also be changed to deal with a dword.
The proc read actually opens the file! You should certainly not keep calling it from the BITY proc.
Rename read as open and place call open before the label jmpback:
Open proc
mov ah,3dh ;open
mov al,0 ; read only mode
mov dx,offset fn ; offset of file name
int 21h ; file handle
jc jmpchyba
ret
endp
BITY proc
mov full,0
mov full+2,0
call Open
mov bx,ax ;Handle!!!
jmpback:
mov actual,00
mov ah,3fh
mov cx,bsize ; count bytes
mov dx,offset buff
int 21h ;
mov actual,ax
add full,ax
adc full+2,0
cmp ax,bsize
je jmpback

Assembler read full numbers from txt file

I am stuck at point where I need to read 3 numbers from created buffer of text file. I've tryed reading every byte, but no luck if the number is bigger than 9.
Steps that I do:
Open text file
Read file's content into created buffer
Put every buffers byte into different register (ax, bx, cx, dx).
The problem is, it reads 1 byte (mov ax, buffer[0]) at a time: if my text file is (10 10 1), it reads 1 then 0 then space symbol(ascii 20) and so on. Should I do cycle that converts and adds every byte to one register while it doesn't detect space symbol? Or is there a possibility to read whole number at one time? Here's the code:
.model small
bufferLen equ 16
.stack 100h
.data
duom db "duom.txt", 0
fident dw 0
buffer db bufferLen dup (?)
.code
start:
mov dx, #data
mov ds, dx
mov bx, 81h
tikrinam: ; not important
mov ax, es:[bx]
inc bx
cmp al, 13
je openf
cmp al, 20h
je tikrinam
cmp ax, "?/"
jne openf
mov ax, es:[bx]
cmp ah, 13
je abouthlp
jmp openf
abouthlp:
mov dx, offset about
mov ah, 09h
int 21h
jmp ending
openf:
mov ah, 3Dh
mov al, 0
mov dx, offset duom
int 21h
mov [fident], ax
readf:
mov ah, 3Fh
mov bx, [fident]
mov cx, bufferLen
mov dx, offset buffer
int 21h
mov al, buffer[0]
mov bl, buffer[1]
mov cl, buffer[2]
I've found the solution if anyone is having same problem:
changeNumbers:
push ax
mov ax, 0
cmp cl, 0
je change
temp1:
mov ch, 0
mov cl, buffer[si]
inc si
cmp cl, 32
je changeNumbers
cmp cl, 0
je changeNumbers
sub cl, 48
mul abc
add ax, cx
jmp temp1
Basically what I did was read every byte and if the number is >9 then add cx to ax and multiply it by 10. Then just push it to stack for further usage. Brain is an amazing thing, I'd say.

Why do I get open error when reading from file in assembly?

I am trying to read a string from a file and print it on the screen, but I always get an error at opening the file. Why is this happening? What is wrong with the code or with the file? PS: the file is in the same folder as the .asm file.
ASSUME cs: code, ds:data
data SEGMENT
inputFile db "D:\AC\input.txt", 0
openingError db 'An error has occured at opening the file!$'
readingError db 'An error has occured at reading the file!$'
s1 db 10 dup(?)
ls db 0
handle dw ?
data ENDS
code SEGMENT
start:
mov ax,data
mov ds,ax
;open the file
mov ah, 3dh
mov al, 0
mov dx, offset inputFile
int 21h
mov handle, ax
jc openError
;read 10 bytes from the file into s1
mov ah, 3fh
mov bx, handle
mov cx, 10
mov dx, offset s1
int 21h
jc readError
openError:
mov ah, 09h
mov dx, offset openingError
int 21h
jmp the_end
readError:
mov ah, 09h
mov dx, offset readingError
int 21h
jmp the_end
;close file
mov ah, 3eh
mov bx, handle
int 21h
;print string on the screen
lea dx, s1
mov ah, 09h
int 21h
the_end:
mov ax,4C00h
int 21h
code ENDS
END start
Did youi forget to actually open the file? Where is the int 21?
mov ah, 3dh
mov al, 0
lea dx, inputFile
int 21h
jc openingError
mov handle, ax

Resources