Drake
Drake

Reputation: 11

Printing a string of characters from a boot sector only displays first character

I'm developing code in an x86 boot sector as part of learning OS development. I'm expecting my code to print this to the console:

Hello

I get this instead:

H

Why is it only printing one character and not the entire string? How can I fix this?

This is a snippet of my code:

mov ah, 0x0e
mov bx, string_p
add bx, 0x7c00
mov al, [bx]
int 0x10
 jmp $
string_p:
       db 'Hello',0
"then padding and magic number"

Upvotes: 1

Views: 2011

Answers (2)

Leandros
Leandros

Reputation: 16825

The interrupt 10H, with register AH set to 0EH (INT 10h/AH=0eh), will print the current character in register AL. Ralf Brown's Interrupt List is considered the Bible of DOS and BIOS interrupts. It's a valuable source of information on what interrupts are available, how they work, and their side effects.

If you use INT 10h/AH=0eh you need to manually advance the string pointer for every character and print them out one at a time. Code like this should work:

org 0x7c00             ; starting address
bits 16                ; 16-Bit mode

main:
  cli                  ; disable interrupts
  cld                  ; clear direction flags
  xor ax, ax           ; set AX to 0
  mov ds, ax           ; set DS to 0
  mov ah, 0x0e         ; call 0EH bios call
  mov si, string       ; move starting address of `string` into SI

loop:
  lodsb                ; load byte at DS into AL, update (increment) SI
  or al, al            ; check if AL is 0 (ORing will do nothing, but set the right flags
  jz hltloop           ; if zero jump to end
  int 0x10             ; do the print call
  jmp loop             ; jump back to loop start

hltloop:
  hlt                  ; halt and catch fire
  jmp hltloop          ; jump back to halt, if an interrupt occurred anyway

string:
       db 'Hello',0

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

This example uses the LODSB instruction to read each character of the string. The LODS instructions are documented as:

Loads a byte, word, or doubleword from the source operand into the AL, AX, or EAX register, respectively. The source operand is a memory location, the address of which is read from the DS:ESI or the DS:SI registers (depending on the address-size attribute of the instruction, 32 or 16, respectively). The DS segment may be over-ridden with a segment override prefix.

Upvotes: 4

mcinteemasud
mcinteemasud

Reputation: 11

This is late, but may help someone. I was having the same issue playing around developing an os on ubuntu. Here is what worked for me. I created a print function and called it after moving the address of my string into bx:

print_function:
pusha
mov ah, 0x0e
mov al, [bx]
int 0x10
cmp al, 0
jne increment
jmp the_end

increment:  
add bx , 1
mov al, [bx]    
int 0x10
cmp al, 0
jne increment
jmp the_end 

the_end:    
popa    
ret

Upvotes: 0

Related Questions