porting over pcboot codebase
[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
8 %include 'macros.inc'
9
10         [bits 16]
11         global _start
12 _start:
13         cli
14         mov ax, cs
15         mov ds, ax
16         mov es, ax
17         mov fs, ax      ; this will store the original real mode segment
18         mov ss, ax
19         ; modify the return to real mode jump segment
20         mov [.jmpcs16 + 3], ax
21
22         xor ax, ax
23         mov sp, ax
24
25         ; check for VM86 and abort
26         mov eax, cr0
27         test ax, 1
28         jz .notvm86
29
30         mov si, str_errvm86
31         call printstr
32         jmp exit
33
34 .notvm86:
35         call setup_serial
36         call enable_a20
37
38         ; calculate GDT linear address
39         xor eax, eax
40         mov ax, cs
41         shl eax, 4
42         add eax, gdt
43         mov [gdt_base], eax
44
45         ; set tmp segment bases to match the linear address of our current seg
46         xor eax, eax
47         mov ax, cs
48         shl eax, 4
49         mov word [gdt + 18h + 2], ax    ; tmp pm code base
50         mov word [gdt + 20h + 2], ax    ; tmp pm data base
51         mov word [gdt + 28h + 2], ax    ; ret-to-realmode code
52         shr eax, 16
53         mov byte [gdt + 18h + 4], al
54         mov byte [gdt + 20h + 4], al
55         mov byte [gdt + 28h + 4], al
56
57         mov si, str_enterpm
58         call printstr
59
60         lgdt [gdt_lim]
61
62         mov eax, cr0
63         or eax, 1
64         mov cr0, eax
65
66         jmp 18h:.pm
67
68         [bits 32]
69 .pm:    mov ax, 20h     ; tmp data selector
70         mov ds, ax
71         mov ax, 10h     ; dest data selector
72         mov es, ax
73
74         ; copy main program high
75         cld
76         mov esi, _ldr_main_start
77         mov edi, _main_start
78         mov ecx, _main_size + 3
79         shr ecx, 2
80         rep movsd
81
82         mov ax, 10h
83         mov ds, ax
84         mov ss, ax
85         mov esp, _main_start
86
87         call 8:startup
88
89         PMPRINT 'X'
90         PMPRINT 'Y'
91         PMPRINT 'Z'
92         PMPRINT 'Z'
93         PMPRINT 'Y'
94         PMPRINT 13
95         PMPRINT 10
96
97         ; return to real mode
98         jmp 28h:.rm
99
100         [bits 16]
101 .rm:    mov eax, cr0
102         and ax, 0xfffe
103         mov cr0, eax
104 .jmpcs16:
105         jmp 42h:.loadcs16       ; 42 seg is modifed at the start
106 .loadcs16:
107         mov ax, fs
108         mov ds, ax
109         mov es, ax
110         mov ss, ax
111
112 exit:   mov ax, 4c00h
113         int 21h
114
115 dbg_inpm db 'DBG: in pmode',10,0
116 dbg_retrm db 'DBG: returning to real mode...',10,0
117
118 str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0
119 str_enterpm db 'Entering 32bit protected mode ...',10,0
120
121 enable_a20:
122         call test_a20
123         jnc .ret
124
125         mov si, .infomsg
126         call printstr           ; print "Enable A20 line ... "
127
128         call enable_a20_kbd
129         call test_a20
130         jnc .done
131         call enable_a20_fast
132         call test_a20
133         jnc .done
134         mov si, .failstr
135         call printstr
136         mov ax, 4c00h
137         int 21h
138 .done:  mov si, .okstr
139         call printstr
140 .ret:   ret
141
142 .infomsg db 'Enable A20 line:',0
143 .failstr db ' failed.',10,0
144 .okstr  db ' success.',10,0
145
146         ; CF = 1 if A20 test fails (not enabled)
147 test_a20:
148         push ds
149         push es
150         xor ax, ax
151         mov ds, ax
152         not ax
153         mov es, ax
154         mov si, 420h
155         mov di, 430h
156         mov dl, [ds:si]
157         mov dh, [es:di]
158         mov [ds:si], al
159         not ax
160         mov [es:di], al
161         cmp al, [ds:si]
162         clc
163         jnz .done
164         stc
165 .done:  mov [ds:si], dl
166         mov [es:di], dh
167         pop es
168         pop ds
169         ret
170
171 enable_a20_fast:
172         mov si, .info
173         call printstr
174         in al, 92h
175         or al, 2
176         out 92h, al
177         ret
178 .info db ' fast ...',0
179
180 KBC_DATA_PORT equ 0x60
181 KBC_CMD_PORT equ 0x64
182 KBC_STATUS_PORT equ 0x64
183 KBC_CMD_WR_OUTPORT equ 0xd1
184
185 KBC_STAT_IN_FULL equ 2
186
187 enable_a20_kbd:
188         mov si, .info
189         call printstr
190         call kbc_wait_write
191         mov al, KBC_CMD_WR_OUTPORT
192         out KBC_CMD_PORT, al
193         call kbc_wait_write
194         mov al, 0xdf
195         out KBC_DATA_PORT, al
196         ret
197 .info db ' kbd ...',0
198
199 kbc_wait_write:
200         in al, KBC_STATUS_PORT
201         and al, KBC_STAT_IN_FULL
202         jnz kbc_wait_write
203         ret
204
205 UART_BASE       equ 2f8h                ; COM1: 3f8, COM2: 2f8
206 UART_DIVISOR    equ 115200 / 9600       ; 9600 baud
207 setup_serial:
208         ; set clock divisor
209         mov dx, UART_BASE + 3   ; LCTL
210         mov al, 80h             ; DLAB
211         out dx, al
212         mov dx, UART_BASE       ; DIVLO
213         mov al, UART_DIVISOR & 0xff
214         out dx, al
215         inc dx                  ; DIVHI
216         mov al, UART_DIVISOR >> 8
217         out dx, al
218         mov dx, UART_BASE + 3   ; LCTL
219         mov al, 3               ; 8n1
220         out dx, al
221         inc dx                  ; MCTL
222         mov al, 0xb             ; DTR/RTS/OUT2
223         out dx, al
224         ret
225
226 ser_putchar:
227         SER_PUTCHAR
228         ret
229
230 ser_printstr:
231         lodsb
232         test al, al
233         jz .end
234         cmp al, 10
235         jnz .nolf
236         push ax
237         mov al, 13
238         call ser_putchar
239         pop ax
240 .nolf:  call ser_putchar
241         jmp ser_printstr
242 .end:   ret
243
244 printstr:
245         lodsb
246         test al, al
247         jz .end
248         cmp al, 10      ; check for line-feed and insert CR before it
249         jnz .nolf
250         push ax
251         mov al, 13
252         call ser_putchar
253         mov dl, 13
254         mov ah, 2
255         int 21h
256         pop ax
257 .nolf:  call ser_putchar
258         mov ah, 2
259         mov dl, al
260         int 21h
261         jmp printstr
262 .end:   ret
263
264         [bits 32]
265 ser_putchar_pmode:
266         SER_PUTCHAR
267         ret
268
269 ser_putstr_pmode:
270         lodsb
271         test al, al
272         jz .end
273         cmp al, 10
274         jnz .nolf
275         push ax
276         mov al, 13
277         call ser_putchar_pmode
278         pop ax
279 .nolf:  call ser_putchar_pmode
280         jmp ser_putstr_pmode
281 .end:   ret
282         
283
284         align 4
285 enterpm dd 0xbad00d     ; space for linear address for far jump to pmode
286 enterpm_sel dw 8        ; selector for far jump to protected mode
287         align 4
288 gdt_lim dw 47           ; GDT limit
289 gdt_base dd 0xbadf00d   ; space for GDT linear address
290
291         align 8
292 gdt:    ; 0: null segment
293         dd 0
294         dd 0
295         ; 1: code - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd (sel: 8)
296         dd 0000ffffh
297         dd 00cf9a00h
298         ; 2: data - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw (sel: 10h)
299         dd 0000ffffh
300         dd 00cf9200h
301         ; 3: tmp code (will set base before entering pmode) (sel: 18h)
302         dd 0000ffffh
303         dd 00cf9a00h
304         ; 4: tmp data (will set base before entering pmode) (sel: 20h)
305         dd 0000ffffh
306         dd 00cf9200h
307         ; 5: return to real-mode 16bit code segment (sel: 28h)
308         dd 0000ffffh
309         dd 00009a00h
310
311
312 ; vi:set ts=8 sts=8 sw=8 ft=nasm: