* 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Edward Dean
						Edward Dean