[SOLUCIONADO] Programa que cuenta caracteres de cadena de texto (Ayuda)

  • 3 Respuestas
  • 708 Vistas

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado mythik_751

  • *
  • Underc0der
  • Mensajes: 4
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
Hola estoy teniendo problemas  la la hora de realizar este programa, se supone que el programa debe de contar los caracteres que escribes y después imprimirlos diciendo cuantas veces aparece tal carácter, por ejemplo
cadena: Anita lava la tina
A = 1
a = 5
n = 2
i = 2
t = 2
 
y así sucesivamente

alguien que sepa  que me falla? o que me pueda ayudar con esto,  seria de mucha ayuda...

Esto es lo que tengo:
___________________________

Código: (asm) [Seleccionar]
data segment
    ; add your data here!
    cadena  db  258 dup(0) ; para definir bits
    resultados db 00h
    letra db 00h
    numero db 00h
   
ends

stack segment
    dw   258  dup(0)
ends

code segment
start:
; set segment registers:
            mov ax, data
            mov ds, ax
            mov es, ax   
   
            mov cadena[00], 0ffh   
            lea dx, cadena     
            mov ah, 0ah 
            int 21h 
   
            mov si, 02h
            dec ah             
 inicio:    mov al, cadena[si]
            mov dl, ax
            inc resultados[dl]
            inc si
            cmp al, 0dh
            jz  imprimir
            jmp inicio
         
imprimir:   mov ah, 09h
            mov dx, resultados
            int 21h
            mov bx, si
            mov letra[0], bl
           
            lea dx, letra
            int 21h
            mov al, resultados[si]
            mov ah, 00h
            mov bl, 64h
            div bl
            add al, 30h
            mov numero[0], al
            div bl
           
            add ah, 30h
            mov numero[2], ah
            add al, 30h
            mov numero[1], al
            lea dx, numero
            mov ah, 09h
            int 21h
           
            div bl
            add ah, 30h
            mov numero[2], ah
            add al,
           
ciclo:    cmp si, 080h
            jz  fin
            cmp si, 0dh
            jz  siguiente
            mov al, resultado[si]
            cmp al, 00h
            jz  siguiente
            mov ah, 09h
            lea dx, l3
   
siguiente:  inc si
                jmp ciclo
           
           
   fin:     lea dx, pkey
            mov ah, 9
            int 21h       
   
            mov ah, 01h
            int 21h
   
            mov ax, 4c00h ; exit to operating system.
            int 21h   
ends

end start ; set entry point and stop the assembler.
« Última modificación: Febrero 27, 2021, 10:41:10 pm por DtxdF »

Desconectado DtxdF

  • *
  • Moderator
  • Mensajes: 937
  • Actividad:
    86.67%
  • Country: 00
  • Reputación 19
  • Eres un auto y tienes dos opciones: Parar o Seguir
    • Ver Perfil
    • Mi repositorio de Github donde encontraras herramientas para tu trabajo.
    • Email
Hola @mythik_751

Observando un poco su código, se puede ver que le faltan operandos a algunas instrucciones y otras vienen siendo inválidas porque el ensamblador no tendrá manera de saber el tamaño con los que va a interactuar. Aquí le escribí un pequeño programa que hace lo que está buscando: contar los caracteres, pero tiene algunas rutinas para facilitar la lectura. Espero le ayude.

Código: (asm) [Seleccionar]
bits 16

; El tamaño de la pila (valor arbitrario)
%define STACK_SIZE 0x256

; Para poder salir de la entrada
%define KEY_END 0xd

; Misceláneas
%define TRUE  1
%define FALSE 0

segment code
..start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, stacktop

push prompt
call writeString
   add sp, 2

; offset
mov si, characters
.L1:
mov al, 0x01
int 0x21
movzx bx, al ; Necesitamos a bx o di para usar el desplazamiento
inc byte [si+bx]
cmp al, KEY_END
jne .L1

xor bx, bx
mov cx, characters_size
.L2:
movzx dx, byte [si+bx] ; Las veces que fue escrito
test dl, dl
jz .L5 ; No hace falta imprimirlo si nunca fue escrito
push bx
call isPrint
add sp, 2
test al, al
jz .L3
; Es imprimible, por lo que e imprime normalmente
push bx
call writeChar
add sp, 2
jmp .L4
.L3:
; No es imprimible, por lo que para mostrarse en pantalla
; se hace lo siguiente:
push '\\' ; Por convención, no es necesario
call writeChar
add sp, 2
push bx ; Se imprime su número ASCII, es vez del carácter
call writeDec
add sp, 2
.L4:
push equal_str
call writeString
add sp, 2
push dx
call writeDec
add sp, 2
push nueva_linea
call writeString
add sp, 2
.L5:
inc bx
loop .L2

mov ax, 0x4c00
int 0x21

isPrint:
push bp
mov bp, sp
mov al, [bp+4]
cmp al, 0o40
jb .L2
cmp al, 0o176
ja .L2
mov al, TRUE ; Es imprimible
jmp .L3
.L2:
xor al, al ; No es imprimible
.L3:
pop bp
ret

writeString:
push bp
mov bp, sp
push ax
push dx
mov ah, 0x09
mov dx, [bp+4]
int 0x21
pop dx
pop ax
pop bp
ret

writeChar:
push bp
mov bp, sp
pusha

mov ah, 0x02
mov dl, [bp+4]
int 0x21

popa
pop bp
ret

writeDec:
push bp
mov bp, sp
; Reservamos 3 palabras (1 bytes sin usar)
; ya que por lógica solo se puede pasar en
; el argumento 1 del procedimiento writeDec
; 5 cifras, pero es para que quede par.
sub sp, 6
pusha

mov ax, [bp+4] ; número a imprimir
xor si, si
lea di, [bp-6]
mov bx, 10
.L1:
xor dx, dx
div bx
or dl, 0x30 ; Lo convertimos a un carácter imprimible
mov [di], dl
inc si
inc di
test ax, ax
jnz .L1

mov cx, si
dec di
.L2:
movzx dx, byte [di]
push dx
call writeChar
add sp, 2
dec di
loop .L2

popa
mov sp, bp
pop bp
ret

segment data
characters: times 255 db 0 ; Sólo el número de caracteres ASCII
characters_size EQU $-characters
prompt: db "Escriba su cadena: $"
equal_str: db " = $"
nueva_linea: db 0xd,0xa,'$'

segment stack stack
resb STACK_SIZE
stacktop:

Espero no sea molestia, pero tuve que usar NASM porque en este momento no puedo usar TASM/MASM, pero no es tan complicado hacer la equivalencia y comprender cada cosa.

~ DtxdF
« Última modificación: Febrero 23, 2021, 02:06:50 am por DtxdF »
Los seres humanos son robots, cuyo combustible es el afanado dinero.

Desconectado mythik_751

  • *
  • Underc0der
  • Mensajes: 4
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
rayos carnal, no se que paso pero se borro la continuidad del chat, que me habías contestado, pero lo intente y no hubo resultado ahorita ya acomode mi código de tal manera que me lo ejecuta pero sigo teniendo problema a la hora que imprima el resultado de los caracteres mira así es como lo tengo
______________


Código: (asm) [Seleccionar]
data segment
    ; add your data here!   
    l1          db  "Escribe algo en pantalla:  $"
    l2          db  "El resultado de caracteres leidos es:  $"
    cadena      db  258 dup(0) ; defino el maximo de bits
    contador    db  130 dup(0)
    letra       db  00h
    numero      db  00h
    salto       db  0ah, 0dh, 24h
    pkey        db  "fin ..$"
   
ends

stack segment
    dw   128  dup(0)
ends

code segment
start:
; set segment registers:
            mov     ax, data
            mov     ds, ax
            mov     es, ax   
           
            lea     dx, l1
            mov     ah, 09h          ; Aqui se lee el letrero 1
            int     21h
            lea     dx, salto
            mov     ah, 09h          ; Ponemos un salto de linea
            int     21h
   
            mov     cadena[0], 0FFh    ; Especificamos el total de caracteres
            lea     dx, cadena         ; que queremos leer
            mov     ah, 0ah 
            int     21h
            lea     dx, salto
            mov     ah, 09h          ; Ponemos un salto de linea
            int     21h   
   
            mov     si, 02h           
            dec     ah             
