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