Reverse word order in text string in assembler

Question:

I have a problem with my assembly language project. I was asked to make a program where a string (message) was requested and after entering it, it would show me the same in reverse (the last word in the string will be the first, without modifying the word).

For example, I have the following string: "Hola Mundo Ensamblador" to which it should print as the result: "Ensamblador Mundo Hola"

I have put this question before, but apparently it had not been clear and the code was already from another place (it was provided to me by the teacher as a reference), so I upload another code with which I have tried but it still does not work for me and honestly I have stopped since I do not know what else I could do to it, I am just learning assembler, I would appreciate a lot if someone could help me by explaining the errors that I have.

I am doing it in the "GUI Turbo Assembler" program .

PS: I'm new in these parts, sorry for my mistakes.

Here is the (new) code I have so far:

.model small
.stack 
.data
 cadena db "i$"    
 cadena2 db " "  
terminador db "$"
Espacio db " "
ap1 dw 0 ;apuntador de inicio de palabras 
ap2 dw 0 ;apuntador de inicio de palabras
ap3 dw 0 ;apuntador de inicio de palabras
banfin dw 0
.code
.startup

mov dx, @data
mov ds, dx

reccad:;recorre cadena 
mov si, ap1
cmp cadena[si], "$"
je iniciar
inc ap1
loop reccad

iniciar:;decrementa la cadena[si]
dec ap1

recpal:;recorre palabra 
mov si, ap1
mov al, cadena[si]
cmp si, 0
je finpalabra
cmp al, " "
je guardarinicio
dec ap1
loop recpal

guardarInicio:;guarda 
inc ap1
mov bx, ap1
mov ap3, bx


mov al, cadena[si]
cmp al, "$"
je impEspacio
cmp al, " "
je impEspacio
mov cadena2[di], al
guardarPalabra:
inc ap1
inc ap2
loop guardarPalabra

guardarPalabra: ;en este bloque fue donde tube problemas 
inc ap2
mov bx,ap2
mov ap3,bx

impespacio:
mov al, Espacio
mov cadena2[di],al
inc ap2

apuntador:
dec ap3
dec ap3

mov ax,ap3
mov ap1, ax

cmp banfin,1
je salir

jmp recpal

finpalabra:
mov banfin, 1
jmp guardarPalabra

salir:
lea dx, cadena2
mov ah, 09h
int 21h

.exit
end

I hope you can help me.

Answer:

Good. I apologize that my solution is not a correction of yours:

"I would really appreciate if someone could help me by explaining the mistakes I have."

But it is a solution to your word problem.

This is the solution, which I will try to explain as best as possible (excuse the accents, but the ide does not support them):

.model small
.stack 
.data
    instr1      DB 'Ingrese el texto a invertir:$'
    texto1      DB 255 dup(?)       ; Cadena original ingresada por usuario
    largoTexto  DW 0                ; Cantidad de caracteres que se ingresan

.code
    .startup
; ----------------------------------
; inicializar el programa
; ----------------------------------
    mov     AX, @data               ; Cargar el puntero de .data al registro Acumulador
    mov     DS, AX                  ; Mover el puntero al registro de segmento

; ----------------------------------
; Mostrar el string de instruccion
; ----------------------------------
    mov     DX, offset instr1       ; Cargar el puntero al string en DX (es necesario DS tambien)
    mov     AH, 09h                 ; Preparar el servicio 09h (poner en AH): Display string (ver wiki)
    int     21h                     ; Usar la interrupcion para mostrar el string