inicio:     mov     al, cadena[si]
            mov     di, dx
           
            inc     contador[di]
            inc     si
            cmp     al, 0dh
            jz      imprimir
            jmp     inicio
                         
imprimir:   lea     dx, l2       
            mov     ah, 09h          ; Leemos el letrero 2
            int     21h
            lea     dx, salto
            mov     ah, 09h          ; Ponemos un salto de linea
            int     21h   
               
            lea     dx, contador[si]
            mov     ah, 09h
            int     21h

           
ciclo:      cmp     si, 080h
            jz      fin
            cmp     si, 0dh
            jz      siguiente
            mov     al, contador[si]
            cmp     al, 00h
            jz      siguiente
            mov     ah, 09h
            lea     dx, l1
            int     21h
            mov     bx, si
            mov     letra[0], bl
            lea     dx, letra
            int     21h
            mov     al, contador[si]
            mov     ah, 00h
            mov     bl, 64h
            div     bl
            add     al, 30h
            mov     numero[0], al
            mov     al, ah
            mov     ah, 00h
            mov     bl, 0ah
            div     bl
            add     ah, 30h
            mov     numero[2], ah
            add     al, 30h
            mov     numero[1], al
            lea     dx, numero
            mov     ah, 09h
            int     21h
               
             
siguiente:  inc     si
            jmp     ciclo
           
           
fin:        lea     dx, pkey
            mov     ah, 09h
            int     21h       
   
            mov     ah, 01h
            int     21h
   
            mov     ax, 4c00h ; exit to operating system.
            int     21h   
ends

end start ; set entry point and stop the assembler.
« Última modificación: Febrero 24, 2021, 03:59:49 pm por DtxdF »

Desconectado DtxdF

  • *
  • Moderator
  • Mensajes: 937
  • Actividad:
    86.67%
  • Country: 00
  • Reputación 19
  • Eres un auto y tienes dos opciones: Parar o Seguir
    • Ver Perfil
    • Mi repositorio de Github donde encontraras herramientas para tu trabajo.
    • Email
@mythik_751

Fue un problema del foro, pero ya está todo bien. Si necesita el mensaje, se lo puedo parafrasear según lo que recuerde, no obstante, no creo que sea necesario para este caso, ya que lo que le estaba contestando era una aclaratoria sobre el uso del operador OFFSET en masm para una instrucción del snippet que había dejado en su anterior comentario (el que fue borrado).

Ahora, nos direccionamos a su código y vemos algunas incongruencias. Según tengo entendido, desea que el programa reciba una cadena como entrada y que se muestre como la salida la cantidad total de caracteres ingresados (con sus respectivas repeticiones numéricas). Volviendo al ejemplo que dejó en el primer comentario, si el usuario escribiera Anita lava la tina debería obtener entonces:


Aclarando que este es el programa ensamblado con NASM que le dejé, aquí la explicación abstracta: El programa lo que hace es declarar e inicializar un arreglo en bytes puestos a 0, podemos llamarle a éste el contador de todos los caracteres ASCII, siendo 255 en este caso. Entonces lo que se necesitaría ahora son dos cosas: el desplazamiento del contador y un índice; éste último es sencillo: el índice es el mismo carácter ASCII pero en decimal (octal, hexadecimal, etc., lo importante es que sea un número no más grande que el tamaño del arreglo characters). Ahora usaremos el desplazamiento más el índice para poder saber dónde se deberá sumar 1 en el arreglo, indicando que se ha escrito ese carácter.

Código: (asm) [Seleccionar]
characters: times 255 db 0 ; El contador de caracteres
Ahora basándome en la explicación, el carácter ASCII, o sea el índice, lo escribirá el mismo usuario con la función 0x01 de la interrupción 0x21:

Código: (asm) [Seleccionar]
.L1:
        mov al, 0x01
        int 0x21
        movzx bx, al
        inc byte [si+bx]
        cmp al, KEY_END
        jne .L1

