assemblyx86operating-systemnasm

adding 32 bit protected to operating system


This question has been solved!!! I have updated the bootloader and kernal code to match the fixed one for any Operating System Makers who are struggling with the same question(might add comments soon to the kernal). Below is the explanation to the fixed code which was made by Nate Eldredge. Unrelated but OS is originally called POVESK. Turns out something is already named that, so it is changed now in bootloader code.

I have been making an operating system recently and have encountered a big problem in making the GDT

kernal code(thats how I spell kernel):

[org 0x8000]
[bits 16]

jmp short start

%define ENDL 0x0D, 0x0A

gdt_start:
    dq 0x0000000000000000

    dq 0x00CF9A000000FFFF
    dq 0x00CF92000000FFFF

    dq 0x00CF92000000FFFF

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

print:
    push si
    push ax
    push bx

.loop:
    lodsb
    or al, al
    jz .done

    mov ah, 0x0E
    mov bh, 0
    int 0x10

    jmp .loop

.done:
    pop bx
    pop ax
    pop si    
    ret

start:
    mov si, start_sixteen
    call print

    in al, 0x64
.wait_ibe:
    test al, 0x02
    jnz .wait_ibe
    mov al, 0xD1
    out 0x64, al
.wait_ibe2:
    in al, 0x64
    test al, 0x02
    jnz .wait_ibe2
    mov al, 0xDF
    out 0x60, al

first_sixteen:
    mov ah, 0x00
    mov al, 0x03
    int 0x10

    cli
    lgdt [gdt_descriptor]

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

    jmp dword 0x08:.afterstart

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

.afterstart:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov ebx, start_thirty_two
    call print32

    cli
    hlt

    jmp short start_prog

print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret

start_prog:
    mov al, 2
    mov eax, 1004
    mov [Xval], eax
    mov eax, 50
    mov [Yval], eax
    mov eax, 10
    mov [Wval], eax
    mov eax, 20
    mov [Hval], eax
    lea esi, [PySpr]
    
    call draw_spr

.halt:
    cli
    hlt

draw_spr:
    xor edx, edx
    mov ecx, [Hval]
draw_sprite:
    mov eax, [Yval]
    mov ebx, 1024
    mul ebx
    add eax, [Xval]
    mov edi, eax
    push ecx
    mov ecx, [Wval]
draw_pixels:
    mov al, [esi]
    cmp al, 5
    je skip_pixel_2
    stosd
    jmp skip_pixel
skip_pixel_2:
    inc edi
skip_pixel:
    inc esi
    loop draw_pixels
    pop ecx
    mov eax, [Yval]
    inc eax
    mov [Yval], eax
    loop draw_sprite
    ret

start_sixteen: db 'STAGE ONE START', ENDL, 0
start_thirty_two: db 'STAGE TWO START', 0

Xval dd 0
Yval dd 0
Wval dd 0
Hval dd 0

PySpr db 5,5,5,2,2,2,2,5,5,5
      db 5,5,2,2,2,2,2,2,5,5
      db 5,2,2,2,2,2,2,2,2,5
      db 2,2,2,2,2,2,2,2,2,2
      db 5,8,8,8,8,8,8,8,8,5
      db 5,8,15,0,8,8,0,15,8,5
      db 5,8,8,8,8,8,8,8,8,5
      db 5,8,8,0,0,0,0,8,8,5
      db 5,5,8,8,8,8,8,8,5,5
      db 2,2,2,2,2,2,2,2,2,2
      db 2,2,2,2,2,2,2,2,2,2
      db 2,5,2,2,2,2,2,2,5,2
      db 2,5,2,2,2,2,2,2,5,2
      db 2,5,2,2,2,2,2,2,5,2
      db 8,5,2,2,2,2,2,2,5,8
      db 5,5,6,6,6,6,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,5,6,6,5,5,6,6,5,5
      db 5,7,7,7,5,5,7,7,7,5

bootloader code(is basically the nanobyte os bootloader at part 3 but minorly changed):

[org 0x7C00]
[bits 16]

%define ENDL 0x0D, 0x0A

jmp short start
nop

bdb_oem:                    db 'CLIPIN..'
bdb_bytes_per_sector:       dw 512
bdb_sectors_per_cluster:    db 1
bdb_reserved_sectors:       dw 1
bdb_fat_count:              db 2
bdb_dir_entries_count:      dw 0E0h
bdb_total_sectors:          dw 2880
bdb_media_descriptor_type:  db 0F0h
bdb_sectors_per_fat:        dw 9
bdb_sectors_per_track:      dw 18
bdb_heads:                  dw 2
bdb_hidden_sectors:         dd 0
bdb_large_sector_count:     dd 0