; ----------------------------------
; Leer la entrada del usuario (no protegido contra desbordamiento de buffer)
; ----------------------------------
    mov     DI, offset texto1       ; mover el puntero a la cadena al DESTINATION INDEX [DI]
    mov     CX, DI                  ; copiar el puntero a CX para luego obtener el largo de la cadena

    le_leer:
    mov     AH, 01h                 ; servicio 1h Read Character input
    int     21h                     ; interrupcion para leer un caracter (el caracter queda en AL)
    cmp     AL, 13                  ; comparar con retorno de carro
    je      le_terminar             ; Si e retorno de carro, entonces terminar la lectura
    mov     [DI], AL                ; Si no es retorno, mover el caracter al buffer texto1
    inc     DI                      ; Incrementar el puntero de texto1
    jmp     le_leer                 ; volver al inicio del ciclo

    le_terminar:
    mov     [DI], "$"               ; Terminar el string con "$" ($ en ciertas impl. de asm, en c y c++ suele ser el x00)
                                    ; aunque he visto tambien { int lenght , char[] string } sobre todo en archivos
    mov     AX, DI                  ; Mover la ultima posicion del puntero a AX
    sub     AX, CX                  ; Calcular los caracteres leidos y ponerlos en AX
    mov     largoTexto, AX          ; Mover el largo calculado a la variable predestinada para ello

; ----------------------------------
; Invertir toda la linea
; ----------------------------------
    mov     SI, offset texto1       ; Poner el puntero a texto1 en Source Index (este sera el extremo izquierdo)
    mov     DI, offset texto1       ; lo mismo para DI (este sera el extremo derecho)
    add     DI, largoTexto          ; Sumar a DI el largo de la cadena, apuntar al final
    dec     DI                      ; Restar 1 para la conversion de largo a indice(ej: "hola", largo=4, ultimo indice=3)

    il_inicio:
    cmp     SI, DI                  ; Compara SI (desde la derecha) con DI (desde la izquierda)
    jge     il_fin                  ; Si SI es mayor que DI (o sea estan en el centro) terminar el ciclo
    mov     AL, byte ptr[SI]        ; Copia el caracter (byte) de la mitad izquierda al registro AL (byte)
    xchg    AL, byte ptr[DI]        ; Intercambia el contenido de AL con el de DI (EXCHANGE), SI queda en DI, DI en AL
    xchg    AL, byte ptr[SI]        ; Intercambia nuevamente con AL para poner lo que habia en DI en SI
    inc     SI                      ; Aumenta SI
    dec     DI                      ; Disminuye DI (la idea es que se encuentren en el centro de la cadena)
    jmp     il_inicio               ; Repetir el ciclo

    il_fin:                         ; Etiqueta para terminar el ciclo

; ----------------------------------
; Invertir Palabras
; ----------------------------------
    mov     CX, offset texto1       ; Poner el puntero a texto1 en CX (sera nuestro contador)
    mov     BX, offset texto1
    mov     DX, offset texto1       ; En DX ira el puntero al fin de la cadena
    add     DX, largoTexto          ; Sumar el largo para apuntar al final

    ip_for:                         
    cmp     CX, DX                  ; Comparacion que determina si se recorre toda la cadena
    jge     ip_forend               ; Si CX es mayor o igual que DX sale del ciclo
    inc     CX                      ; Actualiza CX; CX++

    ip_if1:                         ; Cuando el caracter actual es espacio y no se esta en la primera posicion
    mov     DI, CX                  ; ** no se como cargar el byte al que apunta CX al registro AL... por lo que lo cargo 
    mov     AL, byte ptr[DI]        ; a DI y luego uso byte ptr[DI] para acceder a el y hacer la comparacion **
    cmp     AL, 32                  ; Compara la posicion actual con espacio " " para determinar que hay una palabra nueva  
    jne     ip_if2                  ; Salta a if2 si el caracter actual no es espacio
    cmp     CX, offset texto1       ; Compara si el contador actual es cero. CX == texto1[0]
    je      ip_if2                  ; Salta a if2 
    mov     SI, BX                  ; Mueve BX a SI, BX mantiene el puntero al inicio de la palabra actual
    mov     DI, CX                  ; Mueve CX a DI, CX mantiene el puntero al fin de la parabra actual
    dec     DI                      ; Disminuye DI para no considerar el espacio de la posicion actual y apuntar a la ultima letra.
    mov     BX, CX                  ; Actualiza BX para apuntar a la siguiente palabra
    inc     BX                      ; Suma 1 a BX por que la posicion actual es un " ".
    jmp     ip_while                ; Salta a l ciclo que invierte la palabra

    ip_if2:                         ; Esta condicion se cumple cuando el puntero esta en el final de la cadena (ultimo caracter)
    mov     AX, DX                  ; Mueve DX (puntero al fin de la palabra) a AX
    dec     AX                      ; Disminuye AX para apuntar a la ultima letra. (antes de esto apuntaba al "$" de cierre)
    cmp     CX, AX                  ; Compara si CX esta en el final de la cadena
    jne     ip_for                  ; Si CX no es igual a text1.length - 1  salta a la siguiente iteracion del ciclo
    mov     SI, BX                  ; Si no Mueve BX a SI. (el inicio de la palabra)
    mov     DI, CX                  ; Mueve CX a DI. DI apunta al ultimo caracter de la cadena

    ip_while:                       ; No hace falta comentar esto, ya que es lo mismo que en "invertir letras"
    cmp     SI, DI 
    jge     ip_for
    mov     AL, byte ptr[SI]
    xchg    AL, byte ptr[DI]
    xchg    AL, byte ptr[SI]
    inc     SI
    dec     DI
    jmp     ip_while

    ip_forend:

