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