BITS 16 ;http://www.ousob.com/ng/bios/ng1223.php start: ; Set up 4K stack after this bootloader ; [Remember: Effective Address = Segment*16 + Offset] mov ax, 0x7C0 ; Set 'ax' equal to the location of this bootloader divided by 16 add ax, 0x20 ; Skip over the size of the bootloader divided by 16 mov ss, ax ; Set 'ss' to this location (the beginning of our stack region) mov sp, 8192 ; Set 'ss:sp' to the top of our 8K stack ; Set data segment to where we're loaded so we can implicitly access all 64K from here mov ax, 0x7C0 ; Set 'ax' equal to the location of this bootloader divided by 16 mov ds, ax ; Set 'ds' to the this location mov [disk_identifier], dl ; Print our message and stop execution mov si, message ; Put address of the null-terminated string to output into 'si' call print ; Call our string-printing routine mov si, enter_debug_mode call print call printCRLF ; here goes wait call, for the user to enter debug mode. ; Wating for 2 seconds: mov ah, 0x86 ; code for waiting interupt call mov cx, 0x001e ; high count of microseconds mov dx, 0x8480 ; low count int 0x15 .busy_wait_for_key: xor ax, ax mov ah, 0x01 ; BIOS call to wait for key int 0x16 jnz debug_mode ; entering system check: mov si, enter_system_check mov si, sys_check_ok ; Put address of the null-terminated string to output into 'si' call print ; Call our string-printing routine mov si, boot_system ; Put address of the null-terminated string to output into 'si' call print ; Call our string-printing routine ;This goes first as to now overwrite %ah and %al. mov ax, 0x0 mov es, ax ;Section to write into mov ah, 0x2 ;Read sectors from drive mov al, 0xf ;Number of sectors to read (15 * 512 = 7680 bytes) mov ch, 0x0 ;Low 8 bits of cylinder mov cl, 0x11 ;First sector to read (bits 0-5), upper bits of cylinder (bits 6-7) mov dh, 0x0 ;Head number mov dl, [disk_identifier] ;0x0 ;Drive number mov bx, 0x7e00 ;Offset into section int 0x13 ;Low level disk services ;mov [0x7000], es ; saving retult of read ; ;Debug dump of loaded memory ;mov si, 500 ;mov cx, 512 ;call b_dumpmem jnc notcarry mov si, error_str call print jmp endcarrycheck ; notcarry: mov si, success_str call print call printCRLF call printCRLF ; xor ax, ax ; mov al, [disk_identifier] ; call dumpax mov dl, [disk_identifier] mov ax, 0x7e0 mov ds, ax jmp 0x7e0:0x00 endcarrycheck: cli ; Clear the Interrupt Flag (disable external interrupts) hlt ; Halt the CPU (until the next external interrupt) debug_mode: mov si, .welcome_debug call print cli ; Clear the Interrupt Flag (disable external interrupts) hlt .welcome_debug db 'This is debug mode', 0 data: message db 'Bootloader for SingOS! v0.0.3',13,10,0 enter_debug_mode db 'Press d to enter bootloader debug mode',13,10,0 enter_system_check db 'Performing system check:',13,10,0 sys_check_ok db 'System check ok', 13, 10, 0 boot_system db 'Read SingOS from disk', 13, 10, 0 error_str db 'Error', 0 success_str db 'Success', 0 disk_identifier db 0 ; Routine for printing a 'ax' as hex dumpax: pusha ; save registers mov bx, ax mov ah, 0xE ; Teletype output mov cx, 4 ; 4 nipples in a 16 bit word .loop: rol bx, 4 ; rotate to next nipple mov al, bl ; we copy to al because we need to mask only the low 4 bits and al, 1111b ; Do the masking add al, '0' ; convert to ASCII cmp al, '9' ; If we are greater than 9 ascii, we add 7 to make digit 10 be represented as 'A' jbe .skip ; -|- add al, 7 ; -|- .skip: ; -|- int 0x10 ; BIOS call 'output' loop .loop popa ; restore registers ret dumpax10: ; Prints ax as 16-bit decimal number pusha mov bx, 10 ; Divisor mov cx, 5 ; Loop 5 times .loop1: ; finds digits and pushes them to stack xor dx, dx div bx add dl, '0' push dx loop .loop1 mov ah, 0xE mov cx, 5 ; Loop 5 times mov bl, '0' .loop2: ; Pops from stack until it hits a non-'0' value. It then jumps to nonzero_nopop to print it. pop dx mov al, dl cmp al, bl jne .nonzero_nopop loop .loop2 .nonzero_loop: ; Pops values from the stack and prints them. pop dx mov al, dl .nonzero_nopop: ; Part of the loop that prints the value. Jump to here to print without popping on first iteration. int 0x10 loop .nonzero_loop popa ret dumpax10_rev: ; Prints ax as 16-bit decimal number in reverse pusha mov cx, 10 ; Divisor .loop: xor dx, dx ; zero dx div cx ; Divide dx:ax by 10 -> quotient in ax, remainder in dx mov bx, ax ; save quotient in bx mov al, dl ; put remainder in al add al, '0' ; Make ASCII mov ah, 0xE ; Set teletype output int 0x10 ; BIOS: write one char mov ax, bx ;test ax, ax cmp ax, 0 jnz .loop popa ret printCRLF: mov ah, 0xE mov al, 13 int 0x10 mov al, 10 int 0x10 ret ; Routine for outputting string in 'si' register to screen print: mov ah, 0xE ; Specify 'int 0x10' 'teletype output' function ; [AL = Character, BH = Page Number, BL = Colour (in graphics mode)] .printchar: lodsb ; Load byte at address SI into AL, and increment SI cmp al, 0 je .done ; If the character is zero (NUL), stop writing the string int 0x10 ; Otherwise, print the character via 'int 0x10' jmp .printchar ; Repeat for the next character .done: ret ; b_dumpmem: ; push ax ; push dx ; call printCRLF ; shr cx, 1 ; xor dx, dx ; zero dx ; .loop: ; cmp dx, cx ; jae .end ; mov ax, word [esi + 2*edx] ; call dumpax ; mov ax, 0xe20 ; int 0x10 ; inc dx ; jmp .loop ; .end: ; pop dx ; pop ax ; ret ; Pad to 510 bytes (boot sector size minus 2) with 0s, and finish with the two-byte standard boot signature times 510-($-$$) db 0 dw 0xAA55 ; => 0x55 0xAA (little endian byte order) ; bootloder debug_mode goes here times 8192-($-$$) db 0 ; From Version 0.0.3 we are concatinate the bootloader and the kernel after compile time ;%include "kernel.nasm" ;times 8192-($-$$) db 0