; ----------------------------------
; Mostrar el string invertido
; ----------------------------------
    mov     DX, offset texto1       ; Cargar el puntero al string en DX
    mov     AH, 09h                 ; Preparar el servicio 09h (poner en AH): Display string (ver wiki)
    int     21h                     ; Usar la interrupcion para mostrar el string

    .exit
end

To get to this solution I first wrote the algorithm in Java, since I don't really handle assembler (yes, it took me a couple of days to port this solution from Java to Assembler). This is the algorithm in Java:

import java.util.Scanner;

public class MainClass {

    public static void main(String[] args) {
        /* 
        Se que se podría resolver el problema más rápidamente, pero trato de que se parezca lo más posible a Asm en la forma que opera.
        */
        Scanner reader = new Scanner(System.in);

        // mostrar instrucción
        System.out.println("Ingrese el texto a invertir:");

        // leer entrada de usuario
        char[] texto1 = reader.nextLine().toCharArray();
        int largoTexto = texto1.length;

        // invertir toda la linea
        int SI = 0;
        int DI = largoTexto;
        DI--;

        while (SI < DI) {
            char AL = texto1[SI];
            texto1[SI] = texto1[DI];
            texto1[DI] = AL;
            SI++;
            DI--;
        }

        // invertir palabras
        for (int CX = 0, BX = 0, DX = largoTexto; CX < DX; CX++) {
            if (texto1[CX] == ' ' && CX != 0) {
                SI = BX;
                DI = CX;
                DI--;
                BX = CX;
                BX++;
            } else if (CX == DX - 1) {
                SI = BX;
                DI = CX;
            } else {
                continue;
            }

            while (SI < DI) {
                char AL = texto1[SI];
                texto1[SI] = texto1[DI];
                texto1[DI] = AL;
                SI++;
                DI--;
            }
        }

        System.out.println(String.valueOf(texto1));
    }
}

Además usé los siguientes sitios como documentación:

Lenguaje Ensamblador
Print words in reverse order

Assembly Programming Tutorial
x86 Assembly Guide
A Brief x86 Assembler Tutorial

Read and display string
Read and echo a character
Exploits y stack overflows en windows

Api del MSDOS
DOS INT 21h and Function Codes
Interrupción 21H
x86 Instruction Set Reference
x86 architecture
Data transfer instructions
LEA Instruction
What does the Data Directive means
Data Types and Memory Allocation
Codigos ASCII

Online x86 / x64 Assembler and Disassembler

Scroll to Top