ebr_drive_number:           db 0
                            db 0
ebr_signature:              db 29h
ebr_volume_id:              db 12h, 34h, 56h, 78h
ebr_volume_label:           db 'CLIPIN.. OS'
ebr_system_id:              db 'FAT12   '

start:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    push es
    push word .after
    retf

.after:
    mov [ebr_drive_number], dl
    mov si, msg_loading
    call puts
    push es
    mov ah, 08h
    int 13h
    jc floppy_error
    pop es
    and cl, 0x3F
    xor ch, ch
    mov [bdb_sectors_per_track], cx
    inc dh
    mov [bdb_heads], dh
    mov ax, [bdb_sectors_per_fat]
    mov bl, [bdb_fat_count]
    xor bh, bh
    mul bx
    add ax, [bdb_reserved_sectors]
    push ax
    mov ax, [bdb_dir_entries_count]
    shl ax, 5
    xor dx, dx
    div word [bdb_bytes_per_sector]
    test dx, dx
    jz .root_dir_after
    inc ax

.root_dir_after:
    mov cl, al
    pop ax
    mov dl, [ebr_drive_number]
    mov bx, buffer
    call disk_read
    xor bx, bx
    mov di, buffer

.search_kernal:
    mov si, file_kernal_bin
    mov cx, 11
    push di
    repe cmpsb
    pop di
    je .found_kernal
    add di, 32
    inc bx
    cmp bx, [bdb_dir_entries_count]
    jl .search_kernal
    jmp kernal_not_found_error

.found_kernal:
    mov ax, [di + 26]
    mov [kernal_cluster], ax
    mov ax, [bdb_reserved_sectors]
    mov bx, buffer
    mov cl, [bdb_sectors_per_fat]
    mov dl, [ebr_drive_number]
    call disk_read
    mov bx, kernal_LOAD_SEGMENT
    mov es, bx
    mov bx, kernal_LOAD_OFFSET

.load_kernal_loop:
    mov ax, [kernal_cluster]
    add ax, 31
    mov cl, 1
    mov dl, [ebr_drive_number]
    call disk_read
    add bx, [bdb_bytes_per_sector]
    mov ax, [kernal_cluster]
    mov cx, 3
    mul cx
    mov cx, 2
    div cx
    mov si, buffer
    add si, ax
    mov ax, [ds:si]
    or dx, dx
    jz .even

.odd:
    shr ax, 4
    jmp .next_cluster_after

.even:
    and ax, 0x0FFF

.next_cluster_after:
    cmp ax, 0x0FF8
    jae .read_finish
    mov [kernal_cluster], ax
    jmp .load_kernal_loop

.read_finish:
    mov dl, [ebr_drive_number]
    mov ax, kernal_LOAD_SEGMENT
    mov ds, ax
    mov es, ax
    jmp kernal_LOAD_SEGMENT:kernal_LOAD_OFFSET
    jmp wait_key_and_reboot
    cli
    hlt

floppy_error:
    mov si, msg_read_failed
    call puts
    jmp wait_key_and_reboot

kernal_not_found_error:
    mov si, msg_kernal_not_found
    call puts
    jmp wait_key_and_reboot

wait_key_and_reboot:
    xor ah, ah
    int 16h
    jmp 0FFFFh:0

.halt:
    cli
    hlt

puts:
    push si
    push ax
    push bx

.loop:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    xor bh, bh
    int 0x10
    jmp .loop

.done:
    pop bx
    pop ax
    pop si    
    ret

lba_to_chs:
    push ax
    push dx
    xor dx, dx
    div word [bdb_sectors_per_track]
    inc dx
    mov cx, dx
    xor dx, dx
    div word [bdb_heads]
    mov dh, dl
    mov ch, al
    shl ah, 6
    or cl, ah
    pop ax
    mov dl, al
    pop ax
    ret

disk_read:
    push ax
    push bx
    push cx
    push dx
    push di
    push cx
    call lba_to_chs
    pop ax
    mov ah, 02h
    mov di, 3

.retry:
    pusha
    stc
    int 13h
    jnc .done
    popa
    call disk_reset
    dec di
    test di, di
    jnz .retry

.fail:
    jmp floppy_error

.done:
    popa
    pop di
    pop dx
    pop cx
    pop bx
    pop ax
    ret

disk_reset:
    pusha
    xor ah, ah
    stc
    int 13h
    jc floppy_error
    popa
    ret

msg_loading:            db 'BOOT START', ENDL, 0
msg_read_failed:        db 'FAIL READ DISK', ENDL, 0
msg_kernal_not_found:   db 'KERNAL NOT FOUND', ENDL, 0
file_kernal_bin:        db 'KERNAL  BIN'
kernal_cluster:         dw 0

