--- /dev/null
+; first stage boot loader
+ cpu 8086
+ bits 16
+ section .bootsect
+
+extern _stage2_start
+extern _stage2_end
+
+stacktop equ 7b00h
+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
+
+
+bios_param_block:
+ jmp start ; 2 bytes
+ nop ; 1 byte
+ ; start of BPB at offset 3
+ db "BSPL 0.1" ; 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 2880 ; 13h: number of sectors in volume
+ db 0fh ; 15h: media descriptor type (f = 3.5" HD floppy)
+ dw 9 ; 16h: number of sectors per FAT
+ dw 18 ; 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
+ mov ss, ax
+ mov gs, ax
+ mov fs, ax
+ jmp 00:.setcs
+.setcs:
+ mov sp, stacktop
+ 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 at 7e00h
+ mov ax, _stage2_end
+ sub ax, _stage2_start
+ add ax, 511
+ mov cl, 9
+ shr ax, cl
+ inc ax
+ mov [sect_pending], ax
+ mov ax, _stage2_start
+ shr ax, 1
+ shr ax, 1
+ shr ax, 1
+ shr ax, 1
+ mov es, ax ; destination segment 7e0h to allow loading up to 64k
+ 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, reset es back to 0 and jump
+.done: xor ax, ax
+ mov es, ax
+ jmp _stage2_start
+
+.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: