progress on all fronts
[com32] / src / loader.asm
1         section .loader
2
3         extern startup
4         extern _ldr_main_start
5         extern _main_start
6         extern _main_size
7         extern boot_mem_map
8         extern boot_mem_map_size
9
10 %include 'macros.inc'
11 %define CON_SERIAL
12
13         [bits 16]
14         global _start
15 _start:
16         cli
17         mov ax, cs
18         mov ds, ax
19         mov es, ax
20         mov fs, ax      ; this will store the original real mode segment
21         mov ss, ax
22         ; modify the return to real mode jump segment
23         mov [.jmpcs16 + 3], ax
24
25         xor ax, ax
26         mov sp, ax
27
28         ; check for VM86 and abort
29         mov eax, cr0
30         test ax, 1
31         jz .notvm86
32
33         mov si, str_errvm86
34         call printstr
35         jmp exit
36
37 .notvm86:
38 %ifdef CON_SERIAL
39         call setup_serial
40 %endif
41         call enable_a20
42         call detect_memory
43
44         ; calculate GDT linear address
45         xor eax, eax
46         mov ax, cs
47         shl eax, 4
48         add eax, gdt
49         mov [gdt_base], eax
50
51         ; set tmp segment bases to match the linear address of our current seg
52         xor eax, eax
53         mov ax, cs
54         shl eax, 4
55         mov word [gdt + 18h + 2], ax    ; tmp pm code base
56         mov word [gdt + 20h + 2], ax    ; tmp pm data base
57         mov word [gdt + 28h + 2], ax    ; ret-to-realmode code
58         shr eax, 16
59         mov byte [gdt + 18h + 4], al
60         mov byte [gdt + 20h + 4], al
61         mov byte [gdt + 28h + 4], al
62
63         mov si, str_enterpm
64         call printstr
65
66         lgdt [gdt_lim]
67
68         mov eax, cr0
69         or eax, 1
70         mov cr0, eax
71
72         jmp 18h:.pm
73
74         [bits 32]
75 .pm:    mov ax, 20h     ; tmp data selector
76         mov ds, ax
77         mov ax, 10h     ; dest data selector
78         mov es, ax
79
80         ; copy main program high
81         cld
82         mov esi, _ldr_main_start
83         mov edi, _main_start
84         mov ecx, _main_size + 3
85         shr ecx, 2
86         rep movsd
87
88         ; copy memory map
89         mov eax, [mem_map_size]
90         mov [es:boot_mem_map_size], eax
91         mov esi, mem_map
92         mov edi, boot_mem_map
93         mov ecx, 32     ; 128 bytes
94         rep movsd
95
96         mov ax, 10h
97         mov ds, ax
98         mov ss, ax
99         mov esp, _main_start
100
101         call 8:startup
102         cli
103
104         ; return to real mode
105         jmp 28h:.rm
106
107         [bits 16]
108 .rm:    mov eax, cr0
109         and ax, 0xfffe
110         mov cr0, eax
111 .jmpcs16:
112         jmp 42h:.loadcs16       ; 42 seg is modifed at the start
113 .loadcs16:
114         mov ax, fs
115         mov ds, ax
116         mov es, ax
117         mov ss, ax
118
119         ; restore real-mode IVT
120         lidt [rmidt]
121         sti
122
123         mov ax, 3
124         int 10h
125 exit:   mov ax, 4c00h
126         int 21h
127
128 str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0
129 str_enterpm db 'Entering 32bit protected mode ...',10,0
130
131 enable_a20:
132         call test_a20
133         jnc .ret
134
135         mov si, .infomsg
136         call printstr           ; print "Enable A20 line ... "
137
138         call enable_a20_kbd
139         call test_a20
140         jnc .done
141         call enable_a20_fast
142         call test_a20
143         jnc .done
144         mov si, .failstr
145         call printstr
146         mov ax, 4c00h
147         int 21h
148 .done:  mov si, .okstr
149         call printstr
150 .ret:   ret
151
152 .infomsg db 'Enable A20 line:',0
153 .failstr db ' failed.',10,0
154 .okstr  db ' success.',10,0
155
156         ; CF = 1 if A20 test fails (not enabled)
157 test_a20:
158         push ds
159         push es
160         xor ax, ax
161         mov ds, ax
162         not ax
163         mov es, ax
164         mov si, 420h
165         mov di, 430h
166         mov dl, [ds:si]
167         mov dh, [es:di]
168         mov [ds:si], al
169         not ax
170         mov [es:di], al
171         cmp al, [ds:si]
172         clc
173         jnz .done
174         stc
175 .done:  mov [ds:si], dl
176         mov [es:di], dh
177         pop es
178         pop ds
179         ret
180
181 enable_a20_fast:
182         mov si, .info
183         call printstr
184         in al, 92h
185         or al, 2
186         out 92h, al
187         ret
188 .info db ' fast ...',0
189
190 KBC_DATA_PORT equ 0x60
191 KBC_CMD_PORT equ 0x64
192 KBC_STATUS_PORT equ 0x64
193 KBC_CMD_WR_OUTPORT equ 0xd1
194
195 KBC_STAT_IN_FULL equ 2
196
197 enable_a20_kbd:
198         mov si, .info
199         call printstr
200         call kbc_wait_write
201         mov al, KBC_CMD_WR_OUTPORT
202         out KBC_CMD_PORT, al
203         call kbc_wait_write
204         mov al, 0xdf
205         out KBC_DATA_PORT, al
206         ret
207 .info db ' kbd ...',0
208
209 kbc_wait_write:
210         in al, KBC_STATUS_PORT
211         and al, KBC_STAT_IN_FULL
212         jnz kbc_wait_write
213         ret
214
215
216 ; ---------------------- memory detection -----------------------
217
218 detect_memory:
219         mov si, memdet_detram
220         call printstr
221         mov si, memdet_e820_msg
222         call printstr
223         call detect_mem_e820
224         jnc .done
225         mov si, str_fail
226         call printstr
227
228         mov si, memdet_detram
229         call printstr
230         mov si, memdet_e801_msg
231         call printstr
232         call detect_mem_e801
233         jnc .done
234         mov si, str_fail
235         call printstr
236
237         mov si, memdet_detram
238         call printstr
239         mov esi, memdet_88_msg
240         call printstr
241         call detect_mem_88
242         jnc .done
243         mov esi, str_fail
244         call printstr
245
246         mov si, memdet_detram
247         call printstr
248         mov esi, memdet_cmos_msg
249         call printstr
250         call detect_mem_cmos
251         jnc .done
252         mov esi, str_fail
253         call printstr
254
255         mov si, memdet_fail_msg
256         call printstr
257         jmp exit
258 .done:
259         mov si, str_ok
260         call printstr
261         ret
262
263 str_ok db 'OK',10,0
264 str_fail db 'failed',10,0
265 memdet_fail_msg db 'Failed to detect available memory!',10,0
266 memdet_detram   db 'Detecting RAM '
267 memdet_e820_msg db '(BIOS 15h/0xe820)... ',0
268 memdet_e801_msg db '(BIOS 15h/0xe801)... ',0
269 memdet_88_msg   db '(BIOS 15h/0x88, max 64mb)... ',0
270 memdet_cmos_msg db '(CMOS)...',0
271
272         ; detect extended memory using BIOS call 15h/e820
273 detect_mem_e820:
274         mov dword [mem_map_size], 0
275
276         mov edi, .buffer
277         xor ebx, ebx
278         mov edx, 534d4150h
279
280 .looptop:
281         mov eax, 0xe820
282         mov ecx, 24
283         int 15h
284         jc .fail
285         cmp eax, 534d4150h
286         jnz .fail
287
288         ; skip areas starting above 4GB as we won't be able to use them
289         cmp dword [di + 4], 0
290         jnz .skip
291
292         ; only care for type 1 (usable ram), otherwise ignore
293         cmp dword [di + 16], 1
294         jnz .skip
295
296         mov eax, [.buffer]
297         mov esi, mem_map
298         mov ebp, [mem_map_size]
299         mov [ebp * 8 + esi], eax
300
301         ; skip areas with 0 size (also clamp size to 4gb)
302         ; test high 32bits
303         cmp dword [edi + 12], 0
304         jz .highzero
305         ; high part is non-zero, make low part ffffffff
306         xor eax, eax
307         not eax
308         jmp .skiph0
309
310 .highzero:
311         ; if both high and low parts are zero, ignore
312         mov eax, [di + 8]
313         test eax, eax
314         jz .skip
315
316 .skiph0:mov [ebp * 8 + esi + 4], eax
317         inc dword [mem_map_size]
318
319 .skip:
320         ; terminate the loop if ebx was reset to 0
321         test ebx, ebx
322         jz .done
323         jmp .looptop
324 .done:
325         clc
326         ret
327
328 .fail:  ; if size > 0, then it's not a failure, just the end
329         cmp dword [mem_map_size], 0
330         jnz .done
331         stc
332         ret
333
334 .buffer times 32 db 0
335
336         ; detect extended memory using BIOS call 15h/e801
337 detect_mem_e801:
338         mov si, mem_map
339         mov ebp, [mem_map_size]
340         mov dword [ebp], 0
341
342         xor cx, cx
343         xor dx, dx
344         mov ax, 0xe801
345         int 15h
346         jc .fail
347
348         test cx, cx
349         jnz .foo1
350         test ax, ax
351         jz .fail
352         mov cx, ax
353         mov dx, bx
354
355 .foo1:  mov dword [si], 100000h
356         movzx eax, cx
357         ; first size is in KB, convert to bytes
358         shl eax, 10
359         jnc .foo2
360         ; overflow means it's >4GB, clamp to 4GB
361         mov eax, 0xffffffff
362 .foo2:  mov [si + 4], eax
363         inc dword [mem_map_size]
364         test dx, dx
365         jz .done
366         mov dword [si + 8], 1000000h
367         movzx eax, dx
368         ; second size is in 64kb blocks, convert to bytes
369         shl eax, 16
370         jnc .foo3
371         ; overflow means it's >4GB, clamp to 4GB
372         mov eax, 0xffffffff
373 .foo3:  mov [si + 12], eax
374         inc dword [mem_map_size]
375 .done:
376         clc
377         ret
378 .fail:
379         stc
380         ret
381
382 detect_mem_88:
383         ; reportedly some BIOS implementations fail to clear CF on success
384         clc
385         mov ah, 88h
386         int 15h
387         jc .fail
388
389         test ax, ax
390         jz .fail
391
392         ; ax has size in KB, convert to bytes in eax
393         and eax, 0xffff
394         shl eax, 10
395
396         mov esi, mem_map
397         mov dword [si], 100000h
398         mov [si + 4], eax
399
400         mov dword [mem_map_size], 1
401         clc
402         ret
403 .fail:  stc
404         ret
405
406 detect_mem_cmos:
407         mov al, 31h
408         out 70h, al
409         in al, 71h
410         mov ah, al
411         mov al, 30h
412         out 70h, al
413         in al, 71h
414
415         test ax, ax
416         jz .fail
417
418         ; ax has size in KB, convert to bytes in eax
419         and eax, 0xffff
420         shl eax, 10
421
422         mov esi, mem_map
423         mov dword [si], 100000h
424         mov [si + 4], eax
425         mov dword [mem_map_size], 1
426         clc
427         ret
428 .fail:  stc
429         ret
430
431
432         align 4
433 mem_map_size dd 0
434 mem_map times 128 db 0
435
436
437 ; ----------------------- serial console ------------------------
438
439 %ifdef CON_SERIAL
440 setup_serial:
441         ; set clock divisor
442         mov dx, UART_BASE + 3   ; LCTL
443         mov al, 80h             ; DLAB
444         out dx, al
445         mov dx, UART_BASE       ; DIVLO
446         mov al, UART_DIVISOR & 0xff
447         out dx, al
448         inc dx                  ; DIVHI
449         mov al, UART_DIVISOR >> 8
450         out dx, al
451         mov dx, UART_BASE + 3   ; LCTL
452         mov al, 3               ; 8n1
453         out dx, al
454         inc dx                  ; MCTL
455         mov al, 0xb             ; DTR/RTS/OUT2
456         out dx, al
457         ret
458
459 ser_putchar:
460         SER_PUTCHAR
461         ret
462
463 ser_printstr:
464         lodsb
465         test al, al
466         jz .end
467         cmp al, 10
468         jnz .nolf
469         push ax
470         mov al, 13
471         call ser_putchar
472         pop ax
473 .nolf:  call ser_putchar
474         jmp ser_printstr
475 .end:   ret
476
477 %endif ; def CON_SERIAL
478
479
480 printstr:
481         lodsb
482         test al, al
483         jz .end
484         cmp al, 10      ; check for line-feed and insert CR before it
485         jnz .nolf
486         push ax
487 %ifdef CON_SERIAL
488         mov al, 13
489         call ser_putchar
490 %endif
491         mov dl, 13
492         mov ah, 2
493         int 21h
494         pop ax
495 .nolf:
496 %ifdef CON_SERIAL
497         call ser_putchar
498 %endif
499         mov ah, 2
500         mov dl, al
501         int 21h
502         jmp printstr
503 .end:   ret
504
505
506         align 4
507 enterpm dd 0xbad00d     ; space for linear address for far jump to pmode
508 enterpm_sel dw 8        ; selector for far jump to protected mode
509         align 4
510 gdt_lim dw 47           ; GDT limit
511 gdt_base dd 0xbadf00d   ; space for GDT linear address
512
513         align 8
514 gdt:    ; 0: null segment
515         dd 0
516         dd 0
517         ; 1: code - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd (sel: 8)
518         dd 0000ffffh
519         dd 00cf9a00h
520         ; 2: data - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw (sel: 10h)
521         dd 0000ffffh
522         dd 00cf9200h
523         ; 3: tmp code (will set base before entering pmode) (sel: 18h)
524         dd 0000ffffh
525         dd 00cf9a00h
526         ; 4: tmp data (will set base before entering pmode) (sel: 20h)
527         dd 0000ffffh
528         dd 00cf9200h
529         ; 5: return to real-mode 16bit code segment (sel: 28h)
530         dd 0000ffffh
531         dd 00009a00h
532
533         ; pseudo IDTR descriptor for real-mode IVT at address 0
534         align 4
535         dw 0
536 rmidt:  dw 3ffh         ; IVT limit (1kb / 256 entries)
537         dd 0            ; IVT base 0
538
539 ; vi:set ts=8 sts=8 sw=8 ft=nasm: