1

I'm attempting to make a simple OS and can't seem to get into protected mode.
My code assembles fine and runs but it's in like a loop or something: the screen just flashes the booting message.
I'm pretty sure the problem is when I set cr0 in enter_pm.asm.

boot.asm

[org 0xc700]

mov bp, 0x8000
mov sp, bp


call switch_to_pm

jmp $


%include "enter_pm.asm"
%include "gdt.asm"


times 510-($-$$) db 0
db 0x55, 0xaa

times 512 db 'A'

gdt.asm

GDT_start:


  GDT_null:
        dd 0x0
        dd 0x0

    GDT_code:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10011010
        db 0b11001111
        db 0x0
    GDT_data:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10010010
        db 0b11001111
        db 0x0
GDT_end:

GDT_desriptor:
    dw GDT_end - GDT_start - 1
    dd GDT_start

CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start

enter_pm.asm

[bits 16]

switch_to_pm:
  cli
  lgdt [GDT_desriptor]
  mov eax, cr0
  or eax, 0x1
  mov cr0, eax
  jmp CODE_SEG:init_pm

[bits 32]
init_pm:
    mov al, 'A'
    mov ah, 0x0f
    mov [0xb8000], ax

compile command nasm -f bin boot/boot_read_disc/boot.asm -o boot.bin -i boot/boot_read_disc/

run command qemu-system-x86_64 boot.bin

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 2
    You should add an infinite loop at the end to avoid executing random memory. After the last `mov` in `enter_pm.asm` add a `jmp $`. – ecm Aug 26 '21 at 05:04
  • 3
    `[org 0xc700]` is wrong it should read `org 0x7C00`. – ecm Aug 26 '21 at 05:05
  • 3
    You should set `ss` in the instruction immediately before the one that sets `sp`. You can set `ss:sp` to `0:8000h` for 512 bytes of usable stack. – ecm Aug 26 '21 at 05:07
  • 3
    The `lgdt [GDT_desriptor]` instruction depends on the `DS` segment register. Make sure to set it up correctly. In accordance with `org 0x7C00` you need `xor ax, ax` `mov ds, ax`. – Sep Roland Aug 28 '21 at 18:53

1 Answers1

1

The value in the ORG directive is wrong, probably a typo. It should have read [ORG 0x7C00].

When you use an origin of 7C00h, you also need to make sure that your segment registers match this value. Your code only depends on the DS segment register and the correct value for it is zero. Add xor ax, ax mov ds, ax to the program.

You have initialized the stackpointer at 8000h in order to use a 512-byte stack in the range [7E00h,7FFFh]. It is not enough to only change the SP register. That register works in tandem with the SS segment register. You must set it up to obtain a consistent stackpointer and you must do it in the instruction directly above the one that modifies SP. You could choose to keep SP=8000h and clear the SS register, or you could set SS=07E0h and initialize SP=512.

Once you have entered protected mode, you still have to reload the DS segment register. If you don't, then the CPU won't know about the new 4GB limit on the data segment. Add mov ax, DATA_SEG mov ds, ax to the program.

After your code finally ouputs the character on the screen, there's nothing more to do. You need to halt the execution instead of letting it run free in data memory.

    [org 0x7C00]
    
    xor  ax, ax
    mov  ds, ax
    mov  ss, ax
    mov  sp, 0x8000    
    
    jmp  switch_to_pm

GDT_start:
  GDT_null:
    dd 0x0
    dd 0x0
  GDT_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 0b10011010
    db 0b11001111
    db 0x0
  GDT_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 0b10010010
    db 0b11001111
    db 0x0
GDT_end:
    
GDT_descriptor:
    dw GDT_end - GDT_start - 1
    dd GDT_start
    
CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start

switch_to_pm:
    cli
    lgdt [GDT_descriptor]
    mov  eax, cr0
    or   eax, 1           ; Set PE=1
    mov  cr0, eax
    mov  ax, DATA_SEG
    mov  ds, ax
    jmp  CODE_SEG:init_pm
    
[bits 32]
init_pm:
    mov  ax, 0x0F41       ; WhiteOnBlack capital A
    mov  [0x000B8000], ax ; At(1,1);

    hlt
    jmp  $-1

times 510-($-$$) db 0
dw 0xAA55
Sep Roland
  • 33,889
  • 7
  • 43
  • 76