initial commit
[eightysix] / boot / boot.asm
1 ; first stage boot loader
2         cpu 8086
3         bits 16
4         section .bootsect
5
6 extern _stage2_start
7 extern _stage2_end
8
9 stacktop        equ 7b00h
10 boot_driveno    equ 7b00h
11 num_read_tries  equ 7b06h       ; 2 bytes
12 sect_pending    equ 7b08h       ; 2 bytes
13 sect_per_track  equ 7b0ah       ; 2 bytes
14 cur_head        equ 7b0ch       ; 2 bytes - current head
15 start_sect      equ 7b0eh       ; 2 bytes - start sector in track
16 destptr         equ 7b10h       ; 2 bytes - destination pointer
17 num_heads       equ 7b12h       ; 2 bytes - number of heads
18 cur_cyl         equ 7b14h       ; 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
32 bios_param_block:
33         jmp start       ; 2 bytes
34         nop             ; 1 byte
35         ; start of BPB at offset 3
36         db "BSPL 0.1"   ; 03h: OEM ident, 8 bytes
37         dw 512          ; 0bh: bytes per sector
38         db 1            ; 0dh: sectors per cluster
39         dw 1            ; 0eh: reserved sectors (including boot record)
40         db 2            ; 10h: number of FATs
41         dw 224          ; 11h: number of dir entries
42         dw 2880         ; 13h: number of sectors in volume
43         db 0fh          ; 15h: media descriptor type (f = 3.5" HD floppy)
44         dw 9            ; 16h: number of sectors per FAT
45         dw 18           ; 18h: number of sectors per track
46         dw 2            ; 1ah: number of heads
47         dd 0            ; 1ch: number of hidden sectors
48         dd 0            ; 20h: high bits of sector count
49         db 0            ; 24h: drive number
50         db 0            ; 25h: winnt flags
51         db 28h          ; 26h: signature(?)
52         dd 0            ; 27h: volume serial number
53         db "80 SIX BOOT"; 2bh: volume label, 11 bytes
54         db "FAT12   "   ; 36h: filesystem id, 8 bytes
55
56 start:
57         cld
58         xor ax, ax
59         mov ds, ax
60         mov es, ax
61         mov ss, ax
62         mov gs, ax
63         mov fs, ax
64         jmp 00:.setcs
65 .setcs:
66         mov sp, stacktop
67         mov [boot_driveno], dl
68
69         ; query sectors per track
70         mov ah, 8       ; get drive parameters call, dl already has the drive
71         xor di, di
72         int 13h
73         jc .queryfail
74         and cx, 3fh
75         mov [sect_per_track], cx
76         mov cl, 8
77         shr dx, cl
78         inc dx
79         mov [num_heads], dx
80         jmp .querydone
81 .queryfail:
82         ; in case of failure, try 18 sectors per track, 2 heads
83         mov word [sect_per_track], 18
84         mov word [num_heads], 2
85 .querydone:
86
87 ; load the rest of the code at 7e00h
88         mov ax, _stage2_end
89         sub ax, _stage2_start
90         add ax, 511
91         mov cl, 9
92         shr ax, cl
93         inc ax
94         mov [sect_pending], ax
95         mov ax, _stage2_start
96         shr ax, 1
97         shr ax, 1
98         shr ax, 1
99         shr ax, 1
100         mov es, ax      ; destination segment 7e0h to allow loading up to 64k
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, reset es back to 0 and jump
174 .done:  xor ax, ax
175         mov es, ax
176         jmp _stage2_start
177
178 .fail:  add sp, 2       ; clear num_sect off the stack
179         dec word [num_read_tries]
180         jz .abort
181
182         ; reset controller and retry
183         xor ax, ax
184         mov dl, [boot_driveno]
185         int 13h
186         jmp .rdloop
187
188         ; failed to load second sector
189 .abort: xor ax, ax
190         mov es, ax
191         floppy_motor_off        ; turn off floppy motor (if dl is < 80h)
192         mov ax, str_load_fail
193         call printstr
194 .hang:  hlt
195         jmp .hang
196
197         ; expects string ptr in ax
198 printstr:
199         mov si, ax
200 .loop:  mov al, [si]
201         inc si
202         test al, al
203         jz .done
204         mov ah, 0eh
205         mov bx, 7
206         int 10h
207         jmp .loop
208 .done:  ret
209
210 print_hex_word:
211         mov cx, 4       ; 4 digits to print
212         jmp print_n_hex_digits
213
214 print_hex_byte:
215         mov cx, 2       ; 2 digits to print
216         mov ah, al      ; move them in place
217
218 print_n_hex_digits:
219         rol ax, 1
220         rol ax, 1
221         rol ax, 1
222         rol ax, 1
223         mov dx, ax      ; save ax, print_hex_digit destroys it
224         call print_hex_digit
225         mov ax, dx
226         dec cx
227         jnz print_n_hex_digits
228         ret
229
230 print_hex_digit:
231         mov bl, al
232         and bx, 0fh
233         mov al, [bx + .hexdig_tab]
234         mov ah, 0eh
235         mov bx, 7
236         int 10h
237         ret
238
239 .hexdig_tab:
240         db "0123456789abcdef"
241
242 str_rdtrack2    db " from ",0
243 str_rdtrack3    db "/",0
244 str_load_fail   db "Failed to load 2nd stage!",0
245 str_newline     db 13,10,0
246
247         times 510-($-$$) db 0
248 bootsig dw 0xaa55
249
250 ; vi:set ts=8 sts=8 sw=8 ft=nasm: