I'm having this little problem with an ASM assignment I'm working on. It works like a charm... when I use TD. As I'm really new to Assembly, I really can't find what's the problem.
I need to print on screen how many characters of each ASCII code are present on a .txt file (how many a's, how many b's, and so on). I open the file using first the int 21h/3dh to create a handle, then buffering some of the bytes, working on them, and buffering the next part until the end of file is reached. As I said, when I run it using TurboDebugger, it works perfectly. However, when I execute it simply form the command line it doesn't even create the file handle. It sets the CF after the 3dh interruption, which means an error.
I really really think that I'm not doing something I need to do, and I would appreciate help with this. Thanks! BTW I'm using TASM to compile. I don't think posting a piece of my code would help, but if you think otherwise I will gladly modify the post.
This is the whole code, sorry about the comments in Spanish
NLCR macro
mov dl, 0dh ;Caracter a imprimir a dl
mov ah, 02h ;Preparo la impresion de un caracter
int 21h ;Ejecuto
mov dl, 0ah ;Caracter a imprimir a dl
mov ah, 02h ;Preparo la impresion de un caracter
int 21h ;Ejecuto
endm
SData Segment para 'Data'
msgerrorab db "Error al abrir el archivo.$"
msgerrorca db "Error al cargar el archivo.$"
msgnombre db "Ingrese el nombre del archivo a abrir: $"
msgnoexiste db "Error: el archivo no existe.$"
msgimprimir db "========= Caracter ========== Cantidad =======",10,13,"$"
msglinea1 db "=== $"
caracter db 0
msglinea2 db " ========== $"
cuantos dw 0
msglinea3 db "==========",10,13,"$"
msgtecla db "Presione una tecla para mostrar mas...$"
noarchivo db "$$"
nombre db 20 Dup(?)
buffer db 4096 Dup(4)
handle dw 0
caracteres dw 255 Dup(0)
cantidad dw 0
eof db 0
bytesinv db 10 dup("$")
bytes db 10 dup("$")
contador db 0
SData EndS
pila segment stack 'stack'
dw 256 dup (?)
pila ends
CSeg Segment para public 'Code'
Begin:
Assume CS:CSeg, DS:SData,SS:pila
LeeChar Proc ;Procedure para leer un caracter y guardarlo. Lo salva en AL
xor ax,ax ;Limpia el ax para guardar el resultado
mov ah, 08h ;Prepara el servicio para leer un caracter
int 21h ;Lo ejecuta
ret ;Retorna
LeeChar endP
ImpChar Proc ;Similar, pero imprime un caracter
xor ax,ax ;Limpia el ax para guardar el resultado
mov ah,02h ;Prepara el servicio para imprimir un caracter
int 21h ;Lo ejecuta
ret ;Retorna
ImpChar endP
ImpStr Proc ;Similar, pero imprime un caracter
xor ax,ax ;Limpia el ax para guardar el resultado
mov ah,09h ;Prepara el servicio para imprimir un caracter
int 21h ;Lo ejecuta
ret ;Retorna
ImpStr endP
ImpNum Proc ;el numero en ax, el bx en 000A, dx en 0, y el di en 0.
hexadec: ;Con esto se pasa "a decimal", es decir convierte los caracteres a numeros para ser impresos.
div bx ;Divido por 10 y consigo en DL el numero de mas a la derecha
add dx, 30h ;Sumo 48 para pasarlo a caracter. EL MODULO QUEDA EN DX
mov bytesinv[di], dl ;Lo guardo en el vector
xor dx,dx ;Limpio para volverlo a hacer
inc di ;Aumento el indice del vector
cmp ax,0 ;Si ya no queda nada
je resfinal ;Brinco al resultado final
jmp hexadec ;Si todavia queda, hago el proceso de division de nuevo
resfinal: ;Inversion de los caracteres
xor si,si
mov al, bytesinv[di-1] ;Estas dos lineas vuelcan el vector en otro, pero en el orden que corresponde
mov bytes[si],al ;Pasa al nuevo vector los caracteres
dec di ;Decrementa para mover el indice
inc si ;Aumento el indice del vector
cmp di,0 ;Si ya no queda nada
jne resfinal
lea dx, bytes
xor ax,ax
mov ah,09h
int 21h
ret
ImpNum endP
;;;;;;;;;;;;;;;;;;;;;;;
;Comienzo del programa;
;;;;;;;;;;;;;;;;;;;;;;;
inicio: mov ax,SData ;Con estas dos lineas, pasamos la direccion del segmento SData y lo pasamos al ds
mov ds,ax
mov ax,pila
mov ss,ax
leernombre:
xor di,di
xor ax,ax ;Limpio AX
xor dx,dx ;Limpio DX
lea dx, msgnombre ;Cargo el offset del mensaje que pide el nombre
mov ah,09h ;Preparo el servicio de impresion
int 21h ;Ejecuto
xor ax,ax ;Limpio AX
xor dx,dx ;Limpio DX
lea dx, noarchivo ;Carga el offset de donde voy a guardar el nombre del archivo
mov ah,0ah ;Preparo el servicio de entrada buffereada
int 21h ;Ejecuto
nlcr ;New line carriage return
xor ah,ah ;Limpio AH
mov al, noarchivo[1] ;En narchivo[1] esta la cantidad de caracteres que mide el nombre de archivo
mov di,ax ;Lo paso al DI para usarlo como indice
mov noarchivo[di+2],0 ;La posicion di+2 del nombre contiene el Enter, o 0ah. Lo convierto en 0.
mov ah,3dh ;Preparo el servicio de creacion de handle para abrir el archivo
mov al,0 ;En modo de solo lectura
lea dx, nombre ;Cargo el offset del nombre en DX, sin los bytes que sobran antes
int 21h ;Ejecuto. El handle queda en AX
jc errorabrir
mov handle,ax ;Lo paso a su variable correspondiente
jmp cargarbuffer
errorabrir:
lea dx, msgerrorab ;Cargo el offset del mensaje que pide el nombre
mov ah,09h ;Preparo el servicio de impresion
int 21h
jmp final
errorcarga:
lea dx, msgerrorca ;Cargo el offset del mensaje que pide el nombre
mov ah,09h ;Preparo el servicio de impresion
int 21h
jmp final
cargarbuffer:
xor ax,ax ;Limpio AX
xor bx,bx
lea dx, buffer ;Cargo el offset del buffer de texto
mov ah,3fh ;Preparo el servicio para mover el contenido del archivo
mov bx,handle ;Uso el handle que me genero la int 21/3dh
mov cx,10 ;Voy a guardar 4096 caracteres a la vez
int 21h
jc errorcarga
mov cantidad,ax ;Cargo la cantidad de caracteres leidos
cmp cantidad,4096 ;Si es menor a lo que le dije que cargara, llego al end of file
jl endoffile ;
xor si,si
xor ax,ax
mov bx,handle
mov ah,3eh
int 21h
jmp procesarbuffer
endoffile:
mov eof,1
jmp procesarbuffer
procesarbuffer:
xor bx,bx
xor ax,ax
xor di,di
xor dx,dx
mov bl,2
mov al,buffer[si]
mul bl
mov di,ax
inc caracteres[di]
inc si
cmp si,cantidad
jne procesarbuffer
cmp eof,1
je imprimir
jmp cargarbuffer
imprimir:
xor di,di
xor dx,dx
xor ax,ax
xor bx,bx
mov di,0
mov si,0
lea dx, msgimprimir
mov ah,09h
int 21h
linea: cmp caracteres[di],0
je noimprime
inc contador
mov ax,si
mov caracter,al
lea dx, msglinea1
mov ah,09h
int 21h
mov dl,caracter
xor ax,ax
mov ah,02h
int 21h
lea dx, msglinea2
xor ax,ax
mov ah,09h
int 21h
push di
push si
mov ax, caracteres[di]
mov bx,10
xor dx,dx
xor di,di
call ImpNum
pop si
pop di
inc di
inc di
inc si
nlcr
cmp si,254
je final
cmp contador,20
je pidetecla
jmp linea
noimprime:
inc di
inc di
inc si
cmp si,254
je final
jmp linea
pidetecla:
lea dx, msgtecla
xor ax,ax
mov ah,09h
int 21h
xor ax,ax
mov ah,08h
int 21h
nlcr
jmp linea
final:
xor ax,ax ;Limpia el al y prepara el ah para la salida.
mov ax,4c00h ;Servicio AH=4c int 21h para salir del programa.
int 21h ;Llamada al DOS. Termine el programa.
CSeg EndS ;Fin del segmento de código.
End inicio ;Fin del programa la etiqueta al final dice en que punto debe comenzar el programa.
It seems that the error code that the interrupt returns is a 5, which means Access denied... Could it be because of the path file?? The file is located on the same folder that I'm compiling it on, and it's F:\dos\tasm\a.txt . I'm using DosBox, by the way.
I've found a few bugs and fixed them. See the lines containing ;;;;
for the changes and comments. I'm not sure where exactly the problem is/was. It's working here now. Can you try it?
NLCR macro
mov dl, 0dh ;Caracter a imprimir a dl
mov ah, 02h ;Preparo la impresion de un caracter
int 21h ;Ejecuto
mov dl, 0ah ;Caracter a imprimir a dl
mov ah, 02h ;Preparo la impresion de un caracter
int 21h ;Ejecuto
endm
SData Segment para 'Data'
msgerrorab db "Error al abrir el archivo.$"
msgerrorca db "Error al cargar el archivo.$"
msgnombre db "Ingrese el nombre del archivo a abrir: $"
msgnoexiste db "Error: el archivo no existe.$"
msgimprimir db "========= Caracter ========== Cantidad =======",10,13,"$"
msglinea1 db "=== $"
caracter db 0
msglinea2 db " ========== $"
cuantos dw 0
msglinea3 db "==========",10,13,"$"
msgtecla db "Presione una tecla para mostrar mas...$"
noarchivo db buffer - nombre - 1, 0 ;;;; "$$"
;;;; You must initialize the input buffer for function 2 (the max len, at least)
nombre db 20 Dup(?)
buffer db 4096 Dup(4)
handle dw 0
caracteres dw 256 Dup(0) ;;;; 255 Dup(0)
;;;; You have 256 possible chars, not 255, with 255 you can overflow into "cantidad"
cantidad dw 0
eof db 0
bytesinv db 10 dup("$")
bytes db 10 dup("$")
contador db 0
SData EndS
pila segment stack 'stack'
dw 256 dup (?)
pila ends
CSeg Segment para public 'Code'
Begin:
Assume CS:CSeg, DS:SData, SS:pila
LeeChar Proc ;Procedure para leer un caracter y guardarlo. Lo salva en AL
xor ax, ax ;Limpia el ax para guardar el resultado
mov ah, 08h ;Prepara el servicio para leer un caracter
int 21h ;Lo ejecuta
ret ;Retorna
LeeChar endP
ImpChar Proc ;Similar, pero imprime un caracter
xor ax, ax ;Limpia el ax para guardar el resultado
mov ah, 02h ;Prepara el servicio para imprimir un caracter
int 21h ;Lo ejecuta
ret ;Retorna
ImpChar endP
ImpStr Proc ;Similar, pero imprime un caracter
xor ax, ax ;Limpia el ax para guardar el resultado
mov ah, 09h ;Prepara el servicio para imprimir un caracter
int 21h ;Lo ejecuta
ret ;Retorna
ImpStr endP
ImpNum Proc ;el numero en ax, el bx en 000A, dx en 0, y el di en 0.
hexadec: ;Con esto se pasa "a decimal", es decir convierte los caracteres a numeros para ser impresos.
div bx ;Divido por 10 y consigo en DL el numero de mas a la derecha
add dx, 30h ;Sumo 48 para pasarlo a caracter. EL MODULO QUEDA EN DX
mov bytesinv[di], dl ;Lo guardo en el vector
xor dx, dx ;Limpio para volverlo a hacer
inc di ;Aumento el indice del vector
cmp ax, 0 ;Si ya no queda nada
je resfinal ;Brinco al resultado final
jmp hexadec ;Si todavia queda, hago el proceso de division de nuevo
resfinal: ;Inversion de los caracteres
xor si, si
resfinal2: ;;;;
mov al, bytesinv[di-1] ;Estas dos lineas vuelcan el vector en otro, pero en el orden que corresponde
mov bytes[si], al ;Pasa al nuevo vector los caracteres
dec di ;Decrementa para mover el indice
inc si ;Aumento el indice del vector
cmp di, 0 ;Si ya no queda nada
jne resfinal2 ;;;; resfinal
;;;; You don't want to be storing all digits at 'bytes[si=0]', overwriting each other
mov bytes[si], "$" ;;;;
;;;; You must terminate the string with '$' for function 9, you don't want garbage
lea dx, bytes
xor ax, ax
mov ah, 09h
int 21h
ret
ImpNum endP
;;;;;;;;;;;;;;;;;;;;;;;
;Comienzo del programa;
;;;;;;;;;;;;;;;;;;;;;;;
inicio:
mov ax, SData ;Con estas dos lineas, pasamos la direccion del segmento SData y lo pasamos al ds
mov ds, ax
;;;; mov ax, pila
;;;; mov ss, ax
;;;; .EXEs set up SS:SP for you!
leernombre:
xor di, di
xor ax, ax ;Limpio AX
xor dx, dx ;Limpio DX
lea dx, msgnombre ;Cargo el offset del mensaje que pide el nombre
mov ah, 09h ;Preparo el servicio de impresion
int 21h ;Ejecuto
xor ax, ax ;Limpio AX
xor dx, dx ;Limpio DX
lea dx, noarchivo ;Carga el offset de donde voy a guardar el nombre del archivo
mov ah, 0ah ;Preparo el servicio de entrada buffereada
int 21h ;Ejecuto
nlcr ;New line carriage return
xor ah, ah ;Limpio AH
mov al, noarchivo[1] ;En narchivo[1] esta la cantidad de caracteres que mide el nombre de archivo
mov di, ax ;Lo paso al DI para usarlo como indice
mov noarchivo[di+2],0 ;La posicion di+2 del nombre contiene el Enter, o 0ah. Lo convierto en 0.
mov ah, 3dh ;Preparo el servicio de creacion de handle para abrir el archivo
mov al, 0 ;En modo de solo lectura
lea dx, nombre ;Cargo el offset del nombre en DX, sin los bytes que sobran antes
int 21h ;Ejecuto. El handle queda en AX
jc errorabrir
mov handle, ax ;Lo paso a su variable correspondiente
jmp cargarbuffer
errorabrir:
lea dx, msgerrorab ;Cargo el offset del mensaje que pide el nombre
mov ah, 09h ;Preparo el servicio de impresion
int 21h
jmp final
errorcarga:
lea dx, msgerrorca ;Cargo el offset del mensaje que pide el nombre
mov ah, 09h ;Preparo el servicio de impresion
int 21h
jmp final
cargarbuffer:
xor ax, ax ;Limpio AX
xor bx, bx
lea dx, buffer ;Cargo el offset del buffer de texto
mov ah, 3fh ;Preparo el servicio para mover el contenido del archivo
mov bx, handle ;Uso el handle que me genero la int 21/3dh
mov cx, 4096 ;;;; 10 ;Voy a guardar 4096 caracteres a la vez
;;;; You wanted 4096 and implemented logic for 4096, so use 4096.
int 21h
jc errorcarga
mov cantidad, ax ;Cargo la cantidad de caracteres leidos
cmp cantidad, 4096 ;Si es menor a lo que le dije que cargara, llego al end of file
jl endoffile ;
xor si, si
;;;; xor ax, ax
;;;; mov bx, handle
;;;; mov ah, 3eh
;;;; int 21h
;;;; You had the condition for file closing wrong
jmp procesarbuffer
endoffile:
xor si, si ;;;;
xor ax, ax ;;;;
mov bx, handle ;;;;
mov ah, 3eh ;;;;
int 21h ;;;;
;;;; You had the condition for file closing wrong
mov eof, 1
cmp cantidad, 0 ;;;;
je imprimir ;;;;
;;;; The original code at 'procesarbuffer' wouldn't work with 'cantidad'=0
jmp procesarbuffer
procesarbuffer:
xor bx, bx
xor ax, ax
xor di, di
xor dx, dx
mov bl, 2
mov al, buffer[si]
mul bl
mov di, ax
inc caracteres[di]
inc si
cmp si, cantidad
jne procesarbuffer
cmp eof, 1
je imprimir
jmp cargarbuffer
imprimir:
xor di, di
xor dx, dx
xor ax, ax
xor bx, bx
mov di, 0
mov si, 0
lea dx, msgimprimir
mov ah, 09h
int 21h
linea:
cmp caracteres[di], 0
je noimprime
inc contador
mov ax, si
mov caracter, al
lea dx, msglinea1
mov ah, 09h
int 21h
mov dl, caracter
xor ax, ax
mov ah, 02h
int 21h
lea dx, msglinea2
xor ax, ax
mov ah, 09h
int 21h
push di
push si
mov ax, caracteres[di]
mov bx, 10
xor dx, dx
xor di, di
call ImpNum
pop si
pop di
inc di
inc di
inc si
nlcr
cmp si, 254
je final
cmp contador, 20
je pidetecla
jmp linea
noimprime:
inc di
inc di
inc si
cmp si, 254
je final
jmp linea
pidetecla:
mov contador, 0 ;;;;
;;;; You want to wait for a key after each 20 lines, not just after the first 20
lea dx, msgtecla
xor ax, ax
mov ah, 09h
int 21h
xor ax, ax
mov ah, 08h
int 21h
nlcr
jmp linea
final:
xor ax, ax ;Limpia el al y prepara el ah para la salida.
mov ax, 4c00h ;Servicio AH=4c int 21h para salir del programa.
int 21h ;Llamada al DOS. Termine el programa.
CSeg EndS ;Fin del segmento de codigo.
End inicio ;Fin del programa la etiqueta al final dice en que punto debe comenzar el programa.