Search code examples
dosx86-16

Printing individual characters from a string to standard output in ms-dos/assembly x86-16


I am new to x86-16bit programming. I am very stumped by how I call function 2 properly to read characters individually from a string. If anybody has any ideas it would be greatly appreciated. The code below shows my current attempt (one of many).

    .model small
    .data
        message db "Hello, DOS Here!", 0dh, 0ah
    .code
    main proc
        mov ax, @data
        mov ds, ax
    L1:
        mov ah, 2
        mov bx, 1
        int 21h
        loop L1

        .EXIT
    main endp
    end main

I'm also supposed to use push and pop to be able to print the string in the same method but backwards. I'm sure I'm missing something obvious. All I get when it prints is the British pound symbol for a few lines. (Dec: 156; Hex: 9C)


Solution

  • With this definition message db "Hello, DOS Here!", 0dh, 0ah, next code will print the complete message:

    mov  ah, 02h
    mov  dl, message     ;Get the H character
    int  21h
    mov  dl, message+1   ;Get the e character
    int  21h
    mov  dl, message+2
    int  21h
    mov  dl, message+3
    int  21h
    mov  dl, message+4
    int  21h
    mov  dl, message+5
    int  21h
    mov  dl, message+6
    int  21h
    mov  dl, message+7
    int  21h
    mov  dl, message+8
    int  21h
    mov  dl, message+9
    int  21h
    mov  dl, message+10
    int  21h
    mov  dl, message+11
    int  21h
    mov  dl, message+12
    int  21h
    mov  dl, message+13
    int  21h
    mov  dl, message+14
    int  21h
    mov  dl, message+15
    int  21h
    mov  dl, message+16   ;Get the 0Dh carriage return
    int  21h
    mov  dl, message+17   ;Get the 0Ah linefeed
    int  21h
    

    Rather stupid, wouldn't you say?
    In order to use a loop and obtain much compacter code we need to:

    1. Put the address of your message in an address register. You can choose from SI, DI, BX and BP. I've picked BX in below code.
    2. Read one byte at this address.
    3. Output the byte with DOS.
    4. Increment the pointer in the address register.
    5. Repeat steps 2, 3, 4, 5 for all the text by checking if it was the linefeed character that you just outputted. Since in your text that's the very last character.

    A version of this loop:

     mov  bx, OFFSET message  ;1.
    Again:
     mov  dl, [bx]            ;2.
     mov  ah, 02h             ;3.
     int  21h
     inc  bx                  ;4.
     cmp  dl, 0Ah             ;5.
     jne  Again
    

    I'm also supposed to use push and pop to be able to print the string in the same method but backwards.

    To accomplish this next task of yours, you would put a push dx in between steps 2. and 3. Then write an additional loop to display the reversed string:

    Again2:
     pop  dx
     mov  ah, 02h
     int  21h
     cmp  dl, "H"
     jne  Again2
    

    The pitfalls here will be that

    • you shouldn't output the linefeed and carriage return before the other (real) characters ==> Hint: do 2 pops before starting the 2nd loop
    • you can't readily assume that "H" will only appear as the text's first character ==> Hint: count the number of pushs