Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly “如何修复”;qemu:致命:试图在0x000a0000处的RAM或ROM之外执行代码;_Assembly_X86_Qemu_Bootloader_Osdev - Fatal编程技术网

Assembly “如何修复”;qemu:致命:试图在0x000a0000处的RAM或ROM之外执行代码;

Assembly “如何修复”;qemu:致命:试图在0x000a0000处的RAM或ROM之外执行代码;,assembly,x86,qemu,bootloader,osdev,Assembly,X86,Qemu,Bootloader,Osdev,我正在开发自己的bootloader+内核。我创建了一个项目并将其放在github上:(BranchTMPLibc实现) 我尝试使用QEMU运行boot.bin: qemu-system-i386-fda boot.bin-nographic-serial stdio-monitor none 然而,发生碰撞时: > qemu-system-i386 -fda ./deploy/boot.bin -nographic -serial stdio -monitor none > WAR

我正在开发自己的bootloader+内核。我创建了一个项目并将其放在github上:(BranchTMPLibc实现)

我尝试使用QEMU运行boot.bin:

qemu-system-i386-fda boot.bin-nographic-serial stdio-monitor none

然而,发生碰撞时:

> qemu-system-i386 -fda ./deploy/boot.bin -nographic -serial stdio -monitor none
> WARNING: Image format was not specified for './deploy/boot.bin' and probing guessed raw.
>         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
>         Specify the 'raw' format explicitly to remove the restrictions.
> qemu: fatal: Trying to execute code outside RAM or ROM at 0x000a0000
> 
> EAX=00000055 EBX=00018eb4 ECX=00018eb3 EDX=00000000
ESI=00000001 EDI=00000000 EBP=00016058 ESP=00015f94
EIP=0009ffae EFL=00000896 [-OS-AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00007c36 00000018
IDT=     00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000055 CCD=000000d1 CCO=ADDB    
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
> makefile:26: recipe for target 'run' failed
> make: *** [run] Aborted (core dumped)
My boot.asm和linker.ld:

section .boot
bits 16                     ; We're working at 16-bit mode here
global boot

boot:
    mov ax, 0x2401          
    int 0x15                ; Enable A20 bit 

    mov ax, 0x3             ; Set VGA text mode 3
    int 0x10                ; Otherwise, call interrupt for printing the char   

    mov [disk],dl

    mov ah, 0x2             ;read sectors
    mov al, 60              ;sectors to read
    mov ch, 0               ;cylinder idx
    mov dh, 0               ;head idx
    mov cl, 2               ;sector idx
    mov dl, [disk]          ;disk idx
    mov bx, copy_target     ;target pointer
    int 0x13

    cli                     ; Disable the interrupts
    lgdt [gdt_pointer]      ; Load the gdt table
    mov eax, cr0            ; Init swap cr0...
    or eax,0x1              ; Set the protected mode bit on special CPU reg cr0
    mov cr0, eax
    jmp CODE_SEG:boot32     ; Long jump to the code segment


; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096 if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
;  > 0 = segment grows up from base, 1 = segment grows down for a data segment
;  > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
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
gdt_end:
gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
disk:
    db 0x0

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

;; Magic numbers
times 510 - ($ - $$) db 0

dw 0xaa55
copy_target:
bits 32
    msg:    db "Hello, World more than 512 bytes!", 0

boot32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax  
    ;mov esi, msg            ; SI now points to our message
    ;mov ebx, 0xb8000       ; vga memory position (0) 

.loop   lodsb               ; Loads SI into AL and increments SI [next char]
    or al, al               ; Checks if the end of the string
    jz halt                 ; Jump to halt if the end
    or eax,0x0200           ; The top byte defines the character colour in the buffer as an int value from 0-15 with 0 = black, 1 = blue and 15 = white. 
                            ; The bottom byte defines an ASCII code point
    mov word [ebx], ax      
    add ebx, 2              
    jmp .loop               ; Next iteration of the loop

halt:   
    mov esp, kernel_stack_top
    extern __start
    call __start
    cli
    hlt                     ; CPU command to halt the execution

section .bss
align 4
kernel_stack_bottom: equ $
    resb 16384 ; 16 KB
kernel_stack_top:
我的makefile的相关部分是:

NASM:=nasm
CC:=gcc
SRC_NASM:=./src/init/boot.asm
SRC_C:=./src/init/boot.c ./src/init/init.c ./src/init/version.c
LINKER:=./src/init/linker.ld
DEPLOY=./deploy
BUILD:=./build
BIN:=$(DEPLOY)/boot.bin
OBJ_NASM:=$(BUILD)/boot.o
CFLAGS:=-Wall -Werror -m32 -fno-pie -ffreestanding -mno-red-zone -fno-exceptions -nostdlib -I./src/include
LDFLAGS:=

export ARCH:=i386
export ZLIB_SUPPORT:=false

DEPENDENCIES:=libc
ifeq ($(ZLIB_SUPPORT),true)
DEPENDENCIES:=$(DEPENDENCIES) zlib
endif

all: $(DEPENDENCIES)
    mkdir -p $(DEPLOY)
    mkdir -p $(BUILD)
    $(NASM) $(SRC_NASM) -f elf32 -o $(OBJ_NASM)
    $(CC) $(SRC_C) $(OBJ_NASM) -o $(BIN) $(CFLAGS) -T $(LINKER) $(LDFLAGS)

run:
    qemu-system-i386 -fda $(BIN) -nographic -serial stdio -monitor none
为什么它会以这种方式失败?我如何修复它?

此错误(“尝试在RAM或ROM之外的0x000a0000处执行代码”)通常表示控制流问题-例如,CPU跳转或调用或返回到不可靠的地址,然后开始在未初始化的RAM中执行零(由CPU解释为
添加
指令),直到CPU到达传统VGA区域(0x000A0000)

至于错误的原因,我没有仔细看

我没有真正查看的原因是,这并不重要。最终,您的引导加载程序必须从BIOS获取内存映射(例如,“int 0x15,eax=0xE820”),并希望自动检测内核的大小(而不是假设内核总是正好30 KiB),或者希望处理大于1 MiB的内核(例如,Linux通常大于5 MiB),或者还希望加载某种“初始RAM磁盘”(对于微内核,这是唯一可能的情况,在这种情况下,您可以假设内核将小于在实模式下可访问的~640 KiB RAM),可能需要解压缩内核和/或“初始RAM磁盘”,可能需要检查内核是否正常(例如,可能通过检查头文件和CRC),可能需要能够设置良好的图形视频模式(例如,1920*1600,有数百万种颜色)。还需要“BIOS参数块”(对于未分区的设备-例如软盘)或必须处理分区方案(并且不假定分区从磁盘的开头开始)

所有这些内容(以及更多内容,比如检查A20是否实际启用)都太大,无法容纳512字节(所有这些内容都意味着在前512字节中切换到受保护模式总是一个错误)

这意味着您需要重新设计并重写代码,并且无论您是否找到/修复当前错误,现有代码都将被丢弃,因此没有理由花费时间查找/修复当前错误。

此错误(“尝试在RAM或ROM外部0x000a0000处执行代码”)通常表示控制流问题-例如,CPU跳转或调用或返回到不可靠的地址,然后开始在未初始化的RAM中执行零(CPU将其解释为
add
指令),直到CPU到达传统VGA区域(0x000A0000)

至于错误的原因,我没有仔细看

我没有真正查看的原因是,这并不重要。最终,您的引导加载程序必须从BIOS获取内存映射(例如,“int 0x15,eax=0xE820”),并希望自动检测内核的大小(而不是假设内核总是正好30 KiB),或者希望处理大于1 MiB的内核(例如,Linux通常大于5 MiB),或者还希望加载某种“初始RAM磁盘”(对于微内核,这是唯一可能的情况,在这种情况下,您可以假设内核将小于在实模式下可访问的~640 KiB RAM),可能需要解压缩内核和/或“初始RAM磁盘”,可能需要检查内核是否正常(例如,可能通过检查头文件和CRC),可能需要能够设置良好的图形视频模式(例如,1920*1600,有数百万种颜色)。还需要“BIOS参数块”(对于未分区的设备-例如软盘)或必须处理分区方案(并且不假定分区从磁盘的开头开始)

所有这些内容(以及更多内容,比如检查A20是否实际启用)都太大,无法容纳512字节(所有这些内容都意味着在前512字节中切换到受保护模式总是一个错误)


这意味着您需要重新设计并重写代码,而不管您是否找到/修复当前错误,现有代码都将被丢弃,因此没有理由花时间查找/修复当前错误。

主要问题是您没有将整个内核读入内存。您的代码最终会以执行未初始化的内存(很可能充满了零),到达扩展的BIOS数据区域(正好在0xa0000处的视频内存下方),然后最终开始在0xa0000处执行视频内存。QEMU不允许执行视频内存,因此您会得到错误源

修复这一问题并不像最初看起来那么容易。我系统上的代码约为47300字节。MBR为1个扇区,内核为92个扇区。第一个问题是,并非所有硬件(和模拟器)都能同时读取92个扇区。QEMU和BOCHs的最大值分别为72个软盘驱动器和128个硬盘驱动器。对于某些硬件,这个数字可能更小(低至每条轨道的扇区数)

某些硬件无法读取扇区:

  • 超出64KiB的段限制
  • 它跨越多个磁道。并非所有BIOS都支持多磁道读写。QEMU和BOCHS确实支持它们
  • 如果BIOS使用直接内存访问(DMA)传输进行磁盘访问,则可能无法写入穿过64KiB边界的多个扇区(在物理内存中)。这意味着,如果写入在物理地址0x10000之前开始,在物理地址0x10000之后结束,则无法保证写入成功。0x20000、0x30000、0x40000…0x90000也是如此。QEMU和BOCHS不允许跨此类边界进行磁盘传输
使用BOCHS和QEMU加载高达64KiB的内核的一个简单方法是将64个扇区(32KiB)读取到物理地址0x0000:0x8000
NASM:=nasm
CC:=gcc
SRC_NASM:=./src/init/boot.asm
SRC_C:=./src/init/boot.c ./src/init/init.c ./src/init/version.c
LINKER:=./src/init/linker.ld
DEPLOY=./deploy
BUILD:=./build
BIN:=$(DEPLOY)/boot.bin
OBJ_NASM:=$(BUILD)/boot.o
CFLAGS:=-Wall -Werror -m32 -fno-pie -ffreestanding -mno-red-zone -fno-exceptions -nostdlib -I./src/include
LDFLAGS:=

export ARCH:=i386
export ZLIB_SUPPORT:=false

DEPENDENCIES:=libc
ifeq ($(ZLIB_SUPPORT),true)
DEPENDENCIES:=$(DEPENDENCIES) zlib
endif

all: $(DEPENDENCIES)
    mkdir -p $(DEPLOY)
    mkdir -p $(BUILD)
    $(NASM) $(SRC_NASM) -f elf32 -o $(OBJ_NASM)
    $(CC) $(SRC_C) $(OBJ_NASM) -o $(BIN) $(CFLAGS) -T $(LINKER) $(LDFLAGS)

run:
    qemu-system-i386 -fda $(BIN) -nographic -serial stdio -monitor none
ENTRY(boot)
SECTIONS {
    . = 0x7c00;
    .boot :
    {
        *(.boot)
    }
    /* Place kernel right after boot sector on disk but set the
     * VMA (ORiGin point) to 0x8000 */
    . = 0x8000;
    __kernel_start = .;
    __kernel_start_seg = __kernel_start >> 4;
    .text : AT(0x7e00)
    {
        *(.text.start)
        *(.text*)
    }
    .rodata :
    {
        *(.rodata*)
    }
    .data :
    {
        *(.data)
    }
    /* Compute number of sectors that the kernel uses */
    __kernel_end = .;
    __kernel_size_sectors = (__kernel_end - __kernel_start + 511) / 512;

    .bss :
    {
        __bss_start = .;
        *(COMMON)
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
        /* Compute number of DWORDS that BSS section uses */
        __bss_sizel = (__bss_end - __bss_start) / 4;
    }
}
section .boot
bits 16                     ; We're working at 16-bit mode here
global boot

boot:
    xor ax, ax
    mov ds, ax
    mov ss, ax
    mov sp, 0x7c00          ; Set SS:SP just below bootloader

    cld                     ; DF=0 : string instruction forward movement
    mov ax, 0x2401
    int 0x15                ; Enable A20 bit

    mov ax, 0x3             ; Set VGA text mode 3
    int 0x10                ; Otherwise, call interrupt for printing the char

    mov [disk],dl

    ; Read 64 sectors from LBA 1, CHS=0,0,2 to address 0x0800:0
    mov ax, 0x0800
    mov es, ax              ;ES = 0x800

    mov ah, 0x2             ;read sectors
    mov al, 64              ;sectors to read
    mov ch, 0               ;cylinder idx
    mov dh, 0               ;head idx
    mov cl, 2               ;sector idx
    mov dl, [disk]          ;disk idx
    mov bx, 0               ;target pointer, ES:BX=0x0800:0x0000
    int 0x13

    ; Read 64 sectors from LBA 65, CHS=1,1,12 to address 0x1000:0
    mov ax, 0x1000
    mov es, ax              ;ES=0x1000

    mov ah, 0x2             ;read sectors
    mov al, 64              ;sectors to read
    mov ch, 1               ;cylinder idx
    mov dh, 1               ;head idx
    mov cl, 12              ;sector idx
    mov dl, [disk]          ;disk idx
    mov bx, 0x0000          ;target pointer, ES:BX=0x1000:0x0000
    int 0x13

    cli                     ; Disable the interrupts
    lgdt [gdt_pointer]      ; Load the gdt table
    mov eax, cr0            ; Init swap cr0...
    or eax,0x1              ; Set the protected mode bit on special CPU reg cr0
    mov cr0, eax
    jmp CODE_SEG:boot32     ; Long jump to the code segment


; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096
; if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
;  > 0 = segment grows up from base, 1 = segment grows down for a data segment
;  > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
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
gdt_end:
gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
disk:
    db 0x0

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

;; Magic numbers
times 510 - ($ - $$) db 0
dw 0xaa55

section .data
msg: db "Hello, World more than 512 bytes!", 0

bits 32
section .text.start
boot32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov esi, msg        ; SI now points to our message
    mov ebx, 0xb8000    ; vga memory position (0)

.loop:
    lodsb               ; Loads SI into AL and increments SI [next char]
    or al, al           ; Checks if the end of the string
    jz halt             ; Jump to halt if the end
    or eax,0x0200       ; The top byte defines the character colour in the buffer as
                        ; an int value from 0-15 with 0 = black, 1 = blue and 15 = white.
                        ; The bottom byte defines an ASCII code point
    mov word [ebx], ax
    add ebx, 2
    jmp .loop           ; Next iteration of the loop

halt:
    mov esp, kernel_stack_top
    extern __start
    extern __bss_start
    extern __bss_sizel

    ; Zero the BSS section
    mov ecx, __bss_sizel
    mov edi, __bss_start
    xor eax, eax
    rep stosd

    ; Call C entry point
    call __start
    cli
    hlt                 ; CPU command to halt the execution

section .bss
align 4
kernel_stack_bottom:
    resb 16384          ; 16 KB stack
kernel_stack_top:
OC:=objcopy
DD:=dd
ELF:=$(DEPLOY)/boot.elf
LDFLAGS:=-Wl,--build-id=none
all: $(DEPENDENCIES)
        mkdir -p $(DEPLOY)
        mkdir -p $(BUILD)
        $(NASM) $(SRC_NASM) -f elf32 -o $(OBJ_NASM)
        $(CC) $(SRC_C) $(OBJ_NASM) -o $(ELF) $(CFLAGS) -T $(LINKER) $(LDFLAGS)
        $(OC) -O binary $(ELF) $(BIN)
        $(DD) if=/dev/zero of=$(BIN).tmp count=1440 bs=1024
        $(DD) if=$(BIN) of=$(BIN).tmp conv=notrunc
        mv $(BIN).tmp $(BIN)
ENTRY(boot)
SECTIONS {
    . = 0x7c00;
    .boot :
    {
        *(.boot)
    }
    __kernel_start = .;
    __kernel_start_seg = __kernel_start >> 4;
    .text :
    {
        *(.text.start)
        *(.text*)
    }
    .rodata :
    {
        *(.rodata*)
    }
    .data :
    {
        *(.data)
    }
    /* Compute number of sectors that the kernel uses */
    __kernel_end = .;
    __kernel_size_sectors = (__kernel_end - __kernel_start + 511) / 512;

    .bss :
    {
        __bss_start = .;
        *(COMMON)
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
        /* Compute number of DWORDS that BSS section uses */
        __bss_sizel = (__bss_end - __bss_start) / 4;
    }
}
extern __kernel_size_sectors    ; Size of kernel in 512 byte sectors
extern __kernel_start_seg       ; Segment start of kernel will be laoded at

global boot

STAGE2_LBA_START equ 1          ; Logical Block Address(LBA) Stage2 starts on
                                ;     LBA 1 = sector after boot sector
                                ; Logical Block Address(LBA) Stage2 ends at
STAGE2_LBA_END   equ STAGE2_LBA_START + __kernel_size_sectors
DISK_RETRIES     equ 3          ; Number of times to retry on disk error

bits 16
section .boot

boot:
; Include a BPB (1.44MB floppy with FAT12) to be more compatible with USB floppy media
;%include "src/init/bpb.inc"

boot_start:
    xor ax, ax                  ; DS=SS=ES=0 for stage2 loading
    mov ds, ax
    mov ss, ax                  ; Stack at 0x0000:0x7c00
    mov sp, 0x7c00
    cld                         ; Set string instructions to use forward movement

    ; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
    mov [bootDevice], dl        ; Save boot drive
    mov di, __kernel_start_seg  ; DI = Current segment to read into
    mov si, STAGE2_LBA_START    ; SI = LBA that stage2 starts at
    jmp .chk_for_last_lba       ; Check to see if we are last sector in stage2

.read_sector_loop:
    mov bp, DISK_RETRIES        ; Set disk retry count

    call lba_to_chs             ; Convert current LBA to CHS
    mov es, di                  ; Set ES to current segment number to read into
    xor bx, bx                  ; Offset zero in segment

.retry:
    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                ;     AL = 1 = Sectors to read
    int 0x13                    ; BIOS Disk interrupt call
    jc .disk_error              ; If CF set then disk error

.success:
    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
    inc si                      ; Next LBA

.chk_for_last_lba:
    cmp si, STAGE2_LBA_END      ; Have we reached the last stage2 sector?
    jl .read_sector_loop        ;     If we haven't then read next sector

.stage2_loaded:
    jmp stage2                  ; Jump to second stage

.disk_error:
    xor ah, ah                  ; Int13h/AH=0 is drive reset
    int 0x13
    dec bp                      ; Decrease retry count
    jge .retry                  ; If retry count not exceeded then try again

error_end:
    ; Unrecoverable error; print drive error; enter infinite loop
    mov si, diskErrorMsg        ; Display disk error message
    call print_string
    cli
.error_loop:
    hlt
    jmp .error_loop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                     ; Preserve AX
    mov ax, si                  ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                  ; CL = S = LBA mod SPT
    inc cl                      ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
    or  cl, ah                  ;     upper 2 bits of Sector (CL)
    pop ax                      ; Restore scratch registers
    ret

; Uncomment these lines if not using a BPB (via bpb.inc)
%ifndef WITH_BPB
numHeads:        dw 2           ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif

bootDevice:      db 0x00
diskErrorMsg:    db "Unrecoverable disk error!", 0

; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db  0
dw 0xaa55

section .data
msg: db "Hello, World more than 512 bytes!", 0

; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096
; if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
;  > 0 = segment grows up from base, 1 = segment grows down for a data segment
;  > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
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
gdt_end:
gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
disk:
    db 0x0

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

bits 16
section .text.start
stage2:
    cli                         ; Disable the interrupts
    mov ax, 0x2401
    int 0x15                    ; Enable A20 bit

    lgdt [gdt_pointer]          ; Load the gdt table
    mov eax, cr0                ; Init swap cr0...
    or eax,0x1                  ; Set the protected mode bit on special CPU reg cr0
    mov cr0, eax
    jmp CODE_SEG:startpm        ; FAR JMP to the code segment

bits  32
startpm:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov esi, msg                ; SI now points to our message
    mov ebx, 0xb8000            ; vga memory position (0)

.loop:
    lodsb                       ; Loads SI into AL and increments SI [next char]
    or al, al                   ; Checks if the end of the string
    jz halt                     ; Jump to halt if the end
    or eax,0x0200               ; The top byte defines the character colour in the
                                ; buffer as an int value from 0-15 with 0 = black,
                                ; 1 = blue and 15 = white.
                                ; The bottom byte defines an ASCII code point
    mov word [ebx], ax
    add ebx, 2
    jmp .loop                   ; Next iteration of the loop

halt:
    mov esp, kernel_stack_top
    extern __start
    extern __bss_start
    extern __bss_sizel

    ; Zero the BSS section
    mov ecx, __bss_sizel
    mov edi, __bss_start
    xor eax, eax
    rep stosd

    ; Call C entry point
    call __start
    cli
    hlt                         ; CPU command to halt the execution

section .bss
align 4
kernel_stack_bottom:
    resb 16384                  ; 16 KB stack
kernel_stack_top:
; %include "src/init/bpb.inc"
C = LBA ÷ (HPC × SPT)
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1