Este es un extracto del programa. El registro al vendría siendo el índice, pero necesitamos a bx para poder indexarlo con el desplazamiento que se encuentra en el registro si, aunque en realidad solo necesitamos ajustar la parte baja, y la alta la ajustamos con ceros con la instrucción movzx. Una vez hemos hecho toda esta operación, incrementamos según el resultado de [si+bx] aclarándole a NASM que sólo necesitamos un byte. Luego se comprueba que no se ha tecleado el carácter para salir, si fue así, se sale y continúa imprimiendo el resultado.

Nota: Si no mal recuerdo, emu8086 no emula la instrucción movzx, que, según tengo entendido fue agregada en el 80386[1][2]

Ya para ultimar la pequeña explicación del programa, lo único que sigue es recorrer todo el arreglo de caracteres. por lo que seguiremos usando el desplazamiento y a la misma vez el índice, que protagonizará dos actuaciones: la primera ser el carácter ASCII que vamos a imprimir, y la segunda, ser el desplazamiento del arreglo, que a su vez nos dirá cuantas veces se ha repetido ese carácter. Por obvias razones si el número de repeticiones es cero, no se imprime (aunque esto dependerá de lo que se desea hacer, en este caso, se deja tal cual). Además para agregar un extra, se verifica si el carácter es o no imprimible; si lo es, se imprime normalmente, si no, se imprime el carácter ASCII en decimal pero como prefijo una barra diagonal invertida.

Además, el programa no guarda la cadena porque no es necesario, así se le deja en libertad al usuario de teclear en demasía una cadena de caracteres.

Si desea, también puede estudiar este programa en C que hace más o menos lo mismo:

Código: (c) [Seleccionar]
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define CHARACTERS_SIZE 256

char characters[CHARACTERS_SIZE];

int main(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF)
if (c < CHARACTERS_SIZE)
characters[c] += 1;

for (int i = 0; i < CHARACTERS_SIZE; i++)
if (characters[i] != 0)
if (isprint(i))
printf("%c = %d\n", i, characters[i]);
else
printf("\\%d = %d\n", i, characters[i]);

return EXIT_SUCCESS;

}

Sobre las incongruencias que le digo, es que no veo que trate usted de imprimir en su programa el carácter que se escribió y entre otras cosas que pienso deberían pasar, como también escribir el carácter de igualdad (=) y el número de repeticiones, que según creo, lo quería hacer en este bucle:

Código: (asm) [Seleccionar]
ciclo:      cmp     si, 080h
            jz      fin
            cmp     si, 0dh
            jz      siguiente
            mov     al, contador[si]
            cmp     al, 00h
            jz      siguiente
            mov     ah, 09h
            lea     dx, l1
            int     21h
            mov     bx, si
            mov     letra[0], bl
            lea     dx, letra
            int     21h
            mov     al, contador[si]
            mov     ah, 00h
            mov     bl, 64h
            div     bl
            add     al, 30h
            mov     numero[0], al
            mov     al, ah
            mov     ah, 00h
            mov     bl, 0ah
            div     bl
            add     ah, 30h
            mov     numero[2], ah
            add     al, 30h
            mov     numero[1], al
            lea     dx, numero
            mov     ah, 09h
            int     21h
               
             
siguiente:  inc     si
            jmp     ciclo

Además, tenga cuidado con esto:

Código: (asm) [Seleccionar]
            mov     numero[0], al
            mov     al, ah
            mov     ah, 00h
            mov     bl, 0ah
            div     bl
            add     ah, 30h
            mov     numero[2], ah
            add     al, 30h
            mov     numero[1], al

Sabiendo que después de numero le sigue salto, se sobreescribirán los datos y si desea después usarla para imprimir la nueva línea, habrá incongruencias.

Además también veo en ese mismo bucle que imprime nuevamente la cadena que se encuentra en l1 por lo que me deja aún más con dudas, que serían gratas que las aclarara, aunque es lo de menos, lo importante es que entienda los pequeños ejemplos y trate de recrearlo según lo que haya entendido, algo así como aprender un nuevo concepto y tratar de explicarlo parafraseandolo.

Espero le haya sido útil.

Referencias:

La instrucción movzx según tengo entendido fue agregada en el 80386:

1,- http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html
2.- https://www.nasm.us/doc/nasmdocb.html

~ DtxdF
Los seres humanos son robots, cuyo combustible es el afanado dinero.