[bits 16] [org 100h] [section .text] %include "gdtnasm.inc" %define ONE_MEG 0x100000 %define MEM_INCR ONE_MEG %define LOAD_ADDR ONE_MEG %define RDSK_ADDR (ONE_MEG * 2) %define BUFFER_ADDR 0xfe00 ;0x20000 %define FS_OK 1 %define FS_ERROR 0 start: mov si, msg_loader call print mov [boot_drv], dl ;****************************************************** ; Switch to "flat-real" mode ;****************************************************** cli n5: in al, 0x64 ; Enable A20 gate test al, 2 jnz n5 mov al, 0xD1 out 0x64, al n6: in al, 0x64 test al, 2 jnz n6 mov al, 0xDF out 0x60, al xor eax, eax mov ax, cs ; get code segment shl eax, 4 ; convert segment -> linear add [gdtinfo+2], eax ; do the fixups add [loader_code+2], eax add [loader_data+2], eax lgdt [gdtinfo] ; Load GDTR mov ecx, CR0 ; Set CR0.PE inc cx mov CR0, ecx mov ax, flat_data - gdt ; Selector for 4Gb data seg mov ds, ax ; Extend limit for ds mov es, ax ; Extend limit for es mov fs, ax ; fs and... mov gs, ax ; gs dec cx ; switch back to real mode mov CR0, ecx sti mov ax, cs mov ds, ax ;****************************************************** ; Count installed memory ;****************************************************** mov si, msg_memory call print count_memory: ; set, restore and test a byte at each 1 Mb boundary ; the end is reached when byte read != byte written xor edi, edi .start: mov al, byte [es:edi] ; Save the byte mov byte [es:edi], 0aah ; Write a new one (0AAh) mov ah, byte [es:edi] ; Read it back mov byte [es:edi],al ; Restore the old one cmp ah, 0aah ; Check the byte... jne .finish ; ...if it's different, stop mov al, byte [es:edi] ; Repeat for 55h mov byte [es:edi], 55h mov ah, byte [es:edi] mov byte [es:edi],al cmp ah, 55h jne .finish add edi,MEM_INCR ; Skip to the next chunk jmp .start .finish: mov [memory_top],edi ; edi contains failed address mov eax, edi ; Remind the user how much memory they have shr eax, 20 call print_int mov si, msg_memory.mb call print jmp load_kernel load_fail: ; Generic failure proc used during loading mov si, msg_failed call print jmp $ ;****************************************************** ; Load the kernel into extended memory ;****************************************************** load_kernel: mov si, msg_fs_init call print call fs_init ; Initialize the file system test al, al jz load_fail mov si, msg_done call print mov si, msg_loading_kernel call print mov ebx, LOAD_ADDR ; Load to LOAD_ADDR mov si, kernel_name call fs_loadfile ; Load the kernel test al, al jz load_fail xor eax, eax ; Get the size of the kernel in sectors mov ax, [file_scts] shl eax, 9 mov [kernel_size], eax ; Store the size of the kernel call print_int mov si, msg_done call print ;%if 0 mov si, msg_loading_ramdisk call print mov ebx, RDSK_ADDR ; Load to RDSK_ADDR mov si, ramdisk_name call fs_loadfile ; Load the ramdisk test al, al jz load_fail xor eax, eax ; Get the size of the ramdisk in sectors mov ax, [file_scts] shl eax, 9 mov [rdsk_size], eax ; Store the size of the ramdisk call print_int mov si, msg_done call print ;%endif mov dx, 0x3f2 ; Turn off the floppy motor mov al, 0x0c out dx, al ;****************************************************** ; Enable full protected mode ;****************************************************** ; Interrupts make this thing go *bang* from now on cli mov ecx, cr0 inc cx ; Set CR0.PE for the last time... mov cr0, ecx mov ax, loader_data - gdt mov fs, ax ; fs = loader data mov ax, flat_data - gdt mov gs, ax ; gs = linear data mov ax, kernel_data - gdt mov ds, ax ; ds = es = ss = kernel data mov es, ax mov ss, ax ; Reload CS to complete the move to protected mode jmp dword (loader_code-gdt):pmode1 %include "minifs.asm" ; File system procedures (currently FAT12) ;********************************************************** ;* print: print a string using the BIOS ;* ;* in: ;* si: address of message (NUL-terminated) ;* modifies: ;* nothing ;********************************************************** print: push ax ; Save some registers push bx ; INT 10/0Eh has been known to use BP, but we don't need it mov ah, 0eh xor bx, bx ; Page 0 .1: mov al, [si] ; Load a byte... test al, al ; ...stop on NUL jz .2 int 10h ; Write it inc si jmp .1 .2: pop bx pop ax ret ;********************************************************** ;* print_int: print a 32-bit number in decimal using the BIOS ;* ;* in: ;* eax: number to be printed ;* modifies: ;* nothing ;********************************************************** print_int: push ebx ; save registers push ecx push edx push esi push edi mov esi, 0 ; don't print leading zeroes mov ebx, 1000000000 mov ecx, eax ; store a copy of ax .pipm: cmp ebx, 1 ; if we are on last digit jne .cnpm mov esi, 1 ; make sure we print it (even if zero) .cnpm: mov eax, ecx ; get current num xor edx, edx ; clear dx div ebx ; divide by decimal mov ecx, edx ; save remainder or esi, eax ; if si and ax are zero, it's a leading zero jz .lz ; leading zero push ebx xor bx, bx add al, '0' mov ah, 0eh int 10h pop ebx .lz: mov eax, ebx ; we have to reduce the radix too xor edx, edx ; clear dx mov ebx, 10 ; use 10 as new divider div ebx ; divide mov ebx, eax ; save result or ebx, ebx ; if zero, we're done jnz .pipm pop edi ; restore registers pop esi pop edx pop ecx pop ebx ret [bits 32] ;****************************************************** ; Protected-mode code ;****************************************************** pmode1: push dword 2 ; clear EFLAGS apart from bit 1 popfd %if 1 mov al, byte [ds:0xe6a] mov byte [gs:0b8000h], al mov al, byte [ds:0xe6c] mov byte [gs:0b8002h], al mov al, byte [ds:0xe6e] mov byte [gs:0b8004h], al mov al, byte [ds:0xe70] mov byte [gs:0b8006h], al %endif ; Set up the kernel's entry variables mov eax, dword [fs:memory_top] mov ebx, dword [fs:kernel_size] mov ecx, LOAD_ADDR mov edx, dword [fs:rdsk_size] mov esi, RDSK_ADDR ; Start the kernel! jmp (kernel_code-gdt):0 ;jmp $ [section .data] gdtinfo: dw gdtlength dd gdt gdt: null_desc desc 0,0,0 loader_code desc 0, 0xFFFF, D_CODE + D_READ + D_BIG + D_BIG_LIM loader_data desc 0, 0xFFFF, D_DATA + D_WRITE + D_BIG + D_BIG_LIM kernel_code desc LOAD_ADDR, 0xFFFFF, D_CODE + D_READ + D_BIG + D_BIG_LIM kernel_data desc LOAD_ADDR, 0xFFFFF, D_DATA + D_WRITE + D_BIG + D_BIG_LIM flat_code desc 0, 0xFFFFF, D_CODE + D_READ + D_BIG + D_BIG_LIM flat_data desc 0, 0xFFFFF, D_DATA + D_WRITE + D_BIG + D_BIG_LIM gdtlength equ $ - gdt - 1 msg_loader: db 13, 10, "Starting The M”bius...", 13, 10, 0 msg_memory: db "System memory: ", 0 .mb: db "MB", 13, 10, 0 msg_fs_init: db "Starting file system", 0 msg_done: db " OK", 13, 10, 0 msg_failed: db " failed", 13, 10, 0 msg_loading_kernel: db "Loading the kernel ", 0 msg_loading_ramdisk: db "Loading the ramdisk ", 0 kernel_name: db "KERNEL BIN", 0 ramdisk_name: db "RAMDISK BIN", 0 boot_drv: db 0 memory_top: dd 0 kernel_size:dd 0 rdsk_size: dd 0 sectorbuf: times 512 db 0 sectorbuf_end: