user123
user123

Reputation: 2884

Why does this assembly code not working to print to VGA text mode video memory?

I have the following assembly code. I'm trying to create a small bootloader to go into 32-bit protected mode. Once in protected mode, I need to print to VGA text mode video memory (0xb8000) for testing purposes. My code doesn't work. I found code from various resources around the web and learned that most have similar code which works properly, like this example: Printing characters to screen ASM in protected mode . My code that doesn't work:

bits 16

mov ah, 0x00  ;Set up video mode
mov al, 0x03
int 0x10

gdt_start:
        dq 0x0
gdt_code:
        dw 0xFFFF
        dw 0x0
        db 0x0
        db 10011010b
        db 11001111b
        db 0x0
gdt_data:
        dw 0xFFFF
        dw 0x0
        db 0x0
        db 10010010b
        db 11001111b
        db 0x0  

gdtr:
        dw 24
        dd gdt_start

lgdt [gdtr]

cli

mov eax, cr0
or al, 1
mov cr0, eax

jmp 0x08:protectedMode 

bits 32

protectedMode:
    mov ax, 0x10
    mov ds, ax
    mov es, ax 
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov word [0xb8000], 0x0769 

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

I compile the code with:

nasm -fbin boot.asm -oboot.bin

and run the result with:

qemu-system-x86_64 -fda boot.bin

It doesn't do anything.

When I disassemble the code with:

ndisasm boot.bin

it outputs the following result:

enter image description here

Why is the instruction before appending the zeroes

mov dword [di], 0xb8000

while it should be

mov word [0xb8000], 0x0769

Upvotes: 3

Views: 986

Answers (1)

Thomas Jager
Thomas Jager

Reputation: 5265

When you have this block of data:

gdt_start:
        dq 0x0
gdt_code:
        dw 0xFFFF
        dw 0x0
        db 0x0
        db 10011010b
        db 11001111b
        db 0x0
gdt_data:
        dw 0xFFFF
        dw 0x0
        db 0x0
        db 10010010b
        db 11001111b
        db 0x0  

gdtr:
        dw 24
        dd gdt_start

it's located in the path of execution. This data will be executed as code by the processor as the next instructions after int 0x10. Move this lower, after mov word [0xb8000], 0x0769.

You also need to add an infinite loop after executing that instruction to prevent execution from falling down to whatever garbage follows (GDT table if you put it there).

Always keep in mind that assembly is very low-level. Whatever you stick in your code, whether actual meaningful instructions or not, will be treated as code if the processor ever gets to it. It won't skip data, and it won't stop after the last instruction you write.


As to why the instruction disassembly is wrong, the disassembler doesn't know when to switch to 32-bit mode. It's just a disassembler, not a simulator, so it can't see the effect of the far jmp that gets the CPU to execute that part in 32-bit mode.

You can disassemble the whole thing in 32-bit mode, and then (after some mess before disassembly happens to get back in sync with actual instruction boundaries) it disassembles to what you intended with this:

ndisasm -b 32 boot.bin
...                 ;; some earlier mess of 16-bit code disassembled as 32
0000003B  8ED8              mov ds,eax
0000003D  8EC0              mov es,eax
0000003F  8EE0              mov fs,eax
00000041  8EE8              mov gs,eax
00000043  8ED0              mov ss,eax
00000045  66C70500800B0069  mov word [dword 0xb8000],0x769   ; correct disassembly
         -07
0000004E  0000              add [eax],al
00000050  0000              add [eax],al

Upvotes: 3

Related Questions