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