Rifleman192
Rifleman192

Reputation: 23

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)

Upvotes: 1

Views: 6970

Answers (3)

Moby Disk
Moby Disk

Reputation: 3851

Caveat: I haven't done this in decades and I don't have a compiler in front of me that can do this. This looks like an assignment so I'm not going to write the code, but I'll point you in the right direction.

Int 21h function 2 requires you to set dl equal to the character you want to output. No pushes and pops required here since DOS doesn't use the stack for parameter passing. First, outside of your loop, you want to point a register to the address of the message, something like: MOV si, message. Then, you need to dereference a single byte from the message and put it into dl. This will require using indirect address notation. Then you can call interrupt 21h and write the character.

As for the loop, there are a few ways to approach it. You could use a counter, with cx being the typical register for that, and use a LOOP statement or DEC and JNZ statements. Or you could put a marker at the end of the string, and terminate the loop when you hit that marker. A null character is commonly used for this. You will also need to move to the next character in the string. You can do that by adding an index, or by incrementing the value of the si register each iteration of the loop. Something like INC si

With all that said, there is a much easier way that avoids the loop but maybe it is cheating. Take a look at interrupt 21h function 9

Upvotes: 1

Fifoernik
Fifoernik

Reputation: 9899

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

Upvotes: 1

Rifleman192
Rifleman192

Reputation: 23

Yeah, using function 9 would be 'cheating' as it is one of the other exercises (which was the easy one). I have revised my code and got it to start with the beginning character. I just need to figure out how to get it to keep looping so that it continues to read the string and not stop arbitrarily. Here's what I've got so far (It prints H and the 15 e's):

       .model small
    .data
        message db "Hello, DOS Here!"
    .code
    main proc
        mov ax, @data
        mov ds, ax

        mov ah, 2
        mov cx, 16
        mov dl, message
    L1: 
        int 21h
        mov dl, message + 1
        loop L1

        .EXIT
    main endp
    end main

Upvotes: 0

Related Questions