fixed USB booting issue
[bootcensus] / src / boot / boot.s
1         .code16
2         .section .boot,"a"
3
4         .set stack_top, 0x7be0
5         .set read_retries, 0x7be8
6         .set drive_number, 0x7bec
7         .set cursor_x, 0x7bee
8         .set scratchbuf, 0x7bf0
9         .set scratchbuf_size, 16
10
11 boot:
12         cli
13         # move stack to just below the code
14         xor %ax, %ax
15         mov %ax, %ss
16         mov $stack_top, %sp
17         # use the code segment for data access
18         mov %cs, %ax
19         mov %ax, %ds
20         mov %ax, %es
21
22         mov %dl, drive_number
23
24         call setup_serial
25
26         call get_drive_chs
27
28         mov $loading_msg, %si
29         call print_str
30
31         # load the second stage boot loader and jump to it
32         mov $_boot2_size, %eax
33         call print_num
34
35         mov %eax, %ebx
36         shr $9, %eax
37         and $0x1ff, %ebx
38         jz 0f
39         inc %ax
40 0:      pushw %ax
41         pushw $1
42         # set es to the start of the destination buffer to allow reading in
43         # full 64k chunks
44         mov $boot2_addr, %bx
45         shr $4, %bx
46         mov %bx, %es
47         xor %bx, %bx
48         call read_sectors
49         jmp boot2_addr
50
51         .set ARG_NSECT, 6
52         .set ARG_SIDX, 4
53
54 loading_msg: .asciz "\nLoad "
55 driveno_msg: .asciz "Drive: "
56
57 sect_per_track: .short 18
58 num_cylinders: .short 80
59 num_heads: .short 2
60 heads_mask: .byte 1
61
62 get_drive_chs:
63         mov $driveno_msg, %si
64         call print_str
65         xor %eax, %eax
66         movb drive_number, %dl
67         mov %dl, %al
68         call print_num
69         mov $10, %al
70         call print_char
71
72         mov $8, %ah
73         int $0x13
74         jnc .Lok
75         ret
76
77 .Lok:   xor %eax, %eax
78         mov %ch, %al
79         mov %cl, %ah
80         rol $2, %ah
81         inc %ax
82         and $0x3ff, %ax
83         mov %ax, num_cylinders
84
85         and $0x3f, %cx
86         mov %cx, sect_per_track
87
88         shr $8, %dx
89         mov %dl, heads_mask
90         inc %dx
91         mov %dx, num_heads
92
93         call print_num
94         mov $47, %al
95         call print_char
96         mov %dx, %ax
97         call print_num
98         mov $47, %al
99         call print_char
100         mov %cx, %ax
101         call print_num
102         ret
103
104 # read_sectors(first, num)
105 read_sectors:
106         push %bp
107         mov %sp, %bp
108
109         mov ARG_SIDX(%bp), %ax
110         xor %cx, %cx
111
112         jmp 1f
113 0:      push %ax
114         call read_sector
115         pop %ax
116         inc %ax
117         inc %cx
118 1:      cmp ARG_NSECT(%bp), %cx
119         jnz 0b
120
121         pop %bp
122         ret
123
124 # read_sector(sidx)
125 read_sector:
126         push %bp
127         mov %sp, %bp
128         push %cx
129         push %dx
130
131         movw $3, read_retries
132
133 .Lread_try:
134         # calculate the track (sidx / sectors_per_track)
135         mov 4(%bp), %ax
136
137         xor %dx, %dx
138         mov sect_per_track, %cx
139         div %cx
140         mov %ax, %cx
141         # save the remainder
142         push %dx
143         # head in dh
144         mov %cl, %dh
145         and heads_mask, %dh
146         # cylinder (track/heads) in ch [0-7] and cl[6,7]<-[8,9]
147         push %dx
148         xor %dx, %dx
149         movw num_heads, %cx
150         div %cx
151         pop %dx
152         mov %ax, %cx
153         rol $8, %cx
154         ror $2, %cl
155         and $0xc0, %cl
156         # sector num cl[0-5] is sidx % sectors_per_track + 1
157         pop %ax
158         inc %al
159         or %al, %cl
160
161         # ah = 2 (read), al = 1 sectors
162         mov $0x0201, %ax
163         movb drive_number, %dl
164         int $0x13
165         jnc .Lread_ok
166
167         # abort after 3 attempts
168         decw read_retries
169         jz .Lread_fail
170
171         # error detected, reset controller and retry
172         xor %ah, %ah
173         int $0x13
174         jmp .Lread_try
175
176 .Lread_fail:
177         mov 4(%bp), %ax
178         jmp abort_read
179
180 .Lread_ok:
181         mov $46, %ax
182         call print_char
183
184         # increment es:bx accordingly (advance es if bx overflows)
185         add $512, %bx
186         jnc 0f
187         mov %es, %ax
188         add $4096, %ax
189         mov %ax, %es
190
191 0:      pop %dx
192         pop %cx
193         pop %bp
194         ret
195
196 str_read_error: .asciz "rderr: "
197
198 abort_read:
199         mov $str_read_error, %si
200         call print_str
201         and $0xffff, %eax
202         call print_num
203         mov $10, %al
204         call print_char
205         hlt
206
207         # expects string pointer in ds:si
208 print_str:
209         pusha
210
211 0:      mov (%si), %al
212         cmp $0, %al
213         jz .Lend
214         call print_char
215         inc %si
216         jmp 0b
217
218 .Lend:  popa
219         ret
220
221         # expects character in al
222 print_char:
223         push %es
224         pushw $0xb800
225         pop %es
226         movw cursor_x, %di
227         shl $1, %di
228
229         mov %al, %es:(%di)
230         movb $7, %es:1(%di)
231         incw cursor_x
232
233         call ser_putchar
234         pop %es
235         ret
236
237         # expects number in eax
238         .global print_num
239 print_num:
240         # save registers
241         pusha
242
243         movw $scratchbuf + scratchbuf_size, %si
244         movb $0, (%si)
245         mov $10, %ebx
246 .Lconvloop:
247         xor %edx, %edx
248         div %ebx
249         add $48, %dl
250         dec %si
251         mov %dl, (%si)
252         cmp $0, %eax
253         jnz .Lconvloop
254
255         call print_str
256
257         # restore regs
258         popa
259         ret
260
261         .set UART_DATA, 0x3f8
262         .set UART_DIVLO, 0x3f8
263         .set UART_DIVHI, 0x3f9
264         .set UART_FIFO, 0x3fa
265         .set UART_LCTL, 0x3fb
266         .set UART_MCTL, 0x3fc
267         .set UART_LSTAT, 0x3fd
268         .set DIV_9600, 115200 / 9600
269         .set LCTL_8N1, 0x03
270         .set LCTL_DLAB, 0x80
271         .set FIFO_ENABLE, 0x01
272         .set FIFO_SEND_CLEAR, 0x04
273         .set FIFO_RECV_CLEAR, 0x02
274         .set MCTL_DTR, 0x01
275         .set MCTL_RTS, 0x02
276         .set MCTL_OUT2, 0x08
277         .set LST_TREG_EMPTY, 0x20
278
279 setup_serial:
280         # set clock divisor
281         mov $LCTL_DLAB, %al
282         mov $UART_LCTL, %dx
283         out %al, %dx
284         mov $DIV_9600, %ax
285         mov $UART_DIVLO, %dx
286         out %al, %dx
287         shr $8, %ax
288         mov $UART_DIVHI, %dx
289         out %al, %dx
290         # set format 8n1
291         mov $LCTL_8N1, %al
292         mov $UART_LCTL, %dx
293         out %al, %dx
294         # clear and enable fifo
295         mov $FIFO_ENABLE, %al
296         or $FIFO_SEND_CLEAR, %al
297         or $FIFO_RECV_CLEAR, %al
298         mov $UART_FIFO, %dx
299         out %al, %dx
300         # assert RTS and DTR
301         mov $MCTL_DTR, %al
302         or $MCTL_RTS, %al
303         or $MCTL_OUT2, %al
304         mov $UART_MCTL, %dx
305         out %al, %dx
306         ret
307
308         # expects a character in al
309         .global ser_putchar
310 ser_putchar:
311         push %dx
312
313         cmp $10, %al
314         jnz 0f
315         push %ax
316         mov $13, %al
317         call ser_putchar
318         pop %ax
319
320 0:      mov %al, %ah
321         # wait until the transmit register is empty
322         mov $UART_LSTAT, %dx
323 .Lwait: in %dx, %al
324         and $LST_TREG_EMPTY, %al
325         jz .Lwait
326         mov $UART_DATA, %dx
327         mov %ah, %al
328         out %al, %dx
329
330         pop %dx
331         ret
332         
333         .org 510
334         .byte 0x55
335         .byte 0xaa
336 boot2_addr: