;Copyright 2019 Benji Dial ;Permission to use, copy, modify, and/or distribute this ;software for any purpose with or without fee is hereby ;granted, provided that the above copyright notice and this ;permission notice appear in all copies. ;THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ;ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL ;IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO ;EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, ;INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ;WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ;WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ;TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ;USE OR PERFORMANCE OF THIS SOFTWARE. kload equ 0x0010_0000 global_info:;0x0500 - 0x05ff (0x0515) .fat equ 0x0500;dword - pointer .root_dir equ 0x0504;dword - pointer .low_mem equ 0x0508;word - usable memory from 0 to 1MiB in kiB .mid_mem equ 0x050a;word - usable memory from 1MiB to 16MiB in kiB .mem_used equ 0x050c;word - used memory from 0 to 1MiB in kiB .kernel_s equ 0x050e;word - kernel image length in kiB ;dword - kernel use .high_mem equ 0x0514;word - usable memory from 16MiB up in 64s of kiB fat_header:;0x7c03 - 0x7c3d .oem_name equ 0x7c03;mkfs.fat - .bytes_per_sector equ 0x7c0b;0x0200 - .sectors_per_cluster equ 0x7c0d;0x01 - .reserved_sectors equ 0x7c0e;0x0002 - handled .n_fats equ 0x7c10;0x01 - .root_dir_entries equ 0x7c11; - .n_sectors equ 0x7c13;0x0b40 - .media_type equ 0x7c15; - .sectors_per_fat equ 0x7c16; - handled .sectors_per_track equ 0x7c18;0x0012 - .sides equ 0x7c1a;0x0002 - .hidden_sectors equ 0x7c1c;0x0000_0000 - .long_sectors equ 0x7c20;0x0000_0000 - .drive_number equ 0x7c24; - ignored, used as scratch for real drive number .flags equ 0x7c25; - .signature equ 0x7c26;0x29 - errors on other values .id equ 0x7c27; - .label equ 0x7c2b;PORTLAND OS - .fs_type equ 0x7c36;FAT12 - errors on other values error_codes: .bad_fat_header equ 0o000 .insufficient_memory equ 0o001 .no_kernel equ 0o002 .kernel_too_big equ 0o003 org 0x7c3e bits 16 ;Enable A20 line in al, 0x92 or al, 0x02 out 0x92, al ;Load second sector of bootloader mov ah, 0x42 mov si, dap int 0x13 ;Check FAT signature test byte [fat_header.signature], 0x29 jne bad_fat_header test word [fat_header.fs_type], 0x4146;FA jne bad_fat_header test word [fat_header.fs_type + 2], 0x3154;T1 jne bad_fat_header test word [fat_header.fs_type + 4], 0x2032;2 jne bad_fat_header test word [fat_header.fs_type + 6], 0x2020; jne bad_fat_header ;Store boot drive ID for later mov byte [fat_header.drive_number], dl memory: ;Get available low memory int 0x12 mov word [global_info.low_mem], ax mov word [global_info.mem_used], 0x0020 ;Get available mid and high memory xor dx, dx mov ax, 0xe801 int 0x15 test dx, dx jnz .got mov cx, ax mov dx, bx .got: mov word [global_info.mid_mem], cx mov word [global_info.high_mem], dx ;Load FAT mov ax, word [fat_header.reserved_sectors] mov word [dap.start], ax xor eax, eax mov ax, word [fat_header.sectors_per_fat] call load_low mov dword [global_info.fat], ebx load_root: ;Load root directory mov ax, word [fat_header.sectors_per_fat] xor dh, dh mov dl, word [fat_header.n_fats] mul dx add ax, [fat_header.reserved_sectors] jnc .no_inc inc dx .no_inc: mov word [dap.start], ax mov word [dap.start + 2], dx mov ax, word [fat_header.root_dir_entries] shr ax, 4 call load_low mov dword [global_info.root_dir], ebx ;Calculate end of root directory xor ecx, ecx mov cx, word [fat_header.root_dir_entries] shl ecx, 5 add ecx, ebx jc insufficient_memory find_kernel: mov eax, 0x6e72_656b;kern mov edx, 0x2020_6c65;el .loop: ;Check name test dword [ebx], eax jne .next test dword [ebx + 4], edx jne .next test word [ebx + 8], 0x7973;sy jne .next test byte [ebx + 10], 0x73;s je .found .next: ;Next record add ebx, 32 test ebx, ecx jne .loop ;Got to end of directory mov dl, error_codes.no_kernel jmp error ;ebx is pointer to entry ;word [ebx + 26] is first cluster ;dword [ebx + 28] is size .found: ;Save size for later mov eax, dword [ebx + 28] shr eax, 10 test eax, 0x003f_0000 jz .store_size mov dl, error_codes.kernel_too_big jmp error .store_size: mov word [global_info.kernel_s], ax ;Save sector number for later mov cx, [ebx + 26] ;find 1kiB buffer mov ax, 2 mov byte [load_low.ret_with_segment], 0xc3;ret call load_low shl ebx, 4 load_kernel: load_low: ;ax in : number of sectors ;qword [dap.start] in : starting sector ;Store length in DAP mov word [dap.length], ax ;Calculate kiB used dec ax shr ax, 1 inc ax mov dx, ax add ax, word [global_info.mem_usage] ;Fail if not enough is available jc insufficient_memory cmp ax, word [global_info.low_mem] jg insufficient_memory ;Check if address will fit in DAP test ah, ah jnz insufficient_memory ;Commit usage mov word [global_info.low_mem_usage], ax ;Put address into DAP xor ebx, ebx mov bh, al shr bx, 2 mov word [dap.segment], bx mov word [dap.offset], 0x0000 .ret_with_segment: ;Load from disk mov si, dap mov ah, 0x42 int 0x13 shl ebx, 4 ret bad_fat_header: mov dl, error_codes.bad_fat_header jmp error insufficient_memory: mov dl, error_codes.insufficient_memory jmp error error: ;Print error code (in octal) mov ax, 0xb800 mov ds, ax mov word [0], 0x0f72;E mov ax, 0x0f00 mov al, dl and al, 0x07 or al, 0x30 mov word [6], ax;bottom digit shr dl, 3 mov al, dl and al, 0x07 or al, 0x30 mov word [4], ax;middle digit shr dl, 3 mov al, dl or al, 0x30 mov word [2], ax;top digit cli .halt: hlt jmp .halt gdt: dw 0x28 dd gdt + 6 dq 0x0000_0000_0000_0000 dq 0x0040_9a00_0000_7fff;0x000000 - 0x007fff dq 0x0040_9200_0000_7fff dq 0x00c0_9a00_8000_0ff7;0x008000 - 0xffffff dq 0x00c0_9200_8000_0ff7 dap: dw 0x0010 .length dw 0x0001 .offset dw 0x7e00 .segment dw 0x0000 .start dq 0x0000_0000_0000_0001 memory_usage: .low dw ;word - used memory from 0 to 1MiB in kiB .mid dw 0x0000;word - used memory from 1MiB to 16MiB in kiB .high dw 0x0000;word - used memory from 16MiB up in 64s of kiB