; first stage boot loader cpu 8086 bits 16 section .bootsect extern _stage2_start_seg extern _stage2_size boot_driveno equ 7b00h num_read_tries equ 7b06h ; 2 bytes sect_pending equ 7b08h ; 2 bytes sect_per_track equ 7b0ah ; 2 bytes cur_head equ 7b0ch ; 2 bytes - current head start_sect equ 7b0eh ; 2 bytes - start sector in track destptr equ 7b10h ; 2 bytes - destination pointer num_heads equ 7b12h ; 2 bytes - number of heads cur_cyl equ 7b14h ; 2 bytes - current cylinder %macro floppy_motor_off 0 pushf and dl, 80h jnz %%end ; skip if high bit is set (i.e. it's not a floppy) mov dx, 3f2h in al, dx and al, 0fh out dx, al %%end: popf %endmacro ; 5.25" 360K floppy BPB_DISK_SECTORS equ 720 BPB_TRACK_SECTORS equ 9 BPB_MEDIA_TYPE equ 0fdh ; 3.5" 1.44M floppy ;BPB_DISK_SECTORS equ 2880 ;BPB_TRACK_SECTORS equ 18 ;BPB_MEDIA_TYPE equ 0f0h bios_param_block: jmp start ; 2 bytes nop ; 1 byte ; start of BPB at offset 3 db "86BOOT00" ; 03h: OEM ident, 8 bytes dw 512 ; 0bh: bytes per sector db 1 ; 0dh: sectors per cluster dw 1 ; 0eh: reserved sectors (including boot record) db 2 ; 10h: number of FATs dw 224 ; 11h: number of dir entries dw BPB_DISK_SECTORS ; 13h: number of sectors in volume db BPB_MEDIA_TYPE ; 15h: media descriptor type (f0 = 3.5" HD floppy) dw 9 ; 16h: number of sectors per FAT dw BPB_TRACK_SECTORS ; 18h: number of sectors per track dw 2 ; 1ah: number of heads dd 0 ; 1ch: number of hidden sectors dd 0 ; 20h: high bits of sector count db 0 ; 24h: drive number db 0 ; 25h: winnt flags db 28h ; 26h: signature(?) dd 0 ; 27h: volume serial number db "80 SIX BOOT"; 2bh: volume label, 11 bytes db "FAT12 " ; 36h: filesystem id, 8 bytes start: cld xor ax, ax mov ds, ax mov es, ax jmp 00:.setcs .setcs: ; put the stack high mov ax, 0x7f00 mov ss, ax xor sp, sp mov [boot_driveno], dl ; query sectors per track mov ah, 8 ; get drive parameters call, dl already has the drive xor di, di int 13h jc .queryfail and cx, 3fh mov [sect_per_track], cx mov cl, 8 shr dx, cl inc dx mov [num_heads], dx jmp .querydone .queryfail: ; in case of failure, try 18 sectors per track, 2 heads mov word [sect_per_track], 18 mov word [num_heads], 2 .querydone: ; load the rest of the code high mov ax, _stage2_size add ax, 511 mov cl, 9 shr ax, cl inc ax mov [sect_pending], ax mov ax, _stage2_start_seg mov es, ax ; destination segment mov word [destptr], 0 mov word [start_sect], 1 ; start from sector 1 to skip boot sector mov word [cur_cyl], 0 mov word [cur_head], 0 .rdloop: mov cx, [start_sect] mov ax, [sect_pending] sub ax, cx ; num_sect = pending - start_sect cmp ax, [sect_per_track] jbe .noadj mov ax, [sect_per_track] ; read max sect_per_track at a time sub ax, cx .noadj: test ax, ax jz .done push ax ; save how many sectors we're reading push ax push bx push cx push dx push si call print_hex_word mov ax, str_rdtrack2 call printstr mov ax, [cur_cyl] call print_hex_byte mov ax, str_rdtrack3 call printstr mov ax, [cur_head] call print_hex_byte mov ax, str_rdtrack3 call printstr mov ax, [start_sect] call print_hex_byte mov ax, str_newline call printstr pop si pop dx pop cx pop bx pop ax mov ch, [cur_cyl] mov dh, [cur_head] mov bx, [destptr] inc cl ; sector numbers start from 1 mov ah, 2 ; read sectors is call 2 mov dl, [boot_driveno] int 13h jc .fail ; track read sucessfully mov word [start_sect], 0 ; all subsequent tracks are whole mov ax, [cur_head] inc ax cmp ax, [num_heads] jnz .skip_cyl_adv xor ax, ax inc byte [cur_cyl] .skip_cyl_adv: mov [cur_head], ax pop ax ; num_sect sub [sect_pending], ax push cx mov cl, 9 shl ax, cl ; convert to bytes pop cx add [destptr], ax jnz .rdloop ; loaded sucessfully, load segment registers and jump .done: mov ax, _stage2_start_seg mov ds, ax mov es, ax push ax xor ax, ax push ax retf .fail: add sp, 2 ; clear num_sect off the stack dec word [num_read_tries] jz .abort ; reset controller and retry xor ax, ax mov dl, [boot_driveno] int 13h jmp .rdloop ; failed to load second sector .abort: xor ax, ax mov es, ax floppy_motor_off ; turn off floppy motor (if dl is < 80h) mov ax, str_load_fail call printstr .hang: hlt jmp .hang ; expects string ptr in ax printstr: mov si, ax .loop: mov al, [si] inc si test al, al jz .done mov ah, 0eh mov bx, 7 int 10h jmp .loop .done: ret print_hex_word: mov cx, 4 ; 4 digits to print jmp print_n_hex_digits print_hex_byte: mov cx, 2 ; 2 digits to print mov ah, al ; move them in place print_n_hex_digits: rol ax, 1 rol ax, 1 rol ax, 1 rol ax, 1 mov dx, ax ; save ax, print_hex_digit destroys it call print_hex_digit mov ax, dx dec cx jnz print_n_hex_digits ret print_hex_digit: mov bl, al and bx, 0fh mov al, [bx + .hexdig_tab] mov ah, 0eh mov bx, 7 int 10h ret .hexdig_tab: db "0123456789abcdef" str_rdtrack2 db " from ",0 str_rdtrack3 db "/",0 str_load_fail db "Failed to load 2nd stage!",0 str_newline db 13,10,0 times 510-($-$$) db 0 bootsig dw 0xaa55 ; vi:set ts=8 sts=8 sw=8 ft=nasm: