* Added bootloader files * Changed to spaces from tabs
This commit is contained in:
parent
915330287f
commit
7678019e03
11 changed files with 1090 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -33,3 +33,7 @@
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
*.iso
|
*.iso
|
||||||
|
*.bin
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
.idea/
|
153
src/bootloader/2ndstage.asm
Normal file
153
src/bootloader/2ndstage.asm
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
; ------------------------------------------------------------
|
||||||
|
; Stage 2 of the bootloader
|
||||||
|
; ------------------------------------------------------------
|
||||||
|
%define boot_sector_location (0x7C03) ; The location of the boot sector
|
||||||
|
%define fat_segment (0x0050) ; The memory location to load the FAT into memory
|
||||||
|
%define stage_2_location (0x7E00) ; The location of the second stage bootloader
|
||||||
|
|
||||||
|
%define kernel_stack (0x9FBFF) ; The location of the start of the bottom of the stack
|
||||||
|
|
||||||
|
%define kernel_load_segment (0x3000) ; The segment when the kernel is loaded by the bootloader before A20 is enabled so can access above 1MB
|
||||||
|
%define kernel_load_location (0x30000) ; The location when the kernel is loaded by the bootloader before A20 is enabled so can access above 1MB
|
||||||
|
%define kernel_target_location (0x100000) ; The target location for the kernel to be loaded. Above 1MB.
|
||||||
|
|
||||||
|
%define memory_map_location (0x20000) ; This is where the memory map is loaded into. At bottom of 2nd stage bootloader
|
||||||
|
%define memory_map_segment (0x02000)
|
||||||
|
|
||||||
|
%define boot_params_location (0x7000) ; The location where the boot parameters are save to for the kernel
|
||||||
|
%define SIGNATURE (0x8A3C) ; The signature of the parameters to test for valid parameters
|
||||||
|
|
||||||
|
[bits 16] ; Tell the assembler that this is a 16bit program not 32bit
|
||||||
|
[org stage_2_location]
|
||||||
|
|
||||||
|
jmp stage_2_bootload_start
|
||||||
|
|
||||||
|
times (3 - ($ - $$)) db 0
|
||||||
|
|
||||||
|
boot_sector:
|
||||||
|
%include 'fat_descripter.asm'
|
||||||
|
|
||||||
|
; Macros to make code more readable. This doesn't take up memory here as they are copied where they are used
|
||||||
|
|
||||||
|
%include 'macros.asm'
|
||||||
|
%include 'descriptors_macros.asm'
|
||||||
|
%include 'enabling_a20.asm'
|
||||||
|
%include 'memory.asm'
|
||||||
|
|
||||||
|
stage_2_bootload_start:
|
||||||
|
cli
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
sti
|
||||||
|
|
||||||
|
; Copy the boot sector
|
||||||
|
m_copy_boot_sector
|
||||||
|
|
||||||
|
; Find the kernel file
|
||||||
|
m_find_file kernel_filename, kernel_load_segment
|
||||||
|
|
||||||
|
; Read the kernel into the load segment
|
||||||
|
m_read_file kernel_load_segment, fat_segment
|
||||||
|
|
||||||
|
; Save The size read, stored in BX, may change to number of sectors as kernel size gets bigger
|
||||||
|
; kernel_size is in bytes
|
||||||
|
mov word [kernel_size], bx
|
||||||
|
mov word [boot_parameters.kernel_len], bx
|
||||||
|
|
||||||
|
; Reset the disk
|
||||||
|
m_reset_disk
|
||||||
|
|
||||||
|
; Write the loading message for the second stage
|
||||||
|
m_write_line loading_msg
|
||||||
|
|
||||||
|
; Save the the boot parameters so the kernel can access them
|
||||||
|
m_save_cursor
|
||||||
|
|
||||||
|
; Enable the a20 line
|
||||||
|
m_enable_a20
|
||||||
|
|
||||||
|
; Read the size of the memory and store at 'boot_info'
|
||||||
|
m_get_memory_size
|
||||||
|
|
||||||
|
; Set up and write into memory the interrupt descriptor table
|
||||||
|
m_setup_idt
|
||||||
|
|
||||||
|
; Set up and write into memory the global descriptor table
|
||||||
|
m_setup_gdt
|
||||||
|
|
||||||
|
; Load the tables
|
||||||
|
m_load_gdt_and_idt
|
||||||
|
|
||||||
|
; Enable protected mode
|
||||||
|
m_enable_protected
|
||||||
|
|
||||||
|
jmp 0x08:stage_2_bootloader_32 ; Set CS to the code segment of the kernel in the GDT
|
||||||
|
|
||||||
|
; If there is a floppy disk error or a boot error, the call this function
|
||||||
|
boot_error:
|
||||||
|
m_reboot_with_msg disk_error_msg ; Print the disk error message
|
||||||
|
; Reboot
|
||||||
|
|
||||||
|
%include 'functions.asm'
|
||||||
|
|
||||||
|
idt_descriptor:
|
||||||
|
dw 0x0000 ; 256 entries of 8 bytes for the interrupt table
|
||||||
|
dd 0x0000 ; The location of the table, at 0x0000:0x0000
|
||||||
|
|
||||||
|
gdt_descriptor:
|
||||||
|
dw 0x0017 ; 3 tables of 8 bytes total (minus 1)
|
||||||
|
dd 0x0800 ; The location of the 3 tables, at 0x0000:0x0800, just bellow the IDT
|
||||||
|
|
||||||
|
kernel_filename db "KERNEL BIN", 0
|
||||||
|
disk_error_msg db "Disk error", 0
|
||||||
|
loading_msg db "Loading: 2nd stage bootloader", 0
|
||||||
|
reboot_msg db "Press any key to reboot", 0
|
||||||
|
a20_error db "a20 line not initialised", 0
|
||||||
|
|
||||||
|
loading_kernel db "Loading: Kernel", 0x0A, 0
|
||||||
|
|
||||||
|
memory_map_error db "Error getting memory map from BIOS INT 0x15 0xE820", 0
|
||||||
|
|
||||||
|
; Data storage
|
||||||
|
root_sectors db 0,0
|
||||||
|
root_start db 0,0
|
||||||
|
file_start db 0,0
|
||||||
|
|
||||||
|
kernel_size db 0,0
|
||||||
|
|
||||||
|
; Now in 32bit mode
|
||||||
|
[bits 32]
|
||||||
|
|
||||||
|
stage_2_bootloader_32:
|
||||||
|
m_set_up_segments
|
||||||
|
|
||||||
|
lea esi, [loading_kernel]
|
||||||
|
call print_string_32
|
||||||
|
|
||||||
|
; Move kernel to target location
|
||||||
|
mov esi, kernel_load_location
|
||||||
|
mov edi, kernel_target_location
|
||||||
|
xor ecx, ecx ; Zero out ECX for the kernel size
|
||||||
|
mov cx, word [kernel_size]
|
||||||
|
shr cx, 2 ; Divide by 4 as now copying 4 bytes at a time
|
||||||
|
cld
|
||||||
|
rep movsd
|
||||||
|
|
||||||
|
jmp kernel_target_location ; Jump to the kernel, shouldn't return
|
||||||
|
|
||||||
|
%include '32bit_functions.asm'
|
||||||
|
|
||||||
|
times (3 * 512) - ($ - $$) db 0
|
||||||
|
|
||||||
|
[absolute boot_params_location]
|
||||||
|
|
||||||
|
boot_parameters:
|
||||||
|
.signature resw 1
|
||||||
|
.cursor_pos_x resb 1
|
||||||
|
.cursor_pos_y resb 1
|
||||||
|
.memory_lower resw 1
|
||||||
|
.memory_upper resw 1
|
||||||
|
.memory_map_address resd 1
|
||||||
|
.memory_map_length resw 1
|
||||||
|
.kernel_len resd 1
|
102
src/bootloader/32bit_functions.asm
Normal file
102
src/bootloader/32bit_functions.asm
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
[bits 32]
|
||||||
|
|
||||||
|
%define VIDMEM 0xB8000 ; Video memory mapped
|
||||||
|
%define COLUMNS 80 ; Height of screen
|
||||||
|
%define ROWS 25 ; Width of screen
|
||||||
|
%define CHAR_ATTRIB 0x02 ; Character attribute
|
||||||
|
|
||||||
|
; Prints a character
|
||||||
|
; Input:
|
||||||
|
; BL - The character to print
|
||||||
|
print_char_32:
|
||||||
|
pusha
|
||||||
|
mov edi, VIDMEM ; Let EDI point to the start of video memory
|
||||||
|
xor eax, eax ; Clear EAX
|
||||||
|
|
||||||
|
mov ecx, COLUMNS
|
||||||
|
mov al, byte [boot_parameters.cursor_pos_y] ; Get the y position
|
||||||
|
mul ecx ; Multiply by the number of columns (EAX = y * COLUMNS)
|
||||||
|
|
||||||
|
xor ecx, ecx
|
||||||
|
mov cl, byte [boot_parameters.cursor_pos_x] ; Get the x position
|
||||||
|
add eax, ecx ; Add the x position to (y * COLUMS)
|
||||||
|
shl eax, 1 ; Multiply by 2 as 2 bytes per character
|
||||||
|
|
||||||
|
add edi, eax ; Add the offset to the video memory address
|
||||||
|
|
||||||
|
cmp bl, 0x0A ; Is it a new line
|
||||||
|
je short .new_line
|
||||||
|
|
||||||
|
mov dl, bl ; Get the character to print
|
||||||
|
mov dh, CHAR_ATTRIB ; Add the character attribute
|
||||||
|
mov word [edi], dx ; Write to the video memory
|
||||||
|
|
||||||
|
inc byte [boot_parameters.cursor_pos_x] ; Increment the x position
|
||||||
|
jmp short .print_done
|
||||||
|
|
||||||
|
.new_line:
|
||||||
|
mov byte [boot_parameters.cursor_pos_x], 0 ; Set cursor to beginning of line
|
||||||
|
inc byte [boot_parameters.cursor_pos_y] ; Increment new line
|
||||||
|
|
||||||
|
.print_done:
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Print a null terminated string to the screen
|
||||||
|
; Input:
|
||||||
|
; ESI - Pointer to the string to print
|
||||||
|
print_string_32:
|
||||||
|
pusha
|
||||||
|
|
||||||
|
.print_loop:
|
||||||
|
mov bl, byte [esi] ; Get the character to print
|
||||||
|
cmp bl, 0 ; Is it null
|
||||||
|
je short .print_end ; Then finish
|
||||||
|
|
||||||
|
call print_char_32 ; Print the character
|
||||||
|
|
||||||
|
inc esi ; Increment to next character
|
||||||
|
jmp short .print_loop
|
||||||
|
|
||||||
|
.print_end:
|
||||||
|
mov bh, byte [boot_parameters.cursor_pos_y]
|
||||||
|
mov bl, byte [boot_parameters.cursor_pos_x]
|
||||||
|
call update_cursor ; Update the cursor
|
||||||
|
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Update the cursors new position
|
||||||
|
; Input:
|
||||||
|
; BH - y position
|
||||||
|
; BL - x position
|
||||||
|
update_cursor:
|
||||||
|
pusha
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
mov ecx, COLUMNS
|
||||||
|
mov al, bh
|
||||||
|
mul ecx
|
||||||
|
add al, bl
|
||||||
|
mov ebx, eax
|
||||||
|
|
||||||
|
mov al, 0x0f
|
||||||
|
mov dx, 0x03D4
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov al, bl
|
||||||
|
mov dx, 0x03D5
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
|
||||||
|
mov al, 0x0e
|
||||||
|
mov dx, 0x03D4
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
mov al, bh
|
||||||
|
mov dx, 0x03D5
|
||||||
|
out dx, al
|
||||||
|
|
||||||
|
popa
|
||||||
|
ret
|
29
src/bootloader/Makefile
Normal file
29
src/bootloader/Makefile
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BIN = ../../bin/boot
|
||||||
|
|
||||||
|
INCLUDES = ./
|
||||||
|
|
||||||
|
1stSTAGE_SOURCE = bootloader.asm
|
||||||
|
1stSTAGE_BINARY = bootloader.bin
|
||||||
|
|
||||||
|
2ndSTAGE_SOURCE = 2ndstage.asm
|
||||||
|
2ndSTAGE_BINARY = 2ndstage.bin
|
||||||
|
|
||||||
|
ASM = nasm
|
||||||
|
ASMFLAGS = -f bin
|
||||||
|
|
||||||
|
all: clean $(BIN)/$(2ndSTAGE_BINARY) $(BIN)/$(1stSTAGE_BINARY) | $(BIN)
|
||||||
|
|
||||||
|
$(BIN):
|
||||||
|
mkdir -p $(BIN)
|
||||||
|
|
||||||
|
$(BIN)/$(2ndSTAGE_BINARY): $(2ndSTAGE_SOURCE) | $(BIN)
|
||||||
|
@echo "\nMaking 2nd stage bootloader\n"
|
||||||
|
$(ASM) $< -o $@ $(ASMFLAGS)
|
||||||
|
|
||||||
|
$(BIN)/$(1stSTAGE_BINARY): $(1stSTAGE_SOURCE) | $(BIN)
|
||||||
|
@echo "\nMaking bootloader\n"
|
||||||
|
$(ASM) $< -o $@ $(ASMFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "\nCleanning the workspace\n"
|
||||||
|
rm -f $(BIN)/*.bin
|
109
src/bootloader/bootloader.asm
Normal file
109
src/bootloader/bootloader.asm
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
; ---------------------------------------------------------------------
|
||||||
|
; Here is some information for you so can understand the whole thing :)
|
||||||
|
; Using a 1440KB 3.5" Floppy for the bootloader
|
||||||
|
; ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
%define boot0_location (0x7C00) ; The location that BOOT0 is load to by the BIOS
|
||||||
|
%define boot_signature (0xAA55) ; The boot signature that is needed at the end of the 512 bytes so that it is recognized as a boot device.
|
||||||
|
%define fat_segment (0x0050) ; The memory location to load the FAT into memory
|
||||||
|
;%define stage_2_load_segment (0x0200) ; The location of the second stage bootloader
|
||||||
|
%define stage_2_load_segment (0x07E0)
|
||||||
|
|
||||||
|
[bits 16] ; Tell the assembler that this is a 16bit program not 32bit
|
||||||
|
[org boot0_location] ; As the bootloader is loaded at 0x7C00, all addressing will be relative to this location
|
||||||
|
|
||||||
|
; Will need to jump over the FAT16 header. 3 Bytes are allowed before the header
|
||||||
|
; so can only use a relative/short jump.
|
||||||
|
jmp short bootloader_start
|
||||||
|
|
||||||
|
; Need to pad to a total of 3 bytes so far.
|
||||||
|
; This is so to comply with the FAT16 header.
|
||||||
|
; We could use a NOP instruction as the previous instruction (JMP) is 2 bytes and NOP is 1 byte
|
||||||
|
; See https://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html
|
||||||
|
; Bytes 0-2
|
||||||
|
times (3 - ($ - $$)) db 0
|
||||||
|
|
||||||
|
%include 'fat_descripter.asm'
|
||||||
|
|
||||||
|
; Bytes 62-509
|
||||||
|
|
||||||
|
%include 'macros.asm'
|
||||||
|
|
||||||
|
bootloader_start:
|
||||||
|
; The BIOS can load us (the bootloader) at address 0x000:7C00 or 0x7C00:0000
|
||||||
|
; This is in fact the same address
|
||||||
|
; So we need to normalise this by using a long jump
|
||||||
|
jmp long 0x0000:start_boot0_16bit
|
||||||
|
|
||||||
|
start_boot0_16bit:
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Set up the memory segment for accessing memory the old way.
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
cli ; Disable interrupts so not mess up the declarations of the segments
|
||||||
|
|
||||||
|
mov byte [Logical_drive_number], dl ; Save what drive we booted from (should be 0x00) into our boot parameter block above
|
||||||
|
|
||||||
|
mov ax, cs ; Set all the sectors to start to begin with. Can get this from the code segment. Should be 0x00
|
||||||
|
mov ds, ax ; Set the data segment at the beginning of the bootloader location
|
||||||
|
mov es, ax ; Set the extra1 segment at the beginning of the bootloader location
|
||||||
|
mov ss, ax ; Set the stack segment at the beginning of the bootloader location
|
||||||
|
|
||||||
|
mov sp, boot0_location ; Set the stack pointer to the bootloader and grows down to 0x0.
|
||||||
|
|
||||||
|
sti ; Enable interrupts
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Finished setting up the memory segment for accessing memory the old way.
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
; Reset the floppy disk
|
||||||
|
; Now need to reset the floppy drive so that we can get information from it
|
||||||
|
m_reset_disk
|
||||||
|
|
||||||
|
; Print the loading message
|
||||||
|
m_write_line loading_msg
|
||||||
|
|
||||||
|
; Find the 2ndstage bootloader in the root directory
|
||||||
|
m_find_file stage_2_filename, stage_2_load_segment
|
||||||
|
|
||||||
|
; Load the FAT table into memory
|
||||||
|
m_read_fat fat_segment
|
||||||
|
|
||||||
|
; Read the 2ndstage bootloader into memory
|
||||||
|
m_read_file stage_2_load_segment, fat_segment
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
; Start the second stage bootloader
|
||||||
|
; ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
; Jump to second stage start of code:
|
||||||
|
jmp long stage_2_load_segment:0000h
|
||||||
|
|
||||||
|
|
||||||
|
; If there is a floppy disk error or a boot error, the call this function
|
||||||
|
boot_error:
|
||||||
|
m_reboot_with_msg disk_error_msg ; Print the disk error message
|
||||||
|
; Reboot
|
||||||
|
|
||||||
|
; Include the functions that can be called
|
||||||
|
%include 'functions.asm'
|
||||||
|
|
||||||
|
; Messages
|
||||||
|
disk_error_msg db "Disk error", 0
|
||||||
|
loading_msg db "Loading: 1st stage bootloader", 0
|
||||||
|
reboot_msg db "Press any key to reboot", 0
|
||||||
|
|
||||||
|
; Stage 2 bootloader file name to find
|
||||||
|
stage_2_filename db "2NDSTAGEBIN", 0
|
||||||
|
|
||||||
|
; Data storage
|
||||||
|
root_sectors db 0,0
|
||||||
|
root_start db 0,0
|
||||||
|
file_start db 0,0
|
||||||
|
|
||||||
|
; Pad the rest of the file with zeros
|
||||||
|
; Bytes 510-511
|
||||||
|
times 510 - ($ - $$) db 0
|
||||||
|
; Add the boot signature at the end
|
||||||
|
dw boot_signature
|
47
src/bootloader/descriptors_macros.asm
Normal file
47
src/bootloader/descriptors_macros.asm
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
; Set up the interrupt descriptor table
|
||||||
|
; This is to be stored at 0x0000:0x0000
|
||||||
|
%macro m_setup_idt 0
|
||||||
|
push es
|
||||||
|
push di
|
||||||
|
xor ax, ax
|
||||||
|
mov es, ax
|
||||||
|
mov di, 0x0000
|
||||||
|
mov cx, 2048 ; Write 2048 byte of zero so now into the IDT
|
||||||
|
cld
|
||||||
|
rep stosb
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Set up the global descriptor table
|
||||||
|
; This is to be stored at 0x0000:0x0800
|
||||||
|
%macro m_setup_gdt 0
|
||||||
|
|
||||||
|
; NULL Descriptor:
|
||||||
|
mov cx, 4 ; Write the NULL descriptor,
|
||||||
|
rep stosw ; which is 4 zero-words.
|
||||||
|
|
||||||
|
; Code segment descriptor:
|
||||||
|
mov [es:di], word 0xFFFF ; limit = 0xFFFF (since granularity bit is set, this is 4 GB)
|
||||||
|
mov [es:di + 2], word 0x0000 ; base = 0x0000
|
||||||
|
mov [es:di + 4], byte 0x00 ; base
|
||||||
|
mov [es:di + 5], byte 0x9A ; access = 1001 1010; segment present, ring 0, S=code/data, type=0xA (code, execute/read)
|
||||||
|
mov [es:di + 6], byte 0xCF ; granularity = 1100 1111; limit = 0xf, AVL=0, L=0, 32bit, G=1
|
||||||
|
mov [es:di + 7], byte 0x00 ; base
|
||||||
|
add di, 8
|
||||||
|
|
||||||
|
; Data segment descriptor:
|
||||||
|
mov [es:di], word 0xFFFF ; limit = 0xFFFF (since granularity bit is set, this is 4 GB)
|
||||||
|
mov [es:di + 2], word 0x0000 ; base = 0x0000
|
||||||
|
mov [es:di + 4], byte 0x00 ; base
|
||||||
|
mov [es:di + 5], byte 0x92 ; access = 1001 0010; segment present, ring 0, S=code/data, type=0x2 (data, read/write)
|
||||||
|
mov [es:di + 6], byte 0xCF ; granularity = 1100 1111; limit = 0xf, AVL=0, L=0, 32bit, G=1
|
||||||
|
mov [es:di + 7], byte 0x00 ; base
|
||||||
|
pop di
|
||||||
|
pop es
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Tell the CPU of the GDT and IDT
|
||||||
|
%macro m_load_gdt_and_idt 0
|
||||||
|
cli
|
||||||
|
lgdt [gdt_descriptor]
|
||||||
|
lidt [idt_descriptor]
|
||||||
|
%endmacro
|
161
src/bootloader/enabling_a20.asm
Normal file
161
src/bootloader/enabling_a20.asm
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
; Purpose: To check the status of the a20 line in a completely self-contained state-preserving way.
|
||||||
|
; The function can be modified as necessary by removing pushes at the beginning and their
|
||||||
|
; respective pop's at the end if complete self-containment is not required.
|
||||||
|
;
|
||||||
|
; Returns: 0 in AX if the a20 line is disabled (memory wraps around)
|
||||||
|
; 1 in AX if the a20 line is enabled (memory does not wrap around)
|
||||||
|
test_a20:
|
||||||
|
pushf
|
||||||
|
push ds
|
||||||
|
push es
|
||||||
|
push di
|
||||||
|
push si
|
||||||
|
|
||||||
|
cli
|
||||||
|
|
||||||
|
xor ax, ax
|
||||||
|
mov es, ax
|
||||||
|
|
||||||
|
mov ax, 0xFFFF
|
||||||
|
mov ds, ax
|
||||||
|
|
||||||
|
mov di, 0x0500
|
||||||
|
mov si, 0x0510
|
||||||
|
|
||||||
|
mov al, byte [es:di]
|
||||||
|
push ax
|
||||||
|
|
||||||
|
mov al, byte [ds:si]
|
||||||
|
push ax
|
||||||
|
|
||||||
|
mov byte [es:di], 0x00
|
||||||
|
mov byte [ds:si], 0xFF
|
||||||
|
|
||||||
|
cmp byte [es:di], 0xFF
|
||||||
|
|
||||||
|
pop ax
|
||||||
|
mov byte [ds:si], al
|
||||||
|
|
||||||
|
pop ax
|
||||||
|
mov byte [es:di], al
|
||||||
|
|
||||||
|
mov ax, 0
|
||||||
|
je .a20_enabled
|
||||||
|
|
||||||
|
mov ax, 1
|
||||||
|
|
||||||
|
.a20_enabled:
|
||||||
|
pop si
|
||||||
|
pop di
|
||||||
|
pop es
|
||||||
|
pop ds
|
||||||
|
popf
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Try to enable the a20 line using the BIOS, need to test after this
|
||||||
|
%macro m_enable_a20_via_bios 0
|
||||||
|
pusha
|
||||||
|
mov ax, 0x2403 ; Test if BIOS supports enabling a20 line
|
||||||
|
int 0x15
|
||||||
|
jb short .m_enable_a20_via_bios_done
|
||||||
|
cmp ah, 0
|
||||||
|
jnz short .m_enable_a20_via_bios_done
|
||||||
|
|
||||||
|
mov ax, 0x2402 ; Test the status of the a20 line
|
||||||
|
int 0x15
|
||||||
|
jb short .m_enable_a20_via_bios_done
|
||||||
|
cmp ah, 0
|
||||||
|
jnz short .m_enable_a20_via_bios_done
|
||||||
|
cmp al, 1
|
||||||
|
jz short .m_enable_a20_via_bios_done ; Already enabled
|
||||||
|
|
||||||
|
mov ax, 0x2401 ; Enable the a20 line
|
||||||
|
int 0x15
|
||||||
|
.m_enable_a20_via_bios_done:
|
||||||
|
popa
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Can enable the a20 line by using the keyboard
|
||||||
|
a20_wait_command: ; But need to check if the keyboard is ready to receive commands
|
||||||
|
in al, 0x64
|
||||||
|
test al, 0x02
|
||||||
|
jnz a20_wait_command
|
||||||
|
ret
|
||||||
|
|
||||||
|
a20_wait_data: ; But need to check if the keyboard is ready to receive data
|
||||||
|
in al, 0x64
|
||||||
|
test al, 0x01
|
||||||
|
jz a20_wait_data
|
||||||
|
ret
|
||||||
|
|
||||||
|
%macro m_enable_a20_via_keyboard 0
|
||||||
|
cli
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
mov al, 0xAD ; Disable keyboard
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
mov al, 0xD0 ; Send command 0xd0 (read from input)
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call a20_wait_data
|
||||||
|
in al, 0x60 ; Read input
|
||||||
|
push eax ; Save input
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
mov al, 0xD1 ; Send command 0xd1 (Write to output)
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
pop eax ; Write input back
|
||||||
|
or al, 2
|
||||||
|
out 0x60, al
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
mov al, 0xAE ; Enable keyboard
|
||||||
|
out 0x64, al
|
||||||
|
|
||||||
|
call a20_wait_command
|
||||||
|
sti
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
%macro m_enable_a20_fast 0
|
||||||
|
in al, 0x92
|
||||||
|
test al, 0x02
|
||||||
|
jnz short .m_enable_a20_fast_done
|
||||||
|
|
||||||
|
or al, 0x02
|
||||||
|
and al, 0xFE
|
||||||
|
out 0x92, al
|
||||||
|
.m_enable_a20_fast_done:
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
%macro m_enable_a20 0
|
||||||
|
call test_a20
|
||||||
|
cmp ax, 0
|
||||||
|
jne .a20_enabled
|
||||||
|
|
||||||
|
m_enable_a20_via_keyboard
|
||||||
|
|
||||||
|
call test_a20
|
||||||
|
cmp ax, 0
|
||||||
|
jne .a20_enabled
|
||||||
|
|
||||||
|
m_enable_a20_via_bios
|
||||||
|
|
||||||
|
call test_a20
|
||||||
|
cmp ax, 0
|
||||||
|
jne .a20_enabled
|
||||||
|
|
||||||
|
m_enable_a20_fast
|
||||||
|
|
||||||
|
call test_a20
|
||||||
|
cmp ax, 0
|
||||||
|
jne .a20_enabled
|
||||||
|
|
||||||
|
.a20_enabled_failed:
|
||||||
|
m_reboot_with_msg a20_error
|
||||||
|
|
||||||
|
.a20_enabled:
|
||||||
|
%endmacro
|
30
src/bootloader/fat_descripter.asm
Normal file
30
src/bootloader/fat_descripter.asm
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
; ------------------------------------------
|
||||||
|
; This will be where the floppy FAT12 header
|
||||||
|
; Bytes 3-61
|
||||||
|
; https://technet.microsoft.com/en-us/library/cc976796.aspx
|
||||||
|
; Values are those used by IBM for 1.44 MB, 3.5" diskette
|
||||||
|
; ------------------------------------------
|
||||||
|
|
||||||
|
OEM_name db "DeanOS " ; Bytes 03-10 - OEM name (Original Equipment Manufacturer) The name for the bootloader/OS
|
||||||
|
Bytes_per_sector dw 512 ; Bytes 11-12 - Number of bytes per sector (usually 512 bytes)
|
||||||
|
Sectors_per_cluster db 1 ; Bytes 13 - Number of sectors per cluster, is 1 because in FAT12 a cluster is the same as a sector
|
||||||
|
Reserved_sectors dw 1 ; Bytes 14-15 - For FAT12 is 1
|
||||||
|
FAT_tables db 2 ; Bytes 16 - Number of FAT tables (usually 2)
|
||||||
|
Root_directory_size dw 224 ; Bytes 17-18 - Size of root directory entries 224 for FAT12
|
||||||
|
Sectors_in_filesystem dw 2880 ; Bytes 19-20 - Total number of sectors in the file system (usually 2880)
|
||||||
|
Media_descriptor_type db 0xF0 ; Bytes 21 - Media descriptor: 3.5" floppy 1440KB
|
||||||
|
Sectors_per_FAT dw 9 ; Bytes 22-23 - Number of sectors per FAT is 9
|
||||||
|
Sectors_per_track dw 18 ; Bytes 24-25 - Number of sectors per track is 12 but found to be 9
|
||||||
|
Head_count dw 2 ; Bytes 26-27 - Number of heads/sides of the floppy (usually 2)
|
||||||
|
Hidden_sectors dd 0 ; Bytes 28-31 - Number of hidden sectors (usually 0)
|
||||||
|
Total_sectors dd 0 ; Bytes 32-35 - Total number of sectors in file system
|
||||||
|
Logical_drive_number db 0 ; Bytes 36 - Logical drive number (0)
|
||||||
|
Reserved db 0 ; Bytes 37 - Reserved sectors
|
||||||
|
Extended_signature db 0x29 ; Bytes 38 - Indicates that there 3 more fields
|
||||||
|
Serial_number dd 0xA1B2C3D4 ; Bytes 39-42 - Serial number, can be anything
|
||||||
|
Volume_lable db "OS bootdisk" ; Bytes 43-53 - Name of the volume, 11 characters
|
||||||
|
Filesystem_type db "FAT12 " ; Bytes 54-61 - File system type (FAT12), 8 characters
|
||||||
|
|
||||||
|
; ------------------------------------------
|
||||||
|
; End of FAT12 header
|
||||||
|
; ------------------------------------------
|
103
src/bootloader/functions.asm
Normal file
103
src/bootloader/functions.asm
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
; Print a null terminated string to the screen
|
||||||
|
; DS:SI is the location of the string
|
||||||
|
; Input:
|
||||||
|
; SI - pointer to the string to be printed in register SI
|
||||||
|
print_string_with_new_line:
|
||||||
|
pusha ; Push all registers onto the stack
|
||||||
|
mov ah, 0x0E ; Specify the teletype output function
|
||||||
|
xor bx, bx
|
||||||
|
.loop:
|
||||||
|
lodsb ; Load byte at address SI into AL and increment SI
|
||||||
|
cmp al, 0 ; If it the end of the null-terminated string
|
||||||
|
je .done ; Then exit
|
||||||
|
int 0x10 ; Else print the character in AL as an interrupt into the BIOS
|
||||||
|
jmp short .loop ; Repeat for next character
|
||||||
|
.done:
|
||||||
|
; Print the line feed and carriage return
|
||||||
|
mov al, 0x0A ; Teletype print sub function(0x0E), Line feed (0x0A)
|
||||||
|
int 0x10
|
||||||
|
mov al, 0x0D ; Carriage return
|
||||||
|
int 0x10
|
||||||
|
popa ; Pop the register of the stack
|
||||||
|
ret ; And return to caller
|
||||||
|
|
||||||
|
; Reboot the computer if there was an error
|
||||||
|
reboot:
|
||||||
|
m_write_line reboot_msg
|
||||||
|
|
||||||
|
xor ah, ah ; Sub function for reading a character
|
||||||
|
int 0x16 ; Wait for key press
|
||||||
|
int 0x19 ; Warm reboot
|
||||||
|
|
||||||
|
cli ; If failed to reboot, halt
|
||||||
|
hlt ; Halt
|
||||||
|
|
||||||
|
; Read a sector from the disk
|
||||||
|
; ES:BX is the location of the buffer that the data is read into
|
||||||
|
; As reads often fail, it will try 4 times to read from the disk. The counter is stored in CX.
|
||||||
|
; With the data buffer at ES:BX
|
||||||
|
; Input:
|
||||||
|
; AX - The logical block address (LBA)
|
||||||
|
; ES:BX - The buffer location which the sector will be read into
|
||||||
|
read_sector:
|
||||||
|
xor cx, cx ; Set the counter to 0
|
||||||
|
.read:
|
||||||
|
push ax ; Save the logical block address
|
||||||
|
push cx ; Save the counter
|
||||||
|
|
||||||
|
; Convert the logical block address into the head-cylinder/track-sector values
|
||||||
|
; The conversions are:
|
||||||
|
; (1) Sector = (LBA mod SectorsPerTrack) + 1
|
||||||
|
; (2) Cylinder = (LBA / SectorsPerTrack) / NumHeads
|
||||||
|
; (3) Head = (LBA / SectorsPerTrack) mod NumHeads
|
||||||
|
;
|
||||||
|
; Input:
|
||||||
|
; AX - the logical block address
|
||||||
|
; Output: These are used for the 0x13 BIOS interrupt to read from the disk along with ES:BX and ax
|
||||||
|
; CH - Lower 8 bits of cylinder
|
||||||
|
; CL - Upper 2 bits of cylinder and 6 bits for the sector
|
||||||
|
; DH - The head number
|
||||||
|
; DL - The drive number/ID
|
||||||
|
.lba_to_hcs:
|
||||||
|
push bx ; Save the buffer location
|
||||||
|
|
||||||
|
;mov bx, word [Sectors_per_track] ; Get the sectors per track
|
||||||
|
xor dx, dx ; Set DX to 0x0 (part of operand for DIV instruction and needs to be 0x00)
|
||||||
|
div word [Sectors_per_track] ; Divide (DX:AX / Sectors_per_track)
|
||||||
|
; Quotient (AX) - LBA / SectorsPerTrack
|
||||||
|
; Remainder (DX) - LBA mod SectorsPerTrack
|
||||||
|
|
||||||
|
inc dx ; (1) Sector = (LBA mod SectorsPerTrack) + 1
|
||||||
|
mov cl, dl ; Store sector in cl as defined for the output and for the 0x13 BIOS interrupt
|
||||||
|
|
||||||
|
;mov bx, word [Head_count] ; Get the number of heads
|
||||||
|
xor dx, dx
|
||||||
|
div word [Head_count] ; Quotient (AX) - Cylinder = (LBA / SectorsPerTrack) / NumHeads
|
||||||
|
; Remainder (DX) - Head = (LBA / SectorsPerTrack) mod NumHeads
|
||||||
|
|
||||||
|
mov ch, al ; Store cylinder in ch as defined for the output and for the 0x13 BIOS interrupt
|
||||||
|
mov dh, dl ; Store head in DH as defined for the output and for the 0x13 BIOS interrupt
|
||||||
|
|
||||||
|
mov dl, byte [Logical_drive_number] ; Store drive number in DL as defined for the output and for the 0x13 BIOS interrupt
|
||||||
|
|
||||||
|
pop bx ; Restore the buffer location
|
||||||
|
|
||||||
|
; Using the values above, read off the drive
|
||||||
|
|
||||||
|
mov ax, 0x0201 ; Sub function 2 to read from the disk, Read 1 (0x01) sector
|
||||||
|
int 0x13 ; Call BIOS interrupt 13h
|
||||||
|
jc short .read_fail ; If fails to read (carry bit set)
|
||||||
|
|
||||||
|
pop cx
|
||||||
|
pop ax ; Restore the logical block address
|
||||||
|
ret ; If read successful, then return to caller
|
||||||
|
.read_fail: ; If failed to read, try again, if tried 4 times, the reboot
|
||||||
|
pop cx ; Restore the counter
|
||||||
|
inc cx ; Increment the counter
|
||||||
|
cmp cx, 4 ; Compare if counter is equal to 4
|
||||||
|
je boot_error ; If equal, then error reading 4 times so reboot
|
||||||
|
xor ah, ah ; Reset the disk to try again
|
||||||
|
int 0x13
|
||||||
|
|
||||||
|
pop ax ; Restore the logical block address
|
||||||
|
jmp .read ; Try to read again
|
235
src/bootloader/macros.asm
Normal file
235
src/bootloader/macros.asm
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
; The reboot macro
|
||||||
|
%macro m_reboot_with_msg 1
|
||||||
|
m_write_line %1
|
||||||
|
call reboot
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; m_write_line str_to_write
|
||||||
|
%macro m_write_line 1
|
||||||
|
lea si, [%1]
|
||||||
|
call print_string_with_new_line
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
%macro m_reset_disk 0
|
||||||
|
mov dl, byte [Logical_drive_number] ; The drive to reset
|
||||||
|
xor ah, ah ; The sub function to reset the floppy (0)
|
||||||
|
int 0x13 ; Call BIOS interrupt 13h
|
||||||
|
jc boot_error ; If there was an error resetting the floppy, then the carry bit will be set
|
||||||
|
; If so, jump to failure function and reboot
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Fine a file on the disk
|
||||||
|
; m_find_file filename, load_segment
|
||||||
|
%macro m_find_file 2
|
||||||
|
mov ax, %2
|
||||||
|
mov es, ax
|
||||||
|
; Calculate the number of sectors for the root directory
|
||||||
|
; root_sectors = (root_size * 32) / 512
|
||||||
|
mov ax, 32
|
||||||
|
xor dx, dx ; Must be 0 initially for the multiplication
|
||||||
|
mul word [Root_directory_size]
|
||||||
|
div word [Bytes_per_sector]
|
||||||
|
mov cx, ax ; The number of root entries to read in CX for the loop
|
||||||
|
mov word [root_sectors], ax ; Save this value
|
||||||
|
|
||||||
|
; Calculate the start of the root directory
|
||||||
|
; root_start = (FAT tables) * (sectors per FAT) + (reserved sectors) + (hidden sectors)
|
||||||
|
xor ax, ax
|
||||||
|
mov al, byte [FAT_tables]
|
||||||
|
mul word [Sectors_per_FAT]
|
||||||
|
add ax, word [Hidden_sectors] ; Add the top half of hidden sectors
|
||||||
|
adc ax, word [Hidden_sectors + 2] ; Add the bottom half with carry of hidden sectors
|
||||||
|
add ax, word [Reserved_sectors]
|
||||||
|
mov word [root_start], ax ; Save this value
|
||||||
|
|
||||||
|
.read_next_sector: ; Read a sector from the root directory
|
||||||
|
; If the reading fails, will reboot
|
||||||
|
; CX is a loop counter for how many sector to read until failure
|
||||||
|
push cx ; Save the number of sector for the root directory on the stack
|
||||||
|
push ax ; Save the start of the root directory on the stack
|
||||||
|
xor bx, bx
|
||||||
|
call read_sector ; Read the root directory stored in ax
|
||||||
|
|
||||||
|
.check_entry: ; Check that the loaded sector contain the file we want
|
||||||
|
mov cx, 11 ; Directory entry's are 11 bytes long (file name + file extension = 8 + 3)
|
||||||
|
mov di, bx ; ES:DI is the directory entry address
|
||||||
|
lea si, [%1] ; Load the file name the we are checking for into the SI register (DS:SI)
|
||||||
|
repz cmpsb ; Compare the filename in SI to memory (for 11 bytes)
|
||||||
|
je short .found_file ; If the string matches, the found file
|
||||||
|
; Else try the next entry
|
||||||
|
add bx, 32
|
||||||
|
cmp bx, word [Bytes_per_sector] ; Have we move out of the sector
|
||||||
|
jne short .check_entry ; If not, then try the next entry
|
||||||
|
|
||||||
|
pop ax
|
||||||
|
inc ax ; Increment to the next logical block address
|
||||||
|
pop cx
|
||||||
|
loopnz .read_next_sector ; Decrement CX counter and if not 0 then read the next sector
|
||||||
|
jmp boot_error ; Else if 0, then the file wasn't found so reboot
|
||||||
|
.found_file: ; The stage 2 bootloader file has been found
|
||||||
|
mov ax, word [es:(bx + 0x1A)] ; The directory entry stores the first cluster number of the file
|
||||||
|
; at byte 26 (0x1A). BX is pointing to the address of the start of
|
||||||
|
; the directory entry, so go from there
|
||||||
|
mov word [file_start], ax ; Save the start of the stage 2 bootloader file
|
||||||
|
|
||||||
|
pop ax
|
||||||
|
pop cx
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Read a FAT into memory
|
||||||
|
; m_read_fat FAT_segment
|
||||||
|
%macro m_read_fat 1
|
||||||
|
mov ax, %1 ; Load the memory location for the FAT
|
||||||
|
mov es, ax ; The FAT will be loaded into the extra segment
|
||||||
|
; Calculate the offset of FAT12
|
||||||
|
mov ax, word [Reserved_sectors] ; Add the reserved sectors
|
||||||
|
add ax, word [Hidden_sectors] ; Add the hidden sectors
|
||||||
|
adc ax, word [Hidden_sectors + 2]
|
||||||
|
;Read all FAT sectors into memory
|
||||||
|
mov cx, word [Sectors_per_FAT] ; Read the number of sectors per FAT
|
||||||
|
xor bx, bx ; Set the start offset to be 0x00
|
||||||
|
.read_next_fat_sector:
|
||||||
|
push cx ; Save the loop counter
|
||||||
|
push ax ; Save the start of the FAT
|
||||||
|
call read_sector ; Read the FAT sector
|
||||||
|
pop ax
|
||||||
|
pop cx
|
||||||
|
inc ax ; Increment the logical block address for the next sector
|
||||||
|
add bx, word [Bytes_per_sector] ; Increment by the size of the sector sector
|
||||||
|
loopnz .read_next_fat_sector ; Repeatedly read each FAT into memory (CX of them)
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Read a file into memory
|
||||||
|
; m_read_file load_segment FAT_segment
|
||||||
|
; AX - Is used for the logical block address when reading the sector from the floppy using the FAT table
|
||||||
|
; - The is used for calculating the next FAT entry to read
|
||||||
|
; ES:BX - Is used for the buffer location which the sectors will be read into
|
||||||
|
; CX - Is used for calculating the next FAT entry
|
||||||
|
; - Then storing the next FAT entry
|
||||||
|
; DX - Is used for testing whether to mask the upper bits or shift out the 4 bits
|
||||||
|
; DS:SI - Is used for the location for the FAT table and SI to index into the FAT table
|
||||||
|
%macro m_read_file 2
|
||||||
|
; The root directory will be loaded in a higher segment.
|
||||||
|
; Set ES to this segment.
|
||||||
|
; Here AX is used to set up the extra segment to point to the load segment where the 2nd stage is loaded
|
||||||
|
mov ax, %1 ; Set up the memory segment that will receive the file
|
||||||
|
mov es, ax
|
||||||
|
xor bx, bx ; Set the start offset to be 0x00
|
||||||
|
mov cx, word [file_start] ; Load the start of the file
|
||||||
|
.read_next_file_sector:
|
||||||
|
; Locate the sector to read
|
||||||
|
mov ax, cx ; Sector to read is equal to the current FAT entry
|
||||||
|
add ax, word [root_start] ; Add the start location of the root directory
|
||||||
|
add ax, word [root_sectors] ; Add the size of the root directory
|
||||||
|
sub ax, 2 ; minus 2?
|
||||||
|
|
||||||
|
; Read the sector
|
||||||
|
push cx
|
||||||
|
call read_sector
|
||||||
|
pop cx
|
||||||
|
add bx, word [Bytes_per_sector]
|
||||||
|
|
||||||
|
; Get the next sector to read
|
||||||
|
push ds ; Save the previous data segment value
|
||||||
|
mov dx, %2 ; Make DS:SI (the data segment) point to the FAT
|
||||||
|
mov ds, dx
|
||||||
|
|
||||||
|
push bx ; Save BX as is used for multiply and divide and contains the load location
|
||||||
|
|
||||||
|
mov ax, cx ; Get the start of the FAT entry
|
||||||
|
xor dx, dx ; Set DX to 0 for the multiply and divide
|
||||||
|
mov bx, 3 ; For multiply by 3 -|
|
||||||
|
mul bx ; |
|
||||||
|
mov bx, 2 ; For divide by 2 |- This is for AX * 1.5
|
||||||
|
div bx ; AX - (AX * 3) / 2 -|
|
||||||
|
; DX - (AX * 3) mod 2 - This to check which 4 bits of a byte is needed
|
||||||
|
mov si, ax ; Set the location for the next FAT entry at DS:SI
|
||||||
|
|
||||||
|
pop bx ; Restore BX for the buffer location
|
||||||
|
|
||||||
|
mov cx, word [ds:si] ; Read the FAT entry
|
||||||
|
|
||||||
|
or dx, dx ; If is even, then drop last 4 bits of word
|
||||||
|
; Else is odd, shift the first 4 bits of word
|
||||||
|
|
||||||
|
jnz .read_next_cluster_old ; If is even
|
||||||
|
and cx, 0x0FFF ; Then mask the upper 4 bits
|
||||||
|
jmp .read_next_file_cluster_done
|
||||||
|
.read_next_cluster_old: ; If odd
|
||||||
|
shr cx, 4 ; Shift the 4 bits to the right
|
||||||
|
.read_next_file_cluster_done:
|
||||||
|
pop ds ; Restore DS segment to normal
|
||||||
|
cmp cx, 0xFF8 ; If FAT entry is 0x0FF8, then is the end of file (for FAT12)
|
||||||
|
jl .read_next_file_sector ; If not end of file, read the next sector
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Need to copy the boot sector from the first stage bootloader as contains information thats might have changed
|
||||||
|
; during the first stage
|
||||||
|
%macro m_copy_boot_sector 0
|
||||||
|
push ds ; Need to set DS:SI to 0x0000:0x7C03
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov si, boot_sector_location
|
||||||
|
mov di, boot_sector ; Set ES:DI to location of second stage boot sector
|
||||||
|
mov cx, 34 ; The fist 34 bytes to copy as the rest isn't changed
|
||||||
|
rep movsb
|
||||||
|
pop ds
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Enable protected mode
|
||||||
|
%macro m_enable_protected 0
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 1
|
||||||
|
mov cr0, eax
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
%macro m_save_cursor 0
|
||||||
|
mov word [boot_parameters.signature], SIGNATURE
|
||||||
|
|
||||||
|
mov ah, 0x03
|
||||||
|
xor bh, bh
|
||||||
|
int 0x10
|
||||||
|
|
||||||
|
mov byte [boot_parameters.cursor_pos_x], dl
|
||||||
|
mov byte [boot_parameters.cursor_pos_y], dh
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Set up segments to point to the data GDT table (0x10)
|
||||||
|
%macro m_set_up_segments 0
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
mov ss, ax
|
||||||
|
mov esp, kernel_stack
|
||||||
|
mov ebp, esp
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Parameter: the memory map table memory segment to be stores at
|
||||||
|
%macro m_get_memory_size 0
|
||||||
|
xor eax, eax
|
||||||
|
xor ebx, ebx
|
||||||
|
|
||||||
|
m_bios_get_memory_size_E801
|
||||||
|
|
||||||
|
mov word [boot_parameters.memory_lower], ax
|
||||||
|
mov word [boot_parameters.memory_upper], bx
|
||||||
|
|
||||||
|
push es
|
||||||
|
push di
|
||||||
|
|
||||||
|
xor ax, ax
|
||||||
|
mov ax, memory_map_segment ; Set up the memory segment for where the memory map table will be loaded into
|
||||||
|
mov es, ax
|
||||||
|
|
||||||
|
xor di, di
|
||||||
|
|
||||||
|
m_bios_get_memory_map
|
||||||
|
|
||||||
|
pop di
|
||||||
|
pop es
|
||||||
|
|
||||||
|
mov word [boot_parameters.memory_map_length], si
|
||||||
|
mov dword [boot_parameters.memory_map_address], memory_map_location
|
||||||
|
%endmacro
|
117
src/bootloader/memory.asm
Normal file
117
src/bootloader/memory.asm
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
[bits 16]
|
||||||
|
|
||||||
|
; Get the number of KB of conventional memory up to 64KB.
|
||||||
|
; Output:
|
||||||
|
; AX - The number of KB of conventional memory or -1 if error.
|
||||||
|
%macro m_bios_get_conventional_memory_size 0
|
||||||
|
int 0x12
|
||||||
|
jc short .error_1
|
||||||
|
test ax, ax ; If size=0
|
||||||
|
je short .error_1
|
||||||
|
cmp ah, 0x86 ; Unsupported function
|
||||||
|
je short .error_1
|
||||||
|
cmp ah, 0x80 ; Invalid command
|
||||||
|
je short .error_1
|
||||||
|
ret
|
||||||
|
.error_1:
|
||||||
|
mov ax, -1
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Get the number of contiguous KB starting at 1MB of extended memory up to 64MB.
|
||||||
|
; Output:
|
||||||
|
; AX - The number of contiguous KB starting at 1MB of extended memory or -1 if error.
|
||||||
|
%macro bios_get_extended_memory_size 0
|
||||||
|
mov ax, 0x88
|
||||||
|
int 0x15
|
||||||
|
jc short .error_2
|
||||||
|
test ax, ax ; If size = 0
|
||||||
|
je short .error_2
|
||||||
|
cmp ah, 0x86 ; Unsupported function
|
||||||
|
je short .error_2
|
||||||
|
cmp ah, 0x80 ; Invalid command
|
||||||
|
je short .error_2
|
||||||
|
ret
|
||||||
|
.error_2:
|
||||||
|
mov ax, -1
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Get the memory size for above 64MB.
|
||||||
|
; Output:
|
||||||
|
; AX - KB between 1MB and 16MB. If error, then returns -1.
|
||||||
|
; BX - Number of 64KB blocks above 16MB
|
||||||
|
%macro m_bios_get_memory_size_E801 0
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
xor ecx, ecx ; Clear all registers. This is needed for testing later
|
||||||
|
xor edx, edx
|
||||||
|
mov eax, 0x0000E801
|
||||||
|
int 0x15
|
||||||
|
jc short .error_3
|
||||||
|
cmp ah, 0x86 ; Unsupported function
|
||||||
|
je short .error_3
|
||||||
|
cmp ah, 0x80 ; Invalid command
|
||||||
|
je short .error_3
|
||||||
|
jcxz .use_ax ; BIOS may have stored it in AX, BX or CX, DX. Test if CX is 0
|
||||||
|
mov ax, cx ; It's not, so it should contain memory size; store it
|
||||||
|
mov bx, dx
|
||||||
|
|
||||||
|
.use_ax:
|
||||||
|
pop edx ; Memory size is in ax and bx already, return it
|
||||||
|
pop ecx
|
||||||
|
jmp short .end
|
||||||
|
|
||||||
|
.error_3:
|
||||||
|
mov ax, -1 ; Return -1 as there is an error
|
||||||
|
xor bx, bx ; Zero out BX
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
.end:
|
||||||
|
%endmacro
|
||||||
|
|
||||||
|
; Get the memory map from the BIOS saying which areas of memory are reserved or available.
|
||||||
|
; Input:
|
||||||
|
; ES:DI - The memory segment where the memory map table will be saved to.
|
||||||
|
; Output:
|
||||||
|
; ESI - The number of memory map entries.
|
||||||
|
%macro m_bios_get_memory_map 0
|
||||||
|
xor ebx, ebx ; Start as 0, must preserve value after INT 0x15
|
||||||
|
xor esi, esi ; Number of entries
|
||||||
|
mov eax, 0x0000E820 ; INT 0x15 sub function 0xE820
|
||||||
|
mov ecx, 24 ; Memory map entry structure is 24 bytes
|
||||||
|
mov edx, 0x534D4150 ; SMAP
|
||||||
|
mov [es:di + 20], dword 0x00000001 ; Force a valid ACPI 3.x entry
|
||||||
|
int 0x15 ; Get first entry
|
||||||
|
jc short .error_4 ; If carry is set, then there was and error
|
||||||
|
cmp eax, 0x534D4150 ; BIOS returns SMAP in EAX
|
||||||
|
jne short .error_4
|
||||||
|
test ebx, ebx ; If EBX = 0 then list is one entry
|
||||||
|
je short .error_4 ; Then is worthless, so error.
|
||||||
|
jmp short .start
|
||||||
|
.next_entry:
|
||||||
|
mov eax, 0x0000E820
|
||||||
|
mov ecx, 24
|
||||||
|
mov edx, 0x534D4150
|
||||||
|
mov [es:di + 20], dword 0x00000001
|
||||||
|
int 0x15 ; Get next entry
|
||||||
|
jc short .done ; Carry set if end of list already reached
|
||||||
|
.start:
|
||||||
|
jcxz .skip_entry ; If actual returned bytes is 0, skip entry
|
||||||
|
cmp cl, 20 ; Has it returned a a 24 byte ACPI 3.x response
|
||||||
|
jbe short .notext
|
||||||
|
test byte [es:di + 20], 0x01 ; If so, is the 'ignore this data' bit set
|
||||||
|
jc short .skip_entry
|
||||||
|
.notext:
|
||||||
|
mov ecx, dword [es:di + 8] ; Save the lower 32 bit memory region lengths
|
||||||
|
or ecx, dword [es:di + 12] ; OR with upper region to test for zero
|
||||||
|
jz short .skip_entry ; If zero, the skip entry
|
||||||
|
inc esi ; Good entry so increment entry count and buffer offset
|
||||||
|
add di, 24
|
||||||
|
.skip_entry:
|
||||||
|
cmp ebx, 0 ; If EBX is 0, list is done
|
||||||
|
jne short .next_entry ; Get next entry if not zero
|
||||||
|
jmp short .done ; If zero then finish
|
||||||
|
.error_4:
|
||||||
|
m_reboot_with_msg memory_map_error
|
||||||
|
.done:
|
||||||
|
clc ; Clear the carry as was set before this point because of the JC.
|
||||||
|
%endmacro
|
Loading…
Reference in a new issue