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