* 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
|
||||
*.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