1

I have this program which I want to write the string to the screen, but the output is empty, I have added comments at every line.

[org 0x7c00] ; Tell the assember the place to load this code

    mov bx, HELLO_MSG ; move the data to the bx register
    pusha             ; push all register values to stack?
    call printstr     ; call printstr routine

printstr:
    mov ah, 0x0e ; BIOS teletype output
    pop bx       ; pop to bx
    mov al, bl   ; put low byte of bx to al
    int 0x10     ; print it.
    cmp al, 0    ; Compare if its a string termination character?
    jne printstr ; if not go back to top
    ret          ; return to the caller?


; Data
HELLO_MSG:
    db 'Hello world!', 0

jmp $

times  510-($-$$) db 0
dw  0xaa55

I am running this program from the bootsector of a disk in qemu emulating an AT-compatible x86 based computer.

Michael Karcher
  • 3,803
  • 1
  • 14
  • 25
anekix
  • 2,393
  • 2
  • 30
  • 57
  • 1
    You should do the check for `al` equal to 0 before you decide to print the character. So change the order of `int 0x10` and `cmp`. Also in such case you need and exit jump so `je`. At the end you just `jmp` unconditionally back to `printstr`. – Paweł Łukasik Apr 22 '20 at 18:49
  • 1
    Please think about what happens if `printstr` returns. – Michael Karcher Apr 22 '20 at 18:51
  • Yes i realised my mistake as soon as i posted comment & made an edit, but i think its hacky? – anekix Apr 22 '20 at 18:53
  • 1
    Your hack places the `RET` instruction in a way that it is used *twice*. The second `RET` is missing in the original code. It seems like your environment expects you to "RET"urn from the entry point when the program has finished its assigned task. – Michael Karcher Apr 22 '20 at 18:57
  • I got it working by changing the order of `cmp` and int `0x10` and removed `ret` and `a`.Thanks :) – anekix Apr 22 '20 at 18:59
  • Please read [Can I answer my own question?](http://stackoverflow.com/help/self-answer). It is inappropriate to edit the solution you've found into the question itself. Answers (even from the question author) belong in the space below that is provided for that purpose. (I've rolled back your inappropriate edits.) – Ken White Apr 22 '20 at 19:05
  • Your program just "works" by luck. Your final version does not have any statement that tells the environment that you are done printing and it should stop execution. It just happens that your code crashes or does useless things that do not print any further characters. *Please find the correct way to terminate your program!* I can't tell it to you, because I don't know the environment you work in. If it's a boot sector on a real PC, think about `CLI; HLT`. – Michael Karcher Apr 22 '20 at 19:07
  • @MichaelKarcher i am trying to create a `bootsector `& running in `qemu` .I am all new to this – anekix Apr 22 '20 at 19:10
  • @anekix I don't know whether you got a notice: I undeleted and amended my answer to explain about termination. – Michael Karcher Apr 22 '20 at 19:16

2 Answers2

3

1) The BX register does not hold the character you want to print but holds the memory address of where the character you want to print can be found (@HELLO_MSG). Thus what you really want to say is not "mov al,bl" but "mov al,[bx]", which is to say

"Set AL to The byte stored at memory address BX" (mov AL,[BX])

2) The BX register is used by the BIOS for the colour component it teletypes with. You cant use that register as the index to the memory at HELLO_MSG. Try using the SI register instead - "mov al,[SI]".

3) With each loop you need the program to move to the next character in the string so that it both prints the next character and compared the next character to "0", so you need to increment your indexing register (SI as I suggest in point 2). So you need to put an "inc si" instruction somewhere after the setting of al and before the jne jump.

4) You are also misusing the stack. A pusha instruction will push all the registers onto the stack and they are put on the stack in a specific order. The best way to retrieve back from a pusha is a popa. If you pop bx, you will infact pop what was orignially on ax onto the stack and the stack pointer will be out of sync by about 10 bytes or so. I wouldnt even use the stack.

This is the way I would do what you are trying to do:

    pusha             ; save all the registers to the stack
    mov si, HELLO_MSG ; set SI to to the address of the message
printstr:
    mov ah, 0x0e ; BIOS teletype output
    mov bh, 00h  ; teletyping to the default page at B800h (IBM PC)
    mov bl, 07h  ; the CGA colour white
    mov al,[si]  ; put byte of memory address si into al
    int 0x10     ; print it.
    inc si
    cmp al, 0    ; Compare if its a string termination character?
    jne printstr ; if not go back to top
    popa         ; retrieve all the registers from the stack
    ret          ; return to the caller?


; Data
HELLO_MSG:
    db 'Hello world!', 0

Note that I use pusha and popa just to demonstrate their use. Since I only ax,bx and si, it would be cheaper to do three pushes of these registers and three pops at the end if it was indeed even neccessary to save these registers in the context of the full program. I try to write so that registers can be immediately dumped unless saving their contents are an unavoidable neccessity.

Stephen Duffy
  • 467
  • 2
  • 14
2

The POP BX instruction does not do what you seem to assume. It takes a value from the stack and stores it in BX. It does not access the memory that BX points to. You need something like MOV AL, [BX] to retrieve a character pointed to by BX, and then an instruction to make BX point to the next character.

Also, you don't properly terminate your program. CALL saves the current position and then starts printing the string. When you RETurn, the processor continues execution after the CALL instruction. You need something to tell the processor to stop executing stuff, or it might try to print some more things it shouldn't print.

As you mentioned in a comment, you use a boot sector in qemu. There seems to be no concept of telling qemu to exit, so you should block in your program. A typical way to achieve that is the sequence of the instruction CLI to tell the processor that it should not handle any interrupts, followed by the instruction HLT, which tells the processor to not do anything until it handled an interrupt. As you blocked interrupts, the HLT instruction will wait forever.

Michael Karcher
  • 3,803
  • 1
  • 14
  • 25
  • I can get it to print to the screen but some extra character are being printed, i have made changes in my original question – anekix Apr 22 '20 at 18:45