96506de177878426b08c7da880baebfe4cde1719
[eightysix] / kern / src / 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 ; 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 "80 SIX BOOT"; 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         jmp 00:.setcs
69 .setcs:
70         ; put the stack high
71         mov ax, 0x7f00
72         mov ss, ax
73         xor sp, sp
74         mov [boot_driveno], dl
75
76         ; query sectors per track
77         mov ah, 8       ; get drive parameters call, dl already has the drive
78         xor di, di
79         int 13h
80         jc .queryfail
81         and cx, 3fh
82         mov [sect_per_track], cx
83         mov cl, 8
84         shr dx, cl
85         inc dx
86         mov [num_heads], dx
87         jmp .querydone
88 .queryfail:
89         ; in case of failure, try 18 sectors per track, 2 heads
90         mov word [sect_per_track], 18
91         mov word [num_heads], 2
92 .querydone:
93
94         ; load the rest of the code high
95         mov ax, _stage2_size
96         add ax, 511
97         mov cl, 9
98         shr ax, cl
99         inc ax
100         mov [sect_pending], ax
101         mov ax, _stage2_start_seg
102         mov es, ax      ; destination segment
103         mov word [destptr], 0
104         mov word [start_sect], 1        ; start from sector 1 to skip boot sector
105         mov word [cur_cyl], 0
106         mov word [cur_head], 0
107
108 .rdloop:
109         mov cx, [start_sect]
110         mov ax, [sect_pending]
111         sub ax, cx              ; num_sect = pending - start_sect
112         cmp ax, [sect_per_track]
113         jbe .noadj
114         mov ax, [sect_per_track]        ; read max sect_per_track at a time
115         sub ax, cx
116 .noadj: test ax, ax
117         jz .done
118         push ax         ; save how many sectors we're reading
119
120         push ax
121         push bx
122         push cx
123         push dx
124         push si
125         call print_hex_word
126         mov ax, str_rdtrack2
127         call printstr
128         mov ax, [cur_cyl]
129         call print_hex_byte
130         mov ax, str_rdtrack3
131         call printstr
132         mov ax, [cur_head]
133         call print_hex_byte
134         mov ax, str_rdtrack3
135         call printstr
136         mov ax, [start_sect]
137         call print_hex_byte
138         mov ax, str_newline
139         call printstr
140         pop si
141         pop dx
142         pop cx
143         pop bx
144         pop ax
145
146         mov ch, [cur_cyl]
147         mov dh, [cur_head]
148         mov bx, [destptr]
149
150         inc cl                  ; sector numbers start from 1
151         mov ah, 2               ; read sectors is call 2
152         mov dl, [boot_driveno]
153         int 13h
154         jc .fail
155         ; track read sucessfully
156         mov word [start_sect], 0        ; all subsequent tracks are whole
157         mov ax, [cur_head]
158         inc ax
159         cmp ax, [num_heads]
160         jnz .skip_cyl_adv
161         xor ax, ax
162         inc byte [cur_cyl]
163 .skip_cyl_adv:
164         mov [cur_head], ax
165
166         pop ax                  ; num_sect
167         sub [sect_pending], ax
168         push cx
169         mov cl, 9
170         shl ax, cl              ; convert to bytes
171         pop cx
172         add [destptr], ax
173         jnz .rdloop
174
175         ; loaded sucessfully, load segment registers and jump
176 .done:  mov ax, _stage2_start_seg
177         mov ds, ax
178         mov es, ax
179         push ax
180         xor ax, ax
181         push ax
182         retf
183
184
185 .fail:  add sp, 2       ; clear num_sect off the stack
186         dec word [num_read_tries]
187         jz .abort
188
189         ; reset controller and retry
190         xor ax, ax
191         mov dl, [boot_driveno]
192         int 13h
193         jmp .rdloop
194
195         ; failed to load second sector
196 .abort: xor ax, ax
197         mov es, ax
198         floppy_motor_off        ; turn off floppy motor (if dl is < 80h)
199         mov ax, str_load_fail
200         call printstr
201 .hang:  hlt
202         jmp .hang
203
204         ; expects string ptr in ax
205 printstr:
206         mov si, ax
207 .loop:  mov al, [si]
208         inc si
209         test al, al
210         jz .done
211         mov ah, 0eh
212         mov bx, 7
213         int 10h
214         jmp .loop
215 .done:  ret
216
217 print_hex_word:
218         mov cx, 4       ; 4 digits to print
219         jmp print_n_hex_digits
220
221 print_hex_byte:
222         mov cx, 2       ; 2 digits to print
223         mov ah, al      ; move them in place
224
225 print_n_hex_digits:
226         rol ax, 1
227         rol ax, 1
228         rol ax, 1
229         rol ax, 1
230         mov dx, ax      ; save ax, print_hex_digit destroys it
231         call print_hex_digit
232         mov ax, dx
233         dec cx
234         jnz print_n_hex_digits
235         ret
236
237 print_hex_digit:
238         mov bl, al
239         and bx, 0fh
240         mov al, [bx + .hexdig_tab]
241         mov ah, 0eh
242         mov bx, 7
243         int 10h
244         ret
245
246 .hexdig_tab:
247         db "0123456789abcdef"
248
249 str_rdtrack2    db " from ",0
250 str_rdtrack3    db "/",0
251 str_load_fail   db "Failed to load 2nd stage!",0
252 str_newline     db 13,10,0
253
254         times 510-($-$$) db 0
255 bootsig dw 0xaa55
256
257 ; vi:set ts=8 sts=8 sw=8 ft=nasm: