0

I am making a small operating system and I want to make stuff about video modes ( like getting vesa info, setting video mode ) and it is really hard to do so in 32 bit So I decided to switch back to 16 bit mode and I wrote some code that switches back to 16 bit from protected mode. But when I call the function which I wrote in my kernel I dont think that it returns to my kernel. I also wrote a switch to 32 bit mode function to return back to 32 bit but none of my functions after that works

I have tried to place a lot of ret functions in the switch 16 function to see if it returns to kernel. I tried to put some pixel after I switch back to 32 bit from 16 bit. But none of them worked and I didnt get any results

My switch to 16 bit function

[BITS 32]
global _switch16
_switch16:

    cli

    xor eax, eax
    mov cr3, eax

    lgdt [gdt_real_d]

    jmp CODE_SEG:p16mode

    ret


ivt_real:
    dd 0x3FF
    dd 0x0

gdt_real:
gdt_null : ; the mandatory null descriptor
dd 0x0 ; 'dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 10001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 10001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end : 

gdt_real_d :
dw gdt_end - gdt_real - 1 
dd gdt_real 

CODE_SEG equ gdt_code - gdt_real
DATA_SEG equ gdt_data - gdt_real 

BITS 16
p16mode:

    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax


    and al,0xFE          
    mov  cr0, eax    

    jmp 0:rmode
    ret

rmode:
    lidt [ivt_real]

    mov ax, 0x9000
    mov sp, ax

    sti 

    ret




my switch to 32 bit function

[BITS 16]
global _switch32
_switch32:

    cli

    lgdt [gdt_pmode_d]


    mov eax , cr0 
    or eax , 0x1 
    mov cr0 , eax 


    jmp CODE_SEG:pmode

    ret

gdt_pmode:
gdt_null : ; the mandatory null descriptor
dd 0x0 ; 'dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end : 

gdt_pmode_d :
dw gdt_end - gdt_pmode - 1 
dd gdt_pmode 

CODE_SEG equ gdt_code - gdt_pmode
DATA_SEG equ gdt_data - gdt_pmode 

BITS 32
pmode:

    mov ax , DATA_SEG ; Now in PM , our old segments are meaningless ,
    mov ds , ax 
    mov ss , ax 
    mov es , ax
    mov fs , ax
    mov gs , ax

    mov ebp , 0x90000 
    mov esp , ebp

    sti 

    ret 


My kernel

#include <OS.h>

extern void switch16(void);
extern void switch32(void);

int kmain()
{

    switch16();
    switch32();




    init_idt();
    SetPITSpeed(100);

    init_Cursor();


    char* str = "Hello, world!\0";
    printf(str,0xffffff,0x000000);



    while(1);
}

I compile and link with makefile

OS:
    nasm -fbin Bootloader/bootload.asm -o Bin/bootload.bin
    nasm -fbin Bootloader/diskpad.asm -o Bin/diskpad.bin
    nasm Bootloader/A20Check.asm -f elf -o Bin/a20.o
    nasm Bootloader/switch16.asm -f elf -o Bin/switch16.o
    nasm Bootloader/switch32.asm -f elf -o Bin/switch32.o

    gcc -mno-ms-bitfields -fno-PIC -m32 -ffreestanding -c Kernel/kernel.c -o Bin/kernel.o
    nasm Kernel/kernel_entry.asm -f elf -o Bin/k_entry.o

    ld -T link.ld -o Bin/kernel.tmp Bin/k_entry.o Bin/switch16.o Bin/switch32.o Bin/a20.o Bin/kernel.o
    objcopy -O binary Bin/kernel.tmp Bin/kernel.bin

    copy /b "Bin\bootload.bin" + "Bin\kernel.bin" + "Bin\diskpad.bin" /b "os-image.bin"


    qemu-system-i386 -monitor stdio os-image.bin

I am sure that I can switch to 16 bit because I can use interrupts in the assembly file (but not the kernel )I expect the code to switch to 16 bit, do some bios interrupts, switch back to 32, init_idt and show some sign that it actully works. But when I run the code above I just get empty screen in 1280x1024x24bpp vide mode (I switched to it in my bootloader) No pixel is written and printf doesnt work

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Umm wät? Your to-16 bit register sets the *page table to load from 0x0*... which cannot be right... as for the following instructions you're in limbo until you disable protected mode – Antti Haapala -- Слава Україні Mar 23 '19 at 15:24
  • @AnttiHaapala How can I reslove that issue – Gameplayer Xd Mar 23 '19 at 15:35
  • Have you tried running the kernel in Bochs? I've not used qemu for kernel dev. Bochs is quite slow but it has a built-in processor debugger which came handy when making sense of processor state flags. – Antti Haapala -- Слава Україні Mar 23 '19 at 15:36
  • You don't turn off paging by setting CR3 to zero. If you aren't using paging then the value in CR3 doesn't matter. You can't switch into 16-bit mode and expect to return back to _C_ code compiled for 32-bit. The `ret` in 16-bit mode pops a 2 byte address off the stack and returns. But your 32-bit code placed a 4 byte return address on the stack. You can override that behavior but the _C_ code won't run as expected.To do what you want you will have to enter into real mode; do something in 16-bit real mode; switch back to protected mode; and then return back to the _C_ . – Michael Petch Mar 23 '19 at 16:26
  • Although this is a multiboot example, it is an SO answer that has a complete example of a `do_vbe` function that sets a video mode and returns back the linear frame buffer and all the other VESA info so that the 32-bit code an draw directly to the screen. https://stackoverflow.com/a/41731180/3857942 . The code doesn't deal with paging (that would have to be added). `do_vbe` enters real mode does the VBE related call, switches back to protected mode and then returns back to the caller. – Michael Petch Mar 23 '19 at 16:42
  • Thank you to you all but I solved my issue (I wanted to run interrupts) by this answer/tutorial http://www.rohitab.com/discuss/topic/35103-switch-between-real-mode-and-protected-mode/ – Gameplayer Xd Mar 23 '19 at 17:45
  • @AnttiHaapala: qemu can act as a GDB remote, so I think you can single-step a bootloader / kernel with QEMU as well. But yes, using a debugger makes everything much easier. – Peter Cordes Mar 23 '19 at 17:52
  • 1
    @PeterCordes : You can single step a bootloader but you have to jump through hoops. GDB by itself has no understanding of segment:offset addressing which causes a problem if you happen to be executing code where CS != 0. Still one of the best tools is BOCHs for handling realmode and especially transitions between modes, paging, and initially setting up an IDT and GDT. – Michael Petch Mar 23 '19 at 19:21

0 Answers0