kernal_LOAD_SEGMENT     equ 0x0000
kernal_LOAD_OFFSET      equ 0x8000

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

buffer:

the issue lies in the kernal code. I did some debugging and have determined that this is probably because of a triple fault(conclusion made by chatGPT) and that it usually happened in jmp dword 0x08:.afterstart but strangely after many attempts at fixing this problem(finding many tutorials and projects and fixing them up into my code) the triple fault have appeared in many different lines of code. I am pretty inexperienced in this topic so I may be just really dumb and missing something obvious.

If anybody has a solution please tell me. Thanks in advance!

triple fault in jmp dword 0x08:.afterstart and has started appearing in many other lines.

Build commands:

--WinCMD NASM
nasm scr/boot.asm -f bin -o build/boot.bin
nasm scr/kernal.asm -f bin -o build/kernal.bin
--WSL
dd if=/dev/zero of=v_004.img bs=512 count=2880
mkfs.fat -F 12 -n NBOS v_004.img
dd if=boot.bin of=v_004.img conv=notrunc 
mcopy -i v_004.img kernal.bin ::kernal.bin
--QEMUCMD
qemu-system-x86_64 v_004.img

Solution

  • ecm pointed out the bug with gdt_start needing + kernal_LOAD_SEGMENT * 16, but the same applies to every label in kernal.asm that is used in 32-bit mode.

    From the 16-bit bootloader, you load the kernal at linear address 0x20000 and far jump to 0x2000:0x0000, so CS = 0x2000 on entry to the kernal. With org 0 at the top of the file, the assembler computes the addresses (offsets) of all labels starting with address 0. This is correct in 16-bit mode when CS = 0x2000, but incorrect as soon as you are in 32-bit mode, and CS is a segment with base = 0.

    So the first problem occurs with jmp dword 0x08:.afterstart. The assembler has given .afterstart the address 0x6E, which is correct relative to the start of the file and relative to 16-bit segment 0x2000, but certainly not correct relative to 32-bit segment 0x08 whose base address is 0. The actual code at .afterstart is at linear address 0x2006E, but you are jumping to linear address 0x0006E, which is somewhere in the 16-bit interrupt vector table and whose bytes certainly aren't any useful code. You crash sometime after that when those bytes decode as an instruction which faults (causing a triple fault because you have no exception handlers installed).

    You could fix it by replacing .afterstart with .afterstart + kernal_LOAD_SEGMENT * 16 as you did before, but you would have to do the same with every other label that gets used in 32-bit mode: start_thirty_two, Xval, Yval, etc, etc.

    So a better solution might be to load your kernel in the low 64 KB of memory, and far-jump to it with a segment of 0. For instance, load it at 0x0000:0x8000 (linear address 0x8000), do a far jump from the bootloader to 0x0000:0x8000 (so that CS = 0), and then put org 0x8000 at the top of your file. Then every label in your file has the correct address whether interpreted as an offset to 16-bit segment 0x0000, or as an offset to a 32-bit segment with base address 0. This is fine so long as your kernel fits in 32 KB; and once it exceeds that, you'll probably need a multi-stage bootloader in any case.


    Another bug is that [bits 32] needs to go right before .afterstart, since that code is executing in 32-bit mode. As it stands, mov ax, 0x10 is given its 16-bit encoding, which in 32-bit mode decodes as mov eax, imm32 and uses an additional 2 bytes as the immediate. The result is that you load the high bits of eax with the bytes that make up the following instruction mov ds, ax and don't actually execute it.

    After these fixes the STAGE TWO START message is successfully displayed.


    Next bug (minor): you don't want ENDL in the start_thirty_two string, because those bytes don't start a new line when writing directly to video memory; they just show up as funny looking characters.


    Finally, draw_spr is going to need some serious debugging work as well. I don't understand how the arithmetic there is supposed to work, but you end up with EDI = 0xcbec which is certainly not anywhere within video memory. And you are using stosd to store all 32 bits of eax but only the low 8 bits (al) seems to have been initialized to anything meaningful; did you mean stosb instead?

    More fundamentally, the label names suggest you're trying to draw pixels, but you haven't changed video modes, so you're still in text mode where bytes of video memory are interpreted as characters, not pixels. (Unless you're going for some ASCII art effect, where each "pixel" is a text-mode character cell?)

    Given that you haven't commented your code at all, it's unclear what it is supposed to be doing, so you're on your own from here on out.

    Get a debugger working! I found all these bugs by single-stepping your code in Bochs. It's a complete waste of time to try to develop any kind of assembly program unless you first make sure you have a debugger and know how to use it.