load stage2 high to leave the low RAM for the kernel
[eightysix] / boot / boot.asm
1 ; first stage boot loader
2         cpu 8086
3         bits 16
4         section .bootsect
5
6 extern _stage2_start_seg
7 extern _stage2_size
8
9 boot_driveno    equ 7b00h
10 num_read_tries  equ 7b06h       ; 2 bytes
11 sect_pending    equ 7b08h       ; 2 bytes
12 sect_per_track  equ 7b0ah       ; 2 bytes
13 cur_head        equ 7b0ch       ; 2 bytes - current head
14 start_sect      equ 7b0eh       ; 2 bytes - start sector in track
15 destptr         equ 7b10h       ; 2 bytes - destination pointer
16 num_heads       equ 7b12h       ; 2 bytes - number of heads
17 cur_cyl         equ 7b14h       ; 2 bytes - current cylinder
18
19 %macro floppy_motor_off 0
20         pushf
21         and dl, 80h
22         jnz %%end       ; skip if high bit is set (i.e. it's not a floppy)
23         mov dx, 3f2h
24         in al, dx
25         and al, 0fh
26         out dx, al
27 %%end:  popf
28 %endmacro
29
30
31 bios_param_block:
32         jmp start       ; 2 bytes
33         nop             ; 1 byte
34         ; start of BPB at offset 3
35         db "BSPL 0.1"   ; 03h: OEM ident, 8 bytes
36         dw 512          ; 0bh: bytes per sector
37         db 1            ; 0dh: sectors per cluster
38         dw 1            ; 0eh: reserved sectors (including boot record)
39         db 2            ; 10h: number of FATs
40         dw 224          ; 11h: number of dir entries
41         dw 2880         ; 13h: number of sectors in volume
42         db 0fh          ; 15h: media descriptor type (f = 3.5" HD floppy)
43         dw 9            ; 16h: number of sectors per FAT
44         dw 18           ; 18h: number of sectors per track
45         dw 2            ; 1ah: number of heads
46         dd 0            ; 1ch: number of hidden sectors
47         dd 0            ; 20h: high bits of sector count
48         db 0            ; 24h: drive number
49         db 0            ; 25h: winnt flags
50         db 28h          ; 26h: signature(?)
51         dd 0            ; 27h: volume serial number
52         db "80 SIX BOOT"; 2bh: volume label, 11 bytes
53         db "FAT12   "   ; 36h: filesystem id, 8 bytes
54
55 start:
56         cld
57         xor ax, ax
58         mov ds, ax
59         mov es, ax
60         jmp 00:.setcs
61 .setcs:
62         ; put the stack high
63         mov ax, 0x7f00
64         mov ss, ax
65         xor sp, sp
66         mov [boot_driveno], dl
67
68         ; query sectors per track
69         mov ah, 8       ; get drive parameters call, dl already has the drive
70         xor di, di
71         int 13h
72         jc .queryfail
73         and cx, 3fh
74         mov [sect_per_track], cx
75         mov cl, 8
76         shr dx, cl
77         inc dx
78         mov [num_heads], dx
79         jmp .querydone
80 .queryfail:
81         ; in case of failure, try 18 sectors per track, 2 heads
82         mov word [sect_per_track], 18
83         mov word [num_heads], 2
84 .querydone:
85
86         ; load the rest of the code high
87         mov ax, _stage2_size
88         add ax, 511
89         mov cl, 9
90         shr ax, cl
91         inc ax
92         mov [sect_pending], ax
93         mov ax, _stage2_start_seg
94         mov es, ax      ; destination segment
95         mov word [destptr], 0
96         mov word [start_sect], 1        ; start from sector 1 to skip boot sector
97         mov word [cur_cyl], 0
98         mov word [cur_head], 0
99
100 .rdloop:
101         mov cx, [start_sect]
102         mov ax, [sect_pending]
103         sub ax, cx              ; num_sect = pending - start_sect
104         cmp ax, [sect_per_track]
105         jbe .noadj
106         mov ax, [sect_per_track]        ; read max sect_per_track at a time
107         sub ax, cx
108 .noadj: test ax, ax
109         jz .done
110         push ax         ; save how many sectors we're reading
111
112         push ax
113         push bx
114         push cx
115         push dx
116         push si
117         call print_hex_word
118         mov ax, str_rdtrack2
119         call printstr
120         mov ax, [cur_cyl]
121         call print_hex_byte
122         mov ax, str_rdtrack3
123         call printstr
124         mov ax, [cur_head]
125         call print_hex_byte
126         mov ax, str_rdtrack3
127         call printstr
128         mov ax, [start_sect]
129         call print_hex_byte
130         mov ax, str_newline
131         call printstr
132         pop si
133         pop dx
134         pop cx
135         pop bx
136         pop ax
137
138         mov ch, [cur_cyl]
139         mov dh, [cur_head]
140         mov bx, [destptr]
141
142         inc cl                  ; sector numbers start from 1
143         mov ah, 2               ; read sectors is call 2
144         mov dl, [boot_driveno]
145         int 13h
146         jc .fail
147         ; track read sucessfully
148         mov word [start_sect], 0        ; all subsequent tracks are whole
149         mov ax, [cur_head]
150         inc ax
151         cmp ax, [num_heads]
152         jnz .skip_cyl_adv
153         xor ax, ax
154         inc byte [cur_cyl]
155 .skip_cyl_adv:
156         mov [cur_head], ax
157
158         pop ax                  ; num_sect
159         sub [sect_pending], ax
160         push cx
161         mov cl, 9
162         shl ax, cl              ; convert to bytes
163         pop cx
164         add [destptr], ax
165         jnz .rdloop
166
167         ; loaded sucessfully, load segment registers and jump
168 .done:  mov ax, _stage2_start_seg
169         mov ds, ax
170         mov es, ax
171         push ax
172         xor ax, ax
173         push ax
174         retf
175
176
177 .fail:  add sp, 2       ; clear num_sect off the stack
178         dec word [num_read_tries]
179         jz .abort
180
181         ; reset controller and retry
182         xor ax, ax
183         mov dl, [boot_driveno]
184         int 13h
185         jmp .rdloop
186
187         ; failed to load second sector
188 .abort: xor ax, ax
189         mov es, ax
190         floppy_motor_off        ; turn off floppy motor (if dl is < 80h)
191         mov ax, str_load_fail
192         call printstr
193 .hang:  hlt
194         jmp .hang
195
196         ; expects string ptr in ax
197 printstr:
198         mov si, ax
199 .loop:  mov al, [si]
200         inc si
201         test al, al
202         jz .done
203         mov ah, 0eh
204         mov bx, 7
205         int 10h
206         jmp .loop
207 .done:  ret
208
209 print_hex_word:
210         mov cx, 4       ; 4 digits to print
211         jmp print_n_hex_digits
212
213 print_hex_byte:
214         mov cx, 2       ; 2 digits to print
215         mov ah, al      ; move them in place
216
217 print_n_hex_digits:
218         rol ax, 1
219         rol ax, 1
220         rol ax, 1
221         rol ax, 1
222         mov dx, ax      ; save ax, print_hex_digit destroys it
223         call print_hex_digit
224         mov ax, dx
225         dec cx
226         jnz print_n_hex_digits
227         ret
228
229 print_hex_digit:
230         mov bl, al
231         and bx, 0fh
232         mov al, [bx + .hexdig_tab]
233         mov ah, 0eh
234         mov bx, 7
235         int 10h
236         ret
237
238 .hexdig_tab:
239         db "0123456789abcdef"
240
241 str_rdtrack2    db " from ",0
242 str_rdtrack3    db "/",0
243 str_load_fail   db "Failed to load 2nd stage!",0
244 str_newline     db 13,10,0
245
246         times 510-($-$$) db 0
247 bootsig dw 0xaa55
248
249 ; vi:set ts=8 sts=8 sw=8 ft=nasm: