Reputation: 6035
I have the following x86 program:
mov ah, 0x0e ; Set up call to BIOS routine to print character
mov al, [character] ; Stick the byte at label "character"
int 0x10 ; Display character in al
jmp $ ; Loop forever
character:
db 0x41 ; Put the byte "A" at this position
times 510-($-$$) db 0 ; Pad with zeros and end with the magic number for a bootloader
db 0x55
db 0xaa
I'm running it in two different ways:
dd
and booting it on an old 64 bit laptopI use the following commands to run this code:
$ nasm -f bin -o boot.bin main.s
$ qemu-system-x86_64 boot.bin # to test
$ dd if=boot.bin of=/dev/sda # to put it on a USB stick
The code as written above doesn't work in either case. On the hardware it displays a blinking cursur, and on qemu it prints a Cyrillic letter, rather than "A". So I change the second (non-empty) line to
mov al, [0x7c00 + character]
adding the offset 0x7c00
to the label, since according to some sources x86 puts your bootloader at 0x7c00
in memory. This works as expected in qemu, but continues to give me a blinking cursor on the hardware. Note that this has the same effect as putting [org 0x7c00]
at the top, by which I mean the binaries produced by using the above line or by adding an org
directive are identical (I compared their md5s).
To make sure my hardware doesn't have some weird character set where 0x41
isn't an "A", I tried
mov al, 0x41
and that works on both qemu and the hardware.
How can I properly reference the data stored at "character" so that my laptop will find the value that's supposed to be there? Note that because this is a bootloader the CPU is (if I understand correctly) in 16-bit real mode.
Upvotes: 3
Views: 341
Reputation: 6035
The x86 features several segment registers containing memory offsets. In real mode (and other modes?), these registers are implicitly added to any memory reference you make. Which segment register is used depends on the context (in other words what kind of instruction the address is used in). In our case, when we try to get data from memory with
mov al, [character]
the processor will implicitly add the contents of the ds
(for "data segment") register (multiplied by 16) to the memory offset character
. Note that this happens at runtime, not compile-time, so you won't see this in your binary if you disassemble it.
The solution is to zero out ds
at the top of the assembly program. However, note that you can't actually just say mov ds, 0
because x86 doesn't support writing constants to segment registers - you have to go via another register as in
mov ax, 0
mov ds, ax
For completeness, this is the full updated code which works on both my laptop and QEMU. Differences from the code in the question are commented below.
mov ax, 0 ; Zero out the data segment register
mov ds, ax ;
mov ah, 0x0e
mov al, [0x7c00 + character] ; Add 0x7c00 to the offset
; As mentioned in the question, putting ORG 0x7C00 at the top of the file
; also works (and is better, but this is clearer for demonstration purposes)
; and in fact produces an identical binary to this explicit addition.
int 0x10
jmp $
character:
db 0x41
times 510-($-$$) db 0
db 0x55
db 0xaa
Clearly, what was happening here is that the ds
register was zero by default on QEMU but not on my hardware. A real bootloader written by a professional would always zero out explicitly this sort of thing rather than assuming the BIOS put the registers in any particular state before loading its code.
If you've been reading "Writing a Simple Operating System - from Scratch" by Nick Blundell like me, he actually talks about this stuff a little later on in section 3.6.1 ("Extended memory access using segments"). Unfortunately I got stuck on this several pages before that and didn't read ahead.
Upvotes: 3
Reputation: 157
Maybe You lost some parameters and ORG command
Try This
org 0x7c00 ; Tell NASM ,This program begin at Address 0x7c00
mov ah, 0x0e ; Set up call to BIOS routine to print character
mov al, [character] ; Stick the byte at label "character"
mov bh,0 ; The PAGE 0
mov bl,0xff ; White
int 0x10 ; Display character in al
jmp $ ; Loop forever
character:
db 0x41 ; Put the byte "A" at this position
times 510-($-$$) db 0 ; Pad with zeros and end with the magic number for a bootloader
db 0x55
db 0xaa
Upvotes: -1