]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/cpu/protvcpi.asm
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / cpu / protvcpi.asm
1 ; protvcpi.asm
2 ;
3 ; Test program: Protected mode via VCPI
4 ; (C) 2010-2012 Jonathan Campbell.
5 ; Hackipedia DOS library.
6 ;
7 ; This code is licensed under the LGPL.
8 ; <insert LGPL legal text here>
9 ;
10 ; proot of concept:
11 ; switching the CPU into 386 16-bit protected mode (and back) using VCPI
12 bits 16                 ; 16-bit real mode
13 org 0x100               ; MS-DOS .COM style
14
15 ; assume ES == DS and SS == DS and DS == CS
16
17 ; SELECTORS
18 NULL_SEL        equ             0
19 CODE_SEL        equ             8
20 DATA_SEL        equ             16
21 VIDEO_SEL       equ             24
22 CODE32_SEL      equ             32
23 DATA32_SEL      equ             40
24 VCPI0_SEL       equ             48
25 VCPI1_SEL       equ             56
26 VCPI2_SEL       equ             64
27 TSS_SEL         equ             72
28 TSS2_SEL        equ             80
29 MAX_SEL         equ             88
30
31 ; ===== ENTRY POINT
32                 call            cpu_is_386
33                 je              is_386
34                 mov             dx,str_cpu_not_386
35                 jmp             exit2dos_with_message
36 is_386:
37
38 ; ===== CHECK FOR VIRTUAL 8086 MODE. IN THIS CASE WE WANT IT, IT MEANS VCPI IS PRESENT
39                 smsw            ax                      ; 386 or higher: If we're in real mode
40                 test            al,1                    ; and bit 0 is already set, we're in virtual 8086
41                 jnz             isnt_realmode           ; and our switch to prot mode will cause problems.
42                 mov             dx,str_cpu_need_v86_mode
43                 jmp             exit2dos_with_message
44 isnt_realmode:
45
46 ; choose memory location for PAGE0, PAGEDIR. they must be 4K aligned
47                 xor             ecx,ecx
48                 mov             cx,cs
49                 shl             ecx,4
50
51                 lea             edi,[ecx+ENDOI]
52                 add             edi,0xFFF
53                 and             edi,0xF000
54                 sub             edi,ecx
55                 mov             dword [PAGE0],edi
56                 add             edi,0x1000
57                 mov             dword [PAGEDIR],edi
58
59 ; ===== CHECK FOR VALID INT 67h
60                 xor             ax,ax
61                 mov             es,ax
62                 mov             ax,[es:(0x67*4)]
63                 or              ax,[es:(0x67*4)+2]
64                 or              ax,ax
65                 jnz             int67_valid
66                 mov             dx,str_cpu_need_int67
67                 jmp             exit2dos_with_message
68
69 ; ===== CHECK FOR VCPI
70 int67_valid:    mov             ax,0xDE00
71                 int             67h
72                 cmp             ah,0
73                 jz              vcpi_valid
74                 mov             dx,str_cpu_need_vcpi
75                 jmp             exit2dos_with_message
76
77 ; fill in the page table, get VCPI selectors, and entry point
78 vcpi_valid:     mov             ax,0xDE01
79                 push            ds
80                 pop             es
81                 mov             edi,[PAGE0]
82                 mov             esi,GDT + VCPI0_SEL
83                 int             67h
84                 cmp             ah,0
85                 jz              vcpi_valid2
86                 mov             dx,str_cpu_need_vcpi_info
87                 jmp             exit2dos_with_message
88 vcpi_valid2:    mov             dword [vcpi_entry],ebx
89
90 ; ===== zero both TSS
91                 cld
92                 push            ds
93                 pop             es
94                 xor             ax,ax
95
96                 mov             di,TSS1
97                 mov             cx,104/2
98                 rep             stosw
99
100                 mov             di,TSS2
101                 mov             cx,104/2
102                 rep             stosw
103
104 ; ===== BUILD THE GLOBAL DESCRIPTOR TABLE AND GDTR REGISTER
105                 mov             ax,cs
106                 mov             word [MY_SEGMENT],ax
107                 mov             word [MY_SEGMENT+2],0
108                 mov             bx,ax
109                 shr             bx,12
110                 shl             ax,4                    ; BX:AX = 32-bit physical addr of our segment
111                 mov             word [MY_PHYS_BASE],ax
112                 mov             word [MY_PHYS_BASE+2],bx
113
114                 add             ax,GDT
115                 adc             bx,0                    ; BX:AX += offset of GDT
116
117                 mov             word [GDTR],MAX_SEL - 1
118                 mov             word [GDTR+2],ax
119                 mov             word [GDTR+4],bx        ; GDTR: limit MAX_SEL-1 base=physical mem addr of GDT
120
121                 mov             ax,word [MY_PHYS_BASE]
122                 mov             bx,word [MY_PHYS_BASE+2]
123                 add             ax,IDT
124                 adc             bx,0
125
126                 mov             word [IDTR],2047
127                 mov             word [IDTR+2],ax
128                 mov             word [IDTR+4],bx
129
130                 cld
131
132 ;     zero IDT
133                 mov             di,IDT
134                 mov             cx,1023
135                 xor             ax,ax
136                 rep             stosw
137
138                 mov             di,GDT
139 ;     NULL selector
140                 xor             ax,ax
141                 stosw
142                 stosw
143                 stosw
144                 stosw
145 ;     Code selector
146                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
147                 stosw                                   ; LIMIT
148                 mov             ax,[MY_PHYS_BASE]
149                 stosw                                   ; BASE[15:0]
150                 mov             al,[MY_PHYS_BASE+2]
151                 mov             ah,0x9A
152                 stosw                                   ; BASE[23:16] access byte=executable readable
153                 mov             al,0x0F
154                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
155                 stosw
156 ;     Data selector
157                 xor             ax,ax
158                 dec             ax                      ; 0xFFFF
159                 stosw                                   ; LIMIT
160                 mov             ax,[MY_PHYS_BASE]
161                 stosw                                   ; BASE[15:0]
162                 mov             al,[MY_PHYS_BASE+2]
163                 mov             ah,0x92
164                 stosw                                   ; BASE[23:16] access byte=data writeable
165                 mov             al,0x0F
166                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
167                 stosw
168 ;     Data selector (video)
169                 xor             ax,ax
170                 dec             ax                      ; 0xFFFF
171                 stosw                                   ; LIMIT
172                 mov             ax,0x8000
173                 stosw                                   ; BASE[15:0]
174                 mov             al,0x0B                 ; BASE=0xB8000
175                 mov             ah,0x92
176                 stosw                                   ; BASE[23:16] access byte=data writeable
177                 mov             al,0x0F
178                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
179                 stosw
180 ;     Code selector (32-bit)
181                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
182                 stosw                                   ; LIMIT
183                 mov             ax,[MY_PHYS_BASE]
184                 stosw                                   ; BASE[15:0]
185                 mov             al,[MY_PHYS_BASE+2]
186                 mov             ah,0x9A
187                 stosw                                   ; BASE[23:16] access byte=executable readable
188                 mov             al,0xCF
189                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
190                 stosw
191 ;     Data selector (32-bit)
192                 xor             ax,ax
193                 dec             ax                      ; 0xFFFF
194                 stosw                                   ; LIMIT
195                 mov             ax,[MY_PHYS_BASE]
196                 stosw                                   ; BASE[15:0]
197                 mov             al,[MY_PHYS_BASE+2]
198                 mov             ah,0x92
199                 stosw                                   ; BASE[23:16] access byte=data writeable
200                 mov             al,0xCF
201                 mov             ah,[MY_PHYS_BASE+3]     ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
202                 stosw
203 ;     VCPI0
204                 add             di,8
205 ;     VCPI1
206                 add             di,8
207 ;     VCPI2
208                 add             di,8
209 ;     TSS selector (TSS_SEL)
210                 mov             ebx,[MY_PHYS_BASE]
211                 add             ebx,TSS1
212                 mov             ax,104-1
213                 stosw                                   ; LIMIT
214                 mov             ax,bx
215                 shr             ebx,16
216                 stosw                                   ; BASE[15:0]
217                 mov             al,bl
218                 mov             ah,0x89
219                 stosw                                   ; BASE[23:16] access byte=data writeable non-busy TSS type 9
220                 mov             al,0x0F
221                 mov             ah,bh                   ; LIMIT[19:16] flags=0 BASE[31:24]
222                 stosw
223 ;     TSS selector (TSS_VM86_SEL)
224                 mov             ebx,[MY_PHYS_BASE]
225                 add             ebx,TSS2
226                 mov             ax,104-1
227                 stosw                                   ; LIMIT
228                 mov             ax,bx
229                 shr             ebx,16
230                 stosw                                   ; BASE[15:0]
231                 mov             al,bl
232                 mov             ah,0x89
233                 stosw                                   ; BASE[23:16] access byte=data writeable non-busy TSS type 9
234                 mov             al,0x0F
235                 mov             ah,bh                   ; LIMIT[19:16] flags=0 BASE[31:24]
236                 stosw
237
238 ; prepare page directory
239                 cli
240                 cld
241                 push            ds
242                 pop             es
243                 mov             edi,[PAGEDIR]
244                 mov             edx,[MY_PHYS_BASE]
245
246                 mov             ebx,[PAGE0]
247                 lea             eax,[edx+ebx+7]
248                 stosd
249
250                 xor             eax,eax
251                 mov             ecx,1023
252                 rep             stosd
253
254 ; prepare to switch
255                 cli
256                 cld
257                 push            ds
258                 pop             es
259                 mov             di,VCPI_SETUP
260                 mov             edx,[MY_PHYS_BASE]
261
262                 mov             ebx,[PAGEDIR]
263                 lea             eax,[edx+ebx]
264                 mov             dword [di+0],eax        ; ESI+0 = CR3 register = (SEG<<4)+PAGE0
265
266                 lea             eax,[edx+GDTR]
267                 mov             dword [di+4],eax        ; ESI+4 = GDTR
268
269                 lea             eax,[edx+IDTR]
270                 mov             dword [di+8],eax        ; ESI+8 = IDTR
271
272                 mov             word [di+0xC],0         ; ESI+C = LDTR
273                 mov             word [di+0xE],TSS2_SEL  ; ESI+E = TR
274                 mov             dword [di+0x10],prot16_entry ; ESI+12 = prot16_entry
275                 mov             dword [di+0x14],CODE_SEL        ; ESI+10 = CS
276
277 ; =============== JUMP INTO PROTECTED MODE USING VCPI
278                 mov             esi,VCPI_SETUP
279                 add             esi,[MY_PHYS_BASE]      ; ESI = *LINEAR* address (not DS:ESI!)
280                 mov             ax,0xDE0C
281                 int             67h
282 prot16_entry:
283
284                 mov             ax,DATA_SEL
285                 mov             ds,ax
286                 mov             es,ax
287                 mov             ss,ax
288
289 ; draw directly onto VGA alphanumeric RAM at 0xB8000
290                 cld
291                 push            es
292                 mov             ax,VIDEO_SEL
293                 mov             es,ax
294                 mov             si,vdraw_msg
295                 xor             di,di
296 vdraw1:         lodsb                                   ; AL = DS:SI++
297                 or              al,al
298                 jz              vdraw1e
299                 mov             ah,0x1E
300                 stosw                                   ; ES:DI = AX, DI += 2
301                 jmp             vdraw1
302 vdraw1e:        pop             es
303
304 ; now, jump into 32-bit protected mode
305                 jmp             CODE32_SEL:prot32_entry
306 bits 32
307 prot32_entry:   mov             ax,DATA32_SEL
308                 mov             ds,ax
309                 mov             es,ax
310                 mov             ss,ax
311                 mov             esp,0xFFF0
312
313 ; draw directly onto VGA alphanumeric RAM at 0xB8000
314                 cld
315                 mov             esi,vdraw32_msg
316                 mov             edi,0xB8000+(80*2)
317                 sub             edi,[MY_PHYS_BASE]
318 vdraw321:       lodsb                                   ; AL = DS:SI++
319                 or              al,al
320                 jz              vdraw321e
321                 mov             ah,0x1E
322                 stosw                                   ; ES:DI = AX, DI += 2
323                 jmp             vdraw321
324 vdraw321e:
325
326 ; jump 32-bit to 16-bit
327                 jmp             CODE_SEL:prot32_to_prot
328 bits 16
329 prot32_to_prot: mov             ax,DATA_SEL
330                 mov             ds,ax
331                 mov             es,ax
332                 mov             ss,ax
333
334 ; =============== JUMP OUT OF PROTECTED MODE USING VCPI
335                 mov             ebx,esp
336                 xor             eax,eax
337                 mov             ax,[MY_SEGMENT]
338                 push            eax                     ; SS:ESP+0x28 GS
339                 push            eax                     ; SS:ESP+0x24 FS
340                 push            eax                     ; SS:ESP+0x20 DS
341                 push            eax                     ; SS:ESP+0x1C ES
342                 push            eax                     ; SS:ESP+0x18 SS
343                 push            ebx                     ; SS:ESP+0x14 ESP
344                 pushfd                                  ; SS:ESP+0x10 EFLAGS
345                 push            eax                     ; SS:ESP+0x0C CS
346                 push            dword realmode_entry    ; SS:ESP+0x08 EIP
347                 push            dword VCPI0_SEL         ; SS:ESP+0x04
348                 push            dword [vcpi_entry]      ; SS:ESP+0x00
349                 mov             eax,0xDE0C              ; 0xDE0C = switch to v86 mode
350                 jmp far         dword [esp]             ; simulate a CALL FAR (SS: override implied)
351
352 ; ====== PROVE WE MADE IT TO REAL MODE
353 realmode_entry: mov             si,vdraw2_msg
354                 mov             ax,0xB800
355                 mov             es,ax
356                 mov             di,80*4
357 vdraw2:         lodsb                                   ; AL = DS:SI++
358                 or              al,al
359                 jz              vdraw2e
360                 mov             ah,0x1E
361                 stosw                                   ; ES:DI = AX, DI += 2
362                 jmp             vdraw2
363 vdraw2e:        mov             ax,cs
364                 mov             es,ax
365
366                 sti
367
368 ; ===== DONE
369                 jmp             exit2dos
370
371 ; ===== EXIT TO DOS WITH ERROR MESSAGE DS:DX
372 exit2dos_with_message:
373                 mov             ah,9
374                 int             21h
375 ; ===== EXIT TO DOS
376 exit2dos:       mov             ax,4C00h
377                 int             21h
378
379 ; 8086 test: EFLAGS will always have bits 12-15 set
380 cpu_is_386:     pushf
381                 pop             ax
382                 and             ax,0x0FFF
383                 push            ax
384                 popf
385                 pushf
386                 pop             ax
387                 and             ax,0xF000
388                 cmp             ax,0xF000
389                 jz              cpu_is_386_not
390 ; 286 test: EFLAGS will always have bits 12-15 clear
391                 or              ax,0xF000
392                 push            ax
393                 popf
394                 pushf
395                 pop             ax
396                 and             ax,0xF000
397                 jz              cpu_is_386_not
398 ; it's a 386
399                 xor             ax,ax                   ; ZF=1
400                 ret
401 cpu_is_386_not: mov             ax,1
402                 or              ax,ax                   ; ZF=0
403                 ret
404
405 ; strings
406 str_cpu_not_386: db             "386 or higher required$"
407 str_cpu_need_v86_mode: db       "Virtual 8086 mode required (VCPI)$"
408 str_cpu_need_int67:db           "INT 67h service required$"
409 str_cpu_need_vcpi:db            "VCPI server required$"
410 str_cpu_need_vcpi_info:db       "VCPI server failed to return info$"
411 vdraw2_msg:     db              "This message was drawn on screen back from real mode!",0
412 vdraw_msg:      db              "This message was drawn on screen from 386 16-bit protected mode!",0
413 vdraw32_msg:    db              "This message was drawn on screen from 386 32-bit protected mode!",0
414
415 ; vars
416                 section         .bss align=8
417 vcpi_entry:     resd            1
418 GDTR:           resq            1
419 IDTR:           resq            1
420 MY_PHYS_BASE:   resd            1
421 MY_SEGMENT:     resd            1
422 GDT:            resb            MAX_SEL
423 IDT:            resb            8*256
424 VCPI_SETUP:     resb            0x80
425 TSS1:           resb            108
426 TSS2:           resb            108
427 PAGEDIR:        resd            1
428 PAGE0:          resd            1
429 ENDOI:          resd            1
430