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