]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/cpu/v86kern2.asm
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / cpu / v86kern2.asm
1 ; v86kern2.asm
2 ;
3 ; Test program: Proof-of-concept minimalist virtual 8086 "monitor"
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 ;
11 ; MODE: 16-bit real mode MS-DOS .COM executable
12 ;       Assumes DS == ES == SS
13
14 ; NOTES:
15 ;   - This works... for the most part.
16 ;   - Somehow this works even with DOSBox's funky ROM-based interrupt emulation
17 ;   - The emulation provided is sufficient for real-mode exceptions including INT 3h debug and
18 ;     INT 1h trace. It also handles the correct exception to permit DOS programs to use the
19 ;     FPU if present.
20 ;   - This program also demonstrates a minimal I/O port trapping implementation. In this example,
21 ;     port 0x64 (keyboard controller command port) and port 0x92 (PS/2 & AT Port A) are intercepted
22 ;     to ensure that whatever the program does, the A20 gate is always enabled. This resolves the
23 ;     random crashes observed in Windows 95 DOS mode where HIMEM.SYS and the DOS kernel were
24 ;     apparently playing around with the A20 line.
25 ;
26 ; FIXME:
27 ;   - Privileged instructions like mov cr0,<reg> trigger an exception and this program makes no
28 ;     attempt to emulate those instructions.
29 ;     
30 ;   - Whatever the BIOS does in response to CTRL+ALT+DEL it doesn't work well when we are active.
31 ;
32 ;   - Incomplete VCPI implementation and EMM emulation with no pages to alloc
33 ;
34 ;   - Paging is not enabled
35 ;
36 ; OTHER NOTES:
37 ;   - This code makes no attempt to emulate the LDT manipulation that most BIOS implementations
38 ;     apparently like to do when handling INT 15H extended memory copy. Programs that use extended
39 ;     memory via HIMEM.SYS or via INT 15H will crash. [FIXED: VM86 monitor intercepts INT 15H
40 ;     calls. If the function call is for extended memory copy, the VM86 monitor will carry out
41 ;     the copy itself and return].
42
43 ; Standard selectors in protected mode
44 NULL_SEL        equ             (0 << 3)
45 CODE16_SEL      equ             (1 << 3)
46 DATA16_SEL      equ             (2 << 3)
47 CODE32_SEL      equ             (3 << 3)
48 DATA32_SEL      equ             (4 << 3)
49 FLAT16_SEL      equ             (5 << 3)
50 FLAT32_SEL      equ             (6 << 3)
51 LDT_SEL         equ             (7 << 3)
52 TSS_SEL         equ             (8 << 3)
53 TSS_VM86_SEL    equ             (9 << 3)
54 MAX_SEL         equ             (10 << 3)
55
56 ; We reprogram the PIC to direct IRQ 0-15 to this base interrupt
57 RM_INT_API      equ             0xEF
58 IRQ_BASE_INT    equ             0xF0
59
60 ; Extensible virtual 8086 mode kernel for DOS
61 ; (C) 2011 Jonathan Campbell
62
63                 bits            16
64                 section         .code
65                 [map            v86kern2.map]
66                 org             0x100
67
68 ; ============= ENTRY POINT
69                 mov             ax,cs
70                 mov             word [my_realmode_seg],ax
71                 mov             bp,stack_base
72                 mov             sp,stack_init                   ; SP is normally at 0xFFF0 so move it back down
73                 mov             word [himem_sys_buffer_handle],0
74                 mov             word [himem_sys_buffer_phys+2],0
75                 mov             word [himem_sys_buffer_phys],0
76                 mov             word [himem_sys_entry+2],0
77                 mov             word [himem_sys_entry],0
78                 mov             byte [user_req_unload],0
79                 mov             byte [user_req_iopl],3
80                 mov             byte [irq_pending],0
81                 mov             byte [i_am_tsr],0
82                 mov             byte [v86_if],0
83                 jmp             _entry
84
85 ; ============= CPU DETECT
86 cpu_is_386:     pushf
87                 pop             ax
88                 and             ax,0x0FFF
89                 push            ax
90                 popf
91                 pushf
92                 pop             ax
93                 and             ax,0xF000
94                 cmp             ax,0xF000
95                 jz              cpu_is_386_not
96 ; 286 test: EFLAGS will always have bits 12-15 clear
97                 or              ax,0xF000
98                 push            ax
99                 popf
100                 pushf
101                 pop             ax
102                 and             ax,0xF000
103                 jz              cpu_is_386_not
104 ; it's a 386
105                 xor             ax,ax                   ; ZF=1
106                 ret
107 cpu_is_386_not: mov             ax,1
108                 or              ax,ax                   ; ZF=0
109                 ret
110
111 ; ============= EXIT WITH MESSAGE ($-terminated string at DS:DX)
112 _exit_with_msg: mov             ah,9
113                 int             21h                     ; fall through to _exit
114 ; ============= EXIT
115 _exit:          mov             ax,cs
116                 mov             ds,ax
117                 cmp             word [himem_sys_buffer_handle],0 ; if there is a handle to free, then do it
118                 jz              .no_handle
119                 mov             ah,0Dh                  ; HIMEM.SYS function 0Dh unlock memory block
120                 mov             dx,word [himem_sys_buffer_handle]
121                 call far        word [himem_sys_entry]
122                 mov             ah,0Ah                  ; HIMEM.SYS function 0Ah free memory block
123                 mov             dx,word [himem_sys_buffer_handle]
124                 call far        word [himem_sys_entry]
125 .no_handle:     mov             ax,4C00h
126                 int             21h
127
128 ; ============= PROGRAM STARTS HERE
129 _entry:         call            parse_argv
130                 jz              .argv_ok
131                 mov             dx,str_help
132                 call            _exit_with_msg
133 .argv_ok:       call            cpu_is_386              ; CHECK: 386 or higher
134                 jz              .is386
135                 mov             dx,str_require_386
136                 jmp             _exit_with_msg
137 .is386:         cmp             byte [user_req_unload],0; CHECK: Did user request that we unload?
138                 jz              .not_unload
139                 jmp             unload_this_program
140 .not_unload:    smsw            ax                      ; CHECK: Virtual 8086 mode not already enabled
141                 test            al,1
142                 jz              .not_v86
143                 mov             dx,str_v86_detected
144                 jmp             _exit_with_msg
145 .not_v86:       cmp             dword [himem_sys_buffer_size],64*1024   ; CHECK: buffer size is 64KB or larger
146                 jge             .buffer_size_large_enough
147                 mov             dx,str_buffer_too_small
148                 jmp             _exit_with_msg
149 .buffer_size_large_enough:
150                 cmp             dword [himem_sys_buffer_size],16*1024*1024
151                 jle             .buffer_size_small_enough
152                 mov             dx,str_buffer_too_large
153                 jmp             _exit_with_msg
154 .buffer_size_small_enough:
155                 mov             ax,4300h                ; CHECK: HIMEM.SYS is present
156                 int             2Fh
157                 cmp             al,80h
158                 jz              .yes_himem_sys
159                 jmp             .skip_himem
160 .yes_himem_sys: mov             ax,4310h                ; Get HIMEM.SYS entry point (cannot fail)
161                 int             2Fh
162                 mov             word [himem_sys_entry],bx
163                 mov             word [himem_sys_entry+2],es
164                 mov             ah,5h                   ; HIMEM.SYS Local Enable A20
165                 call far        word [himem_sys_entry]
166                 cmp             ax,1
167                 jz              .yes_himem_a20
168                 mov             dx,str_himem_a20_error
169                 jmp             _exit_with_msg
170 .yes_himem_a20: mov             ah,09h                  ; HIMEM.SYS allocate block
171                 cli                                     ; <- in case BIOS interrupts do not save upper 16 bits
172                 mov             edx,[himem_sys_buffer_size]
173                 add             edx,1023
174                 shr             edx,10                  ; EDX = (X BYTES+1023)/1024 KB
175                 call far        word [himem_sys_entry]
176                 cmp             ax,1
177                 jz              .yes_himem_buf
178                 mov             dx,str_himem_alloc_err
179                 jmp             _exit_with_msg
180 .yes_himem_buf: mov             word [himem_sys_buffer_handle],dx ; store memory handle
181                 mov             ah,0Ch                  ; HIMEM.SYS lock memory block
182                 call far        word [himem_sys_entry]  ; NOTE: DX = memory handle (still)
183                 cmp             ax,1
184                 jz              .yes_himem_lock
185                 mov             dx,str_himem_lock_err
186                 jmp             _exit_with_msg
187 .yes_himem_lock:mov             word [himem_sys_buffer_phys],bx ; store DX:BX physical memory address
188                 mov             word [himem_sys_buffer_phys+2],dx
189 .skip_himem:
190
191 ; tss physical addrs
192                 xor             eax,eax
193                 mov             ax,cs
194                 shl             eax,4
195                 add             eax,tss_main
196                 mov             [tss_phys_base],eax ; = 104 bytes
197
198                 xor             eax,eax
199                 mov             ax,cs
200                 shl             eax,4
201                 add             eax,tss_vm86
202                 mov             [tss_vm86_phys_base],eax ; = 8192+104 bytes
203
204 ; PRINT "BUFFER AT: " + *((DWORD*)himem_sys_buffer_phys) + "\n"
205                 mov             dx,str_buffer_at
206                 call            dos_puts
207                 cli
208                 mov             eax,[himem_sys_buffer_phys]
209                 mov             di,scratch_str
210                 call            eax_to_hex_16_dos
211                 mov             dx,di
212                 call            dos_puts
213
214                 cli
215                 mov             eax,[himem_sys_buffer_phys]
216                 add             eax,[himem_sys_buffer_size]
217                 dec             eax
218                 mov             byte [scratch_str],'-'
219                 mov             di,scratch_str+1
220                 call            eax_to_hex_16_dos
221                 mov             dx,scratch_str
222                 call            dos_puts
223
224                 mov             dx,str_crlf
225                 call            dos_puts
226
227                 xor             eax,eax
228                 mov             ax,cs
229                 mov             es,ax
230                 shl             eax,4
231                 mov             dword [my_phys_base],eax
232
233 ; we use the extended memory buffer for VCPI emulation (page allocation)
234                 mov             eax,dword [himem_sys_buffer_phys]
235                 mov             dword [vcpi_alloc_bitmap_phys],eax
236                 mov             dword [vcpi_alloc_bitmap_size],eax
237                 mov             dword [vcpi_alloc_pages_phys],eax
238
239                 cmp             eax,0
240                 jz              .nothing
241
242                 add             eax,4096                ; assume bitmap size of 4K, enough for 4MB of pages
243                 mov             dword [vcpi_alloc_pages_phys],eax
244
245                 mov             ebx,dword [himem_sys_buffer_size]
246                 add             ebx,0xFFF
247                 shr             ebx,12+3                ; EBX = size of bitmap (up to 4K)
248                 mov             dword [vcpi_alloc_bitmap_size],ebx
249 .nothing:
250
251 ; clear the IDT and GDT
252                 cld
253                 xor             ax,ax
254
255                 mov             cx,MAX_SEL / 2
256                 mov             di,gdt
257                 rep             stosw
258
259 ; prepare the IDTR and GDTR.
260 ; real mode versions: limit=0xFFFF base=0
261                 xor             eax,eax
262                 dec             ax                      ; AX = 0xFFFF
263                 mov             word [idtr_real],ax
264                 mov             word [gdtr_real],ax
265                 inc             ax
266                 mov             dword [idtr_real+2],eax
267                 mov             dword [gdtr_real+2],eax
268 ; protected mode GDTR limit=MAX_SEL-1 base=(code segment)+var
269                 mov             word [gdtr_pmode],MAX_SEL - 1
270                 mov             word [idtr_pmode],(256 << 3) - 1
271                 mov             eax,[my_phys_base]
272                 add             eax,gdt
273                 mov             dword [gdtr_pmode+2],eax
274                 mov             eax,[my_phys_base]
275                 add             eax,idt
276                 mov             dword [idtr_pmode+2],eax
277
278 ; build the GDT
279                 cld
280                 lea             di,[gdt+CODE16_SEL]
281 ; Code selector (CODE_16SEL)
282                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
283                 stosw                                   ; LIMIT
284                 mov             ax,[my_phys_base]
285                 stosw                                   ; BASE[15:0]
286                 mov             al,[my_phys_base+2]
287                 mov             ah,0x9A
288                 stosw                                   ; BASE[23:16] access byte=executable readable
289                 mov             al,0x0F
290                 mov             ah,[my_phys_base+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
291                 stosw
292 ; Data selector (DATA16_SEL)
293                 xor             ax,ax
294                 dec             ax                      ; 0xFFFF
295                 stosw                                   ; LIMIT
296                 mov             ax,[my_phys_base]
297                 stosw                                   ; BASE[15:0]
298                 mov             al,[my_phys_base+2]
299                 mov             ah,0x92
300                 stosw                                   ; BASE[23:16] access byte=data writeable
301                 mov             al,0x0F
302                 mov             ah,[my_phys_base+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
303                 stosw
304 ; Code selector (CODE_32SEL)
305                 dec             ax                      ; 0x0000 - 1 = 0xFFFF
306                 stosw                                   ; LIMIT
307                 mov             ax,[my_phys_base]
308                 stosw                                   ; BASE[15:0]
309                 mov             al,[my_phys_base+2]
310                 mov             ah,0x9A
311                 stosw                                   ; BASE[23:16] access byte=executable readable
312                 mov             al,0xCF
313                 mov             ah,[my_phys_base+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
314                 stosw
315 ; Data selector (DATA32_SEL)
316                 xor             ax,ax
317                 dec             ax                      ; 0xFFFF
318                 stosw                                   ; LIMIT
319                 mov             ax,[my_phys_base]
320                 stosw                                   ; BASE[15:0]
321                 mov             al,[my_phys_base+2]
322                 mov             ah,0x92
323                 stosw                                   ; BASE[23:16] access byte=data writeable
324                 mov             al,0xCF
325                 mov             ah,[my_phys_base+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
326                 stosw
327 ; Data selector (FLAT16_SEL)
328                 xor             ax,ax
329                 dec             ax                      ; 0xFFFF
330                 stosw                                   ; LIMIT
331                 xor             ax,ax
332                 stosw                                   ; BASE[15:0]
333                 mov             ah,0x92
334                 stosw                                   ; BASE[23:16] access byte=data writeable
335                 mov             al,0x8F
336                 xor             ah,ah
337                 stosw
338 ; Data selector (FLAT32_SEL)
339                 xor             ax,ax
340                 dec             ax                      ; 0xFFFF
341                 stosw                                   ; LIMIT
342                 xor             ax,ax
343                 stosw                                   ; BASE[15:0]
344                 mov             ah,0x92
345                 stosw                                   ; BASE[23:16] access byte=data writeable
346                 mov             al,0xCF
347                 xor             ah,ah
348                 stosw
349 ; LDT selector (LDT_SEL)
350                 mov             ax,7                    ; I have no use for the LDT
351                 stosw                                   ; LIMIT
352                 mov             ax,[my_phys_base]
353                 stosw                                   ; BASE[15:0]
354                 mov             al,[my_phys_base+2]
355                 mov             ah,0x82
356                 stosw                                   ; BASE[23:16] access byte=data writeable LDT type 2
357                 mov             al,0x0F
358                 mov             ah,[my_phys_base+3]     ; LIMIT[19:16] flags=0 BASE[31:24]
359                 stosw
360 ; TSS selector (TSS_SEL)
361                 mov             ax,104-1
362                 stosw                                   ; LIMIT
363                 mov             ax,[tss_phys_base]
364                 stosw                                   ; BASE[15:0]
365                 mov             al,[tss_phys_base+2]
366                 mov             ah,0x89
367                 stosw                                   ; BASE[23:16] access byte=data writeable non-busy TSS type 9
368                 mov             al,0x0F
369                 mov             ah,[tss_phys_base+3]    ; LIMIT[19:16] flags=0 BASE[31:24]
370                 stosw
371 ; TSS selector (TSS_VM86_SEL)
372                 mov             ax,104+8192-1
373                 stosw                                   ; LIMIT
374                 mov             ax,[tss_vm86_phys_base]
375                 stosw                                   ; BASE[15:0]
376                 mov             al,[tss_vm86_phys_base+2]
377                 mov             ah,0x89
378                 stosw                                   ; BASE[23:16] access byte=data writeable non-busy TSS type 9
379                 mov             al,0x0F
380                 mov             ah,[tss_vm86_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
381                 stosw
382
383 ; prepare the CPU registers
384                 lidt            [idtr_pmode]
385                 lgdt            [gdtr_pmode]
386
387 ; enter protected mode
388                 mov             eax,0x00000011
389                 or              eax,[cr0_more]
390                 mov             cr0,eax
391                 jmp             CODE16_SEL:pmode16_entry
392 pmode16_entry:  mov             ax,DATA16_SEL
393                 mov             ds,ax
394                 mov             es,ax
395                 mov             fs,ax
396                 mov             gs,ax
397                 mov             ss,ax
398                 mov             sp,stack_init
399
400 ; load task register
401                 mov             ax,TSS_SEL
402                 ltr             ax
403
404 ; load LDT
405                 mov             ax,LDT_SEL
406                 lldt            ax
407
408 ; now enter 32-bit protected mode
409                 jmp             CODE32_SEL:pmode32_entry
410                 bits            32
411 pmode32_entry:  mov             ax,DATA32_SEL
412                 mov             ds,ax
413                 mov             es,ax
414                 mov             ss,ax
415                 mov             ax,FLAT32_SEL
416                 mov             fs,ax
417                 mov             gs,ax
418                 mov             esp,stack_init
419 ; at this point: we are in 32-bit protected mode!
420
421 ; ============= setup the TSS representing our task (for when we return)
422                 cld
423                 mov             edi,[tss_phys_base]
424                 sub             edi,[my_phys_base]
425
426                 xor             eax,eax                                 ; TSS+0x00 = no backlink
427                 stosd
428                 mov             eax,stack_init                          ; TSS+0x04 = ESP for CPL0
429                 stosd
430                 mov             eax,DATA32_SEL                          ; TSS+0x08 = SS for CPL0
431                 stosd
432                 mov             eax,stack_init                          ; TSS+0x0C = ESP for CPL1
433                 stosd
434                 mov             eax,DATA32_SEL                          ; TSS+0x10 = SS for CPL1
435                 stosd
436                 mov             eax,stack_init                          ; TSS+0x14 = ESP for CPL2
437                 stosd
438                 mov             eax,DATA32_SEL                          ; TSS+0x18 = SS for CPL2
439                 stosd
440                 xor             eax,eax                                 ; TSS+0x1C = CR3
441                 stosd
442                 mov             eax,0                                   ; TSS+0x20 = EIP [FIXME]
443                 stosd
444                 mov             eax,0x00000002                          ; TSS+0x24 = EFLAGS VM=0
445                 stosd
446                 xor             eax,eax                                 ; TSS+0x28 = EAX
447                 stosd
448                 xor             eax,eax                                 ; TSS+0x2C = ECX
449                 stosd
450                 xor             eax,eax                                 ; TSS+0x30 = EDX
451                 stosd
452                 xor             eax,eax                                 ; TSS+0x34 = EBX
453                 stosd
454                 mov             eax,stack_init                          ; TSS+0x38 = ESP
455                 stosd
456                 xor             eax,eax                                 ; TSS+0x3C = EBP
457                 stosd
458                 xor             eax,eax                                 ; TSS+0x40 = ESI
459                 stosd
460                 xor             eax,eax                                 ; TSS+0x44 = EDI
461                 stosd
462                 mov             ax,DATA32_SEL                           ; TSS+0x48 = ES
463                 stosd
464                 mov             ax,CODE32_SEL                           ; TSS+0x4C = CS
465                 stosd
466                 mov             ax,DATA32_SEL                           ; TSS+0x50 = SS
467                 stosd
468                 mov             ax,DATA32_SEL                           ; TSS+0x54 = DS
469                 stosd
470                 mov             ax,DATA32_SEL                           ; TSS+0x58 = FS
471                 stosd
472                 mov             ax,DATA32_SEL                           ; TSS+0x5C = GS
473                 stosd
474                 xor             eax,eax                                 ; TSS+0x60 = LDTR
475                 stosd
476                 mov             eax,(104 << 16)                         ; TSS+0x64 = I/O map base
477                 stosd
478
479 ; ============= setup the TSS representing the virtual 8086 mode task
480                 cld
481                 mov             edi,[tss_vm86_phys_base]
482                 sub             edi,[my_phys_base]
483
484                 xor             eax,eax                                 ; TSS+0x00 = no backlink
485                 stosd
486                 mov             eax,stack_init                          ; TSS+0x04 = ESP for CPL0
487                 stosd
488                 mov             eax,DATA32_SEL                          ; TSS+0x08 = SS for CPL0
489                 stosd
490                 mov             eax,stack_init                          ; TSS+0x0C = ESP for CPL1
491                 stosd
492                 mov             eax,DATA32_SEL                          ; TSS+0x10 = SS for CPL1
493                 stosd
494                 mov             eax,stack_init                          ; TSS+0x14 = ESP for CPL2
495                 stosd
496                 mov             eax,DATA32_SEL                          ; TSS+0x18 = SS for CPL2
497                 stosd
498                 xor             eax,eax                                 ; TSS+0x1C = CR3
499                 stosd
500                 mov             eax,vm86_entry                          ; TSS+0x20 = EIP
501                 stosd
502                 mov             eax,0x00020202                          ; TSS+0x24 = EFLAGS VM=1 IOPL=N IF=1
503                 movzx           ebx,byte [user_req_iopl]
504                 and             bl,3
505                 shl             ebx,12
506                 or              eax,ebx                                 ; EFLAGS |= user_req_iopl << 12
507                 stosd
508                 xor             eax,eax                                 ; TSS+0x28 = EAX
509                 stosd
510                 xor             eax,eax                                 ; TSS+0x2C = ECX
511                 stosd
512                 xor             eax,eax                                 ; TSS+0x30 = EDX
513                 stosd
514                 xor             eax,eax                                 ; TSS+0x34 = EBX
515                 stosd
516                 mov             eax,stack_init_vm86                     ; TSS+0x38 = ESP
517                 stosd
518                 xor             eax,eax                                 ; TSS+0x3C = EBP
519                 stosd
520                 xor             eax,eax                                 ; TSS+0x40 = ESI
521                 stosd
522                 xor             eax,eax                                 ; TSS+0x44 = EDI
523                 stosd
524                 mov             ax,[my_realmode_seg]                    ; TSS+0x48 = ES
525                 stosd
526                 mov             ax,[my_realmode_seg]                    ; TSS+0x4C = CS
527                 stosd
528                 mov             ax,[my_realmode_seg]                    ; TSS+0x50 = SS
529                 stosd
530                 mov             ax,[my_realmode_seg]                    ; TSS+0x54 = DS
531                 stosd
532                 mov             ax,[my_realmode_seg]                    ; TSS+0x58 = FS
533                 stosd
534                 mov             ax,[my_realmode_seg]                    ; TSS+0x5C = GS
535                 stosd
536                 xor             eax,eax                                 ; TSS+0x60 = LDTR
537                 stosd
538                 mov             eax,(104 << 16)                         ; TSS+0x64 = I/O map base (0x68 == 104)
539                 stosd
540                 xor             eax,eax
541                 mov             ecx,8192 >> 2                           ; TSS+0x68 = I/O permission map (pre-set to all open)
542                 rep             stosd
543
544 ; actually, we want it to trap some ports off the bat
545                 mov             edi,[tss_vm86_phys_base]
546                 sub             edi,[my_phys_base]
547                 add             edi,0x68                                ; TSS+0x68 = I/O permission map
548                 or              byte [edi+(0x64/8)],1 << (0x64 & 7)     ; trap port 0x64
549                 or              byte [edi+(0x92/8)],1 << (0x92 & 7)     ; trap port 0x92
550
551 ; zero VCPI page map
552                 mov             eax,dword [vcpi_alloc_bitmap_phys]
553                 cmp             eax,0
554                 jz              .nothing
555                 push            es
556                 mov             ax,FLAT32_SEL
557                 mov             es,ax
558                 xor             eax,eax
559                 cld
560                 mov             edi,dword [vcpi_alloc_bitmap_phys]
561                 mov             ecx,dword [vcpi_alloc_bitmap_size]
562                 shr             ecx,2
563                 rep             stosd
564                 pop             es
565 .nothing:
566
567 ; set up the IDT
568                 cld
569
570                 mov             edi,idt
571                 mov             esi,interrupt_procs
572
573 ; the first 32 interrupts (0x00-0x1F) are marked CPL=0. This causes
574 ; real-mode software interrupts in that range to instead cause a GPF
575 ; which we can then safely reflect back to real mode. If all were
576 ; marked CPL=3 then a real-mode software interrupt would trigger an
577 ; actual INT in the IDT and it would be extremely difficult for
578 ; fault handlers to differentiate between actual faults vs. software
579 ; interrupts.
580                 mov             ecx,0x14
581 .idtdef0:       lodsw
582                 stosw                                   ; base[15:0]
583                 mov             ax,CODE32_SEL
584                 stosw
585                 mov             ax,0x8E00               ; DPL=0 (so that software interrupts 0x00-0x1F can be handled by our GPF)
586                 stosw
587                 xor             ax,ax
588                 stosw
589                 loop            .idtdef0
590
591                 mov             ecx,0x100 - 0x14
592 .idtdef3:       lodsw
593                 stosw                                   ; base[15:0]
594                 mov             ax,CODE32_SEL
595                 stosw
596                 mov             ax,0xEE00               ; DPL=3
597                 stosw
598                 xor             ax,ax
599                 stosw
600                 loop            .idtdef3
601
602 ; next we need to reprogram the PIC so that IRQ 0-7 do not conflict with the CPU exceptions.
603                 mov             al,0x10                 ; ICW1 A0=0
604                 out             20h,al
605                 mov             al,IRQ_BASE_INT         ; ICW2 A0=1
606                 out             21h,al
607                 mov             al,0x04                 ; ICW3 A0=1 slave on IRQ 2
608                 out             21h,al
609
610                 mov             al,0x10                 ; ICW1 A0=0
611                 out             0xA0,al
612                 mov             al,IRQ_BASE_INT+8       ; ICW2 A0=1
613                 out             0xA1,al
614                 mov             al,0x02                 ; ICW3 A0=1
615                 out             0xA1,al
616
617 ; jump into virtual 8086 mode
618                 jmp             TSS_VM86_SEL:0
619
620 ; interrupt jump table. emits a 4-byte instruction (no error code)
621 ;    interrupt_entry_4 <vector>
622 %macro interrupt_entry_4 1
623 ; non-error code version. We push a dummy error code along with the intnum.
624 interrupt_%1:   push            byte 0
625                 push            %1
626                 jmp             interrupt_routine
627 %endmacro
628
629 ; interrupt jump table. emits a 4-byte instruction (no error code), meant for IRQs
630 ;    interrupt_entry_4 <vector>
631 %macro interrupt_entry_4irq 1
632 ; non-error code version. We push a dummy error code along with the intnum.
633 interrupt_%1:   push            byte 0
634                 push            %1
635                 jmp             interrupt_routine_irq
636 %endmacro
637
638 ; interrupt jump table. emits a 4-byte instruction (no error code), meant for IRQs
639 ;    interrupt_entry_4 <vector>
640 %macro interrupt_entry_4_tf 1
641 ; non-error code version. We push a dummy error code along with the intnum.
642 interrupt_%1:   push            byte 0
643                 push            %1
644                 jmp             interrupt_routine_tf
645 %endmacro
646
647 ; error code version. The CPU pushes an error code on the stack. We just push the intnum and move on,
648 %macro interrupt_entry_4_ec 1
649 interrupt_%1:   push            %1
650                 jmp             interrupt_routine
651 %endmacro
652
653 ; specialized for GPF fault. no time to jump to generic interrupt handlers.
654 %macro interrupt_entry_4gpf 1
655 interrupt_%1:   push            %1
656                 jmp             interrupt_routine_gpf
657 %endmacro
658
659 ; specialized for EMM/VCPI entry. no time to jump to generic interrupt handlers.
660 %macro interrupt_entry_4emm 1
661 interrupt_%1:   push            byte 0
662                 push            %1
663                 jmp             interrupt_routine_emm
664 %endmacro
665
666                 interrupt_entry_4    0x00
667                 interrupt_entry_4_tf 0x01
668                 interrupt_entry_4    0x02
669                 interrupt_entry_4    0x03
670                 interrupt_entry_4    0x04
671                 interrupt_entry_4    0x05
672                 interrupt_entry_4    0x06
673                 interrupt_entry_4    0x07
674                 interrupt_entry_4_ec 0x08
675                 interrupt_entry_4    0x09
676                 interrupt_entry_4_ec 0x0A
677                 interrupt_entry_4_ec 0x0B
678                 interrupt_entry_4_ec 0x0C
679                 interrupt_entry_4gpf 0x0D
680                 interrupt_entry_4    0x0E
681                 interrupt_entry_4    0x0F
682                 interrupt_entry_4    0x10
683                 interrupt_entry_4    0x11
684                 interrupt_entry_4    0x12
685                 interrupt_entry_4    0x13
686                 interrupt_entry_4    0x14               ; <== FIRST INTERRUPT NOT TO TRAP VIA GPF
687                 interrupt_entry_4    0x15
688                 interrupt_entry_4    0x16
689                 interrupt_entry_4    0x17
690                 interrupt_entry_4    0x18
691                 interrupt_entry_4    0x19
692                 interrupt_entry_4    0x1A
693                 interrupt_entry_4    0x1B
694                 interrupt_entry_4    0x1C
695                 interrupt_entry_4    0x1D
696                 interrupt_entry_4    0x1E
697                 interrupt_entry_4    0x1F
698                 interrupt_entry_4    0x20
699                 interrupt_entry_4    0x21
700                 interrupt_entry_4    0x22
701                 interrupt_entry_4    0x23
702                 interrupt_entry_4    0x24
703                 interrupt_entry_4    0x25
704                 interrupt_entry_4    0x26
705                 interrupt_entry_4    0x27
706                 interrupt_entry_4    0x28
707                 interrupt_entry_4    0x29
708                 interrupt_entry_4    0x2A
709                 interrupt_entry_4    0x2B
710                 interrupt_entry_4    0x2C
711                 interrupt_entry_4    0x2D
712                 interrupt_entry_4    0x2E
713                 interrupt_entry_4    0x2F
714                 interrupt_entry_4    0x30
715                 interrupt_entry_4    0x31
716                 interrupt_entry_4    0x32
717                 interrupt_entry_4    0x33
718                 interrupt_entry_4    0x34
719                 interrupt_entry_4    0x35
720                 interrupt_entry_4    0x36
721                 interrupt_entry_4    0x37
722                 interrupt_entry_4    0x38
723                 interrupt_entry_4    0x39
724                 interrupt_entry_4    0x3A
725                 interrupt_entry_4    0x3B
726                 interrupt_entry_4    0x3C
727                 interrupt_entry_4    0x3D
728                 interrupt_entry_4    0x3E
729                 interrupt_entry_4    0x3F
730                 interrupt_entry_4    0x40
731                 interrupt_entry_4    0x41
732                 interrupt_entry_4    0x42
733                 interrupt_entry_4    0x43
734                 interrupt_entry_4    0x44
735                 interrupt_entry_4    0x45
736                 interrupt_entry_4    0x46
737                 interrupt_entry_4    0x47
738                 interrupt_entry_4    0x48
739                 interrupt_entry_4    0x49
740                 interrupt_entry_4    0x4A
741                 interrupt_entry_4    0x4B
742                 interrupt_entry_4    0x4C
743                 interrupt_entry_4    0x4D
744                 interrupt_entry_4    0x4E
745                 interrupt_entry_4    0x4F
746                 interrupt_entry_4    0x50
747                 interrupt_entry_4    0x51
748                 interrupt_entry_4    0x52
749                 interrupt_entry_4    0x53
750                 interrupt_entry_4    0x54
751                 interrupt_entry_4    0x55
752                 interrupt_entry_4    0x56
753                 interrupt_entry_4    0x57
754                 interrupt_entry_4    0x58
755                 interrupt_entry_4    0x59
756                 interrupt_entry_4    0x5A
757                 interrupt_entry_4    0x5B
758                 interrupt_entry_4    0x5C
759                 interrupt_entry_4    0x5D
760                 interrupt_entry_4    0x5E
761                 interrupt_entry_4    0x5F
762                 interrupt_entry_4    0x60
763                 interrupt_entry_4    0x61
764                 interrupt_entry_4    0x62
765                 interrupt_entry_4    0x63
766                 interrupt_entry_4    0x64
767                 interrupt_entry_4    0x65
768                 interrupt_entry_4    0x66
769                 interrupt_entry_4emm 0x67
770                 interrupt_entry_4    0x68
771                 interrupt_entry_4    0x69
772                 interrupt_entry_4    0x6A
773                 interrupt_entry_4    0x6B
774                 interrupt_entry_4    0x6C
775                 interrupt_entry_4    0x6D
776                 interrupt_entry_4    0x6E
777                 interrupt_entry_4    0x6F
778                 interrupt_entry_4    0x70
779                 interrupt_entry_4    0x71
780                 interrupt_entry_4    0x72
781                 interrupt_entry_4    0x73
782                 interrupt_entry_4    0x74
783                 interrupt_entry_4    0x75
784                 interrupt_entry_4    0x76
785                 interrupt_entry_4    0x77
786                 interrupt_entry_4    0x78
787                 interrupt_entry_4    0x79
788                 interrupt_entry_4    0x7A
789                 interrupt_entry_4    0x7B
790                 interrupt_entry_4    0x7C
791                 interrupt_entry_4    0x7D
792                 interrupt_entry_4    0x7E
793                 interrupt_entry_4    0x7F
794                 interrupt_entry_4    0x80
795                 interrupt_entry_4    0x81
796                 interrupt_entry_4    0x82
797                 interrupt_entry_4    0x83
798                 interrupt_entry_4    0x84
799                 interrupt_entry_4    0x85
800                 interrupt_entry_4    0x86
801                 interrupt_entry_4    0x87
802                 interrupt_entry_4    0x88
803                 interrupt_entry_4    0x89
804                 interrupt_entry_4    0x8A
805                 interrupt_entry_4    0x8B
806                 interrupt_entry_4    0x8C
807                 interrupt_entry_4    0x8D
808                 interrupt_entry_4    0x8E
809                 interrupt_entry_4    0x8F
810                 interrupt_entry_4    0x90
811                 interrupt_entry_4    0x91
812                 interrupt_entry_4    0x92
813                 interrupt_entry_4    0x93
814                 interrupt_entry_4    0x94
815                 interrupt_entry_4    0x95
816                 interrupt_entry_4    0x96
817                 interrupt_entry_4    0x97
818                 interrupt_entry_4    0x98
819                 interrupt_entry_4    0x99
820                 interrupt_entry_4    0x9A
821                 interrupt_entry_4    0x9B
822                 interrupt_entry_4    0x9C
823                 interrupt_entry_4    0x9D
824                 interrupt_entry_4    0x9E
825                 interrupt_entry_4    0x9F
826                 interrupt_entry_4    0xA0
827                 interrupt_entry_4    0xA1
828                 interrupt_entry_4    0xA2
829                 interrupt_entry_4    0xA3
830                 interrupt_entry_4    0xA4
831                 interrupt_entry_4    0xA5
832                 interrupt_entry_4    0xA6
833                 interrupt_entry_4    0xA7
834                 interrupt_entry_4    0xA8
835                 interrupt_entry_4    0xA9
836                 interrupt_entry_4    0xAA
837                 interrupt_entry_4    0xAB
838                 interrupt_entry_4    0xAC
839                 interrupt_entry_4    0xAD
840                 interrupt_entry_4    0xAE
841                 interrupt_entry_4    0xAF
842                 interrupt_entry_4    0xB0
843                 interrupt_entry_4    0xB1
844                 interrupt_entry_4    0xB2
845                 interrupt_entry_4    0xB3
846                 interrupt_entry_4    0xB4
847                 interrupt_entry_4    0xB5
848                 interrupt_entry_4    0xB6
849                 interrupt_entry_4    0xB7
850                 interrupt_entry_4    0xB8
851                 interrupt_entry_4    0xB9
852                 interrupt_entry_4    0xBA
853                 interrupt_entry_4    0xBB
854                 interrupt_entry_4    0xBC
855                 interrupt_entry_4    0xBD
856                 interrupt_entry_4    0xBE
857                 interrupt_entry_4    0xBF
858                 interrupt_entry_4    0xC0
859                 interrupt_entry_4    0xC1
860                 interrupt_entry_4    0xC2
861                 interrupt_entry_4    0xC3
862                 interrupt_entry_4    0xC4
863                 interrupt_entry_4    0xC5
864                 interrupt_entry_4    0xC6
865                 interrupt_entry_4    0xC7
866                 interrupt_entry_4    0xC8
867                 interrupt_entry_4    0xC9
868                 interrupt_entry_4    0xCA
869                 interrupt_entry_4    0xCB
870                 interrupt_entry_4    0xCC
871                 interrupt_entry_4    0xCD
872                 interrupt_entry_4    0xCE
873                 interrupt_entry_4    0xCF
874                 interrupt_entry_4    0xD0
875                 interrupt_entry_4    0xD1
876                 interrupt_entry_4    0xD2
877                 interrupt_entry_4    0xD3
878                 interrupt_entry_4    0xD4
879                 interrupt_entry_4    0xD5
880                 interrupt_entry_4    0xD6
881                 interrupt_entry_4    0xD7
882                 interrupt_entry_4    0xD8
883                 interrupt_entry_4    0xD9
884                 interrupt_entry_4    0xDA
885                 interrupt_entry_4    0xDB
886                 interrupt_entry_4    0xDC
887                 interrupt_entry_4    0xDD
888                 interrupt_entry_4    0xDE
889                 interrupt_entry_4    0xDF
890                 interrupt_entry_4    0xE0
891                 interrupt_entry_4    0xE1
892                 interrupt_entry_4    0xE2
893                 interrupt_entry_4    0xE3
894                 interrupt_entry_4    0xE4
895                 interrupt_entry_4    0xE5
896                 interrupt_entry_4    0xE6
897                 interrupt_entry_4    0xE7
898                 interrupt_entry_4    0xE8
899                 interrupt_entry_4    0xE9
900                 interrupt_entry_4    0xEA
901                 interrupt_entry_4    0xEB
902                 interrupt_entry_4    0xEC
903                 interrupt_entry_4    0xED
904                 interrupt_entry_4    0xEE
905                 interrupt_entry_4    0xEF
906                 interrupt_entry_4irq 0xF0
907                 interrupt_entry_4irq 0xF1
908                 interrupt_entry_4irq 0xF2
909                 interrupt_entry_4irq 0xF3
910                 interrupt_entry_4irq 0xF4
911                 interrupt_entry_4irq 0xF5
912                 interrupt_entry_4irq 0xF6
913                 interrupt_entry_4irq 0xF7
914                 interrupt_entry_4irq 0xF8
915                 interrupt_entry_4irq 0xF9
916                 interrupt_entry_4irq 0xFA
917                 interrupt_entry_4irq 0xFB
918                 interrupt_entry_4irq 0xFC
919                 interrupt_entry_4irq 0xFD
920                 interrupt_entry_4irq 0xFE
921                 interrupt_entry_4irq 0xFF
922
923 interrupt_procs:dw              interrupt_0x00
924                 dw              interrupt_0x01
925                 dw              interrupt_0x02
926                 dw              interrupt_0x03
927                 dw              interrupt_0x04
928                 dw              interrupt_0x05
929                 dw              interrupt_0x06
930                 dw              interrupt_0x07
931                 dw              interrupt_0x08
932                 dw              interrupt_0x09
933                 dw              interrupt_0x0A
934                 dw              interrupt_0x0B
935                 dw              interrupt_0x0C
936                 dw              interrupt_0x0D
937                 dw              interrupt_0x0E
938                 dw              interrupt_0x0F
939                 dw              interrupt_0x10
940                 dw              interrupt_0x11
941                 dw              interrupt_0x12
942                 dw              interrupt_0x13
943                 dw              interrupt_0x14
944                 dw              interrupt_0x15
945                 dw              interrupt_0x16
946                 dw              interrupt_0x17
947                 dw              interrupt_0x18
948                 dw              interrupt_0x19
949                 dw              interrupt_0x1A
950                 dw              interrupt_0x1B
951                 dw              interrupt_0x1C
952                 dw              interrupt_0x1D
953                 dw              interrupt_0x1E
954                 dw              interrupt_0x1F
955
956                 dw              interrupt_0x20
957                 dw              interrupt_0x21
958                 dw              interrupt_0x22
959                 dw              interrupt_0x23
960                 dw              interrupt_0x24
961                 dw              interrupt_0x25
962                 dw              interrupt_0x26
963                 dw              interrupt_0x27
964                 dw              interrupt_0x28
965                 dw              interrupt_0x29
966                 dw              interrupt_0x2A
967                 dw              interrupt_0x2B
968                 dw              interrupt_0x2C
969                 dw              interrupt_0x2D
970                 dw              interrupt_0x2E
971                 dw              interrupt_0x2F
972                 dw              interrupt_0x30
973                 dw              interrupt_0x31
974                 dw              interrupt_0x32
975                 dw              interrupt_0x33
976                 dw              interrupt_0x34
977                 dw              interrupt_0x35
978                 dw              interrupt_0x36
979                 dw              interrupt_0x37
980                 dw              interrupt_0x38
981                 dw              interrupt_0x39
982                 dw              interrupt_0x3A
983                 dw              interrupt_0x3B
984                 dw              interrupt_0x3C
985                 dw              interrupt_0x3D
986                 dw              interrupt_0x3E
987                 dw              interrupt_0x3F
988
989                 dw              interrupt_0x40
990                 dw              interrupt_0x41
991                 dw              interrupt_0x42
992                 dw              interrupt_0x43
993                 dw              interrupt_0x44
994                 dw              interrupt_0x45
995                 dw              interrupt_0x46
996                 dw              interrupt_0x47
997                 dw              interrupt_0x48
998                 dw              interrupt_0x49
999                 dw              interrupt_0x4A
1000                 dw              interrupt_0x4B
1001                 dw              interrupt_0x4C
1002                 dw              interrupt_0x4D
1003                 dw              interrupt_0x4E
1004                 dw              interrupt_0x4F
1005                 dw              interrupt_0x50
1006                 dw              interrupt_0x51
1007                 dw              interrupt_0x52
1008                 dw              interrupt_0x53
1009                 dw              interrupt_0x54
1010                 dw              interrupt_0x55
1011                 dw              interrupt_0x56
1012                 dw              interrupt_0x57
1013                 dw              interrupt_0x58
1014                 dw              interrupt_0x59
1015                 dw              interrupt_0x5A
1016                 dw              interrupt_0x5B
1017                 dw              interrupt_0x5C
1018                 dw              interrupt_0x5D
1019                 dw              interrupt_0x5E
1020                 dw              interrupt_0x5F
1021
1022                 dw              interrupt_0x60
1023                 dw              interrupt_0x61
1024                 dw              interrupt_0x62
1025                 dw              interrupt_0x63
1026                 dw              interrupt_0x64
1027                 dw              interrupt_0x65
1028                 dw              interrupt_0x66
1029                 dw              interrupt_0x67
1030                 dw              interrupt_0x68
1031                 dw              interrupt_0x69
1032                 dw              interrupt_0x6A
1033                 dw              interrupt_0x6B
1034                 dw              interrupt_0x6C
1035                 dw              interrupt_0x6D
1036                 dw              interrupt_0x6E
1037                 dw              interrupt_0x6F
1038                 dw              interrupt_0x70
1039                 dw              interrupt_0x71
1040                 dw              interrupt_0x72
1041                 dw              interrupt_0x73
1042                 dw              interrupt_0x74
1043                 dw              interrupt_0x75
1044                 dw              interrupt_0x76
1045                 dw              interrupt_0x77
1046                 dw              interrupt_0x78
1047                 dw              interrupt_0x79
1048                 dw              interrupt_0x7A
1049                 dw              interrupt_0x7B
1050                 dw              interrupt_0x7C
1051                 dw              interrupt_0x7D
1052                 dw              interrupt_0x7E
1053                 dw              interrupt_0x7F
1054
1055                 dw              interrupt_0x80
1056                 dw              interrupt_0x81
1057                 dw              interrupt_0x82
1058                 dw              interrupt_0x83
1059                 dw              interrupt_0x84
1060                 dw              interrupt_0x85
1061                 dw              interrupt_0x86
1062                 dw              interrupt_0x87
1063                 dw              interrupt_0x88
1064                 dw              interrupt_0x89
1065                 dw              interrupt_0x8A
1066                 dw              interrupt_0x8B
1067                 dw              interrupt_0x8C
1068                 dw              interrupt_0x8D
1069                 dw              interrupt_0x8E
1070                 dw              interrupt_0x8F
1071                 dw              interrupt_0x90
1072                 dw              interrupt_0x91
1073                 dw              interrupt_0x92
1074                 dw              interrupt_0x93
1075                 dw              interrupt_0x94
1076                 dw              interrupt_0x95
1077                 dw              interrupt_0x96
1078                 dw              interrupt_0x97
1079                 dw              interrupt_0x98
1080                 dw              interrupt_0x99
1081                 dw              interrupt_0x9A
1082                 dw              interrupt_0x9B
1083                 dw              interrupt_0x9C
1084                 dw              interrupt_0x9D
1085                 dw              interrupt_0x9E
1086                 dw              interrupt_0x9F
1087
1088                 dw              interrupt_0xA0
1089                 dw              interrupt_0xA1
1090                 dw              interrupt_0xA2
1091                 dw              interrupt_0xA3
1092                 dw              interrupt_0xA4
1093                 dw              interrupt_0xA5
1094                 dw              interrupt_0xA6
1095                 dw              interrupt_0xA7
1096                 dw              interrupt_0xA8
1097                 dw              interrupt_0xA9
1098                 dw              interrupt_0xAA
1099                 dw              interrupt_0xAB
1100                 dw              interrupt_0xAC
1101                 dw              interrupt_0xAD
1102                 dw              interrupt_0xAE
1103                 dw              interrupt_0xAF
1104                 dw              interrupt_0xB0
1105                 dw              interrupt_0xB1
1106                 dw              interrupt_0xB2
1107                 dw              interrupt_0xB3
1108                 dw              interrupt_0xB4
1109                 dw              interrupt_0xB5
1110                 dw              interrupt_0xB6
1111                 dw              interrupt_0xB7
1112                 dw              interrupt_0xB8
1113                 dw              interrupt_0xB9
1114                 dw              interrupt_0xBA
1115                 dw              interrupt_0xBB
1116                 dw              interrupt_0xBC
1117                 dw              interrupt_0xBD
1118                 dw              interrupt_0xBE
1119                 dw              interrupt_0xBF
1120
1121                 dw              interrupt_0xC0
1122                 dw              interrupt_0xC1
1123                 dw              interrupt_0xC2
1124                 dw              interrupt_0xC3
1125                 dw              interrupt_0xC4
1126                 dw              interrupt_0xC5
1127                 dw              interrupt_0xC6
1128                 dw              interrupt_0xC7
1129                 dw              interrupt_0xC8
1130                 dw              interrupt_0xC9
1131                 dw              interrupt_0xCA
1132                 dw              interrupt_0xCB
1133                 dw              interrupt_0xCC
1134                 dw              interrupt_0xCD
1135                 dw              interrupt_0xCE
1136                 dw              interrupt_0xCF
1137                 dw              interrupt_0xD0
1138                 dw              interrupt_0xD1
1139                 dw              interrupt_0xD2
1140                 dw              interrupt_0xD3
1141                 dw              interrupt_0xD4
1142                 dw              interrupt_0xD5
1143                 dw              interrupt_0xD6
1144                 dw              interrupt_0xD7
1145                 dw              interrupt_0xD8
1146                 dw              interrupt_0xD9
1147                 dw              interrupt_0xDA
1148                 dw              interrupt_0xDB
1149                 dw              interrupt_0xDC
1150                 dw              interrupt_0xDD
1151                 dw              interrupt_0xDE
1152                 dw              interrupt_0xDF
1153
1154                 dw              interrupt_0xE0
1155                 dw              interrupt_0xE1
1156                 dw              interrupt_0xE2
1157                 dw              interrupt_0xE3
1158                 dw              interrupt_0xE4
1159                 dw              interrupt_0xE5
1160                 dw              interrupt_0xE6
1161                 dw              interrupt_0xE7
1162                 dw              interrupt_0xE8
1163                 dw              interrupt_0xE9
1164                 dw              interrupt_0xEA
1165                 dw              interrupt_0xEB
1166                 dw              interrupt_0xEC
1167                 dw              interrupt_0xED
1168                 dw              interrupt_0xEE
1169                 dw              interrupt_0xEF
1170                 dw              interrupt_0xF0
1171                 dw              interrupt_0xF1
1172                 dw              interrupt_0xF2
1173                 dw              interrupt_0xF3
1174                 dw              interrupt_0xF4
1175                 dw              interrupt_0xF5
1176                 dw              interrupt_0xF6
1177                 dw              interrupt_0xF7
1178                 dw              interrupt_0xF8
1179                 dw              interrupt_0xF9
1180                 dw              interrupt_0xFA
1181                 dw              interrupt_0xFB
1182                 dw              interrupt_0xFC
1183                 dw              interrupt_0xFD
1184                 dw              interrupt_0xFE
1185                 dw              interrupt_0xFF
1186
1187 interrupt_0x00_strs:
1188                 dw              str_fault_0x00
1189                 dw              str_fault_0x01
1190                 dw              str_fault_0x02
1191                 dw              str_fault_0x03
1192                 dw              str_fault_0x04
1193                 dw              str_fault_0x05
1194                 dw              str_fault_0x06
1195                 dw              str_fault_0x07
1196                 dw              str_fault_0x08
1197                 dw              str_fault_0x09
1198                 dw              str_fault_0x0A
1199                 dw              str_fault_0x0B
1200                 dw              str_fault_0x0C
1201                 dw              str_fault_0x0D
1202                 dw              str_fault_0x0E
1203                 dw              str_fault_0x0F
1204                 dw              str_fault_0x10
1205                 dw              str_fault_0x11
1206                 dw              str_fault_0x12
1207                 dw              str_fault_0x13
1208
1209 ; VCPI PM entry
1210 %macro pm_int_entry 0
1211                 pusha
1212                 push            ds
1213                 push            es
1214                 mov             ax,DATA32_SEL
1215                 mov             ds,ax
1216                 mov             es,ax
1217                 lea             eax,[esp+4+4+32+4+4+12] ; ESP + DS + ES + PUSHA + INTNUM + ERRCODE + IRET
1218                 ; TODO: MOVE EAX over ESP image on stack
1219                 lea             ebp,[esp]
1220 %endmacro
1221 %macro pm_int_exit 0
1222                 pop             es
1223                 pop             ds
1224                 popa
1225                 retf
1226 %endmacro
1227
1228 ; this must reside here. Any further down the jmps in the table will be out of range.
1229 ; Check:
1230 ;           (EFLAGS & VM) = Happend in v86 mode?
1231 %macro int_entry 0
1232                 pusha
1233                 push            ds
1234                 push            es
1235                 mov             ax,DATA32_SEL
1236                 mov             ds,ax
1237                 mov             es,ax
1238                 lea             eax,[esp+4+4+32+4+4+12] ; ESP + DS + ES + PUSHA + INTNUM + ERRCODE + IRET
1239                 ; TODO: MOVE EAX over ESP image on stack
1240                 lea             ebp,[esp]
1241 %endmacro
1242 %macro int_exit 0
1243                 pop             es
1244                 pop             ds
1245                 popa
1246                 add             esp,8           ; drop error code + interrupt num
1247                 iret
1248 %endmacro
1249 %define         int_ES          word [ebp+0]
1250 %define         int_DS          word [ebp+4]
1251 %define         int_EDI         dword [ebp+8]
1252 %define         int_ESI         dword [ebp+12]
1253 %define         int_EBP         dword [ebp+16]
1254 %define         int_ESP         dword [ebp+20]
1255 %define         int_EBX         dword [ebp+24]
1256 %define         int_EDX         dword [ebp+28]
1257 %define         int_ECX         dword [ebp+32]
1258 %define         int_EAX         dword [ebp+36]
1259 %define         int_AX          word [ebp+36]
1260 %define         int_AL          byte [ebp+36]
1261 %define         int_INTNUM      dword [ebp+40]
1262 %define         int_INTNUM_b    byte [ebp+40]
1263 %define         int_ERRCODE     dword [ebp+44]
1264 %define         int_EIP         dword [ebp+48]
1265 %define         int_EIP_w       word [ebp+48]
1266 %define         int_CS          word [ebp+52]
1267 %define         int_EFLAGS      dword [ebp+56]
1268 %define         int_FLAGS       word [ebp+56]
1269 ; the following also exist if coming out of v86 mode
1270 %define         int_v86_ESP     dword [ebp+60]
1271 %define         int_v86_SS      word [ebp+64]
1272 %define         int_v86_ES      word [ebp+68]
1273 %define         int_v86_DS      word [ebp+72]
1274 %define         int_v86_FS      word [ebp+76]
1275 %define         int_v86_GS      word [ebp+80]
1276
1277 irq_rm_map:     db              0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
1278                 db              0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77
1279
1280 ; return: EAX = physical memory address of vm86 SS:SP pointer
1281 vm86_ss_esp_to_phys:
1282                 xor             eax,eax
1283                 mov             ax,int_v86_SS
1284                 shl             eax,4
1285                 mov             ebx,int_v86_ESP
1286                 and             ebx,0xFFFF
1287                 add             eax,ebx
1288                 ret
1289
1290 ; return: EAX = physical memory address of vm86 CS:IP pointer
1291 vm86_cs_eip_to_phys:
1292                 xor             eax,eax
1293                 mov             ax,int_CS
1294                 shl             eax,4
1295                 mov             ebx,int_EIP
1296                 and             ebx,0xFFFF
1297                 add             eax,ebx
1298                 ret
1299
1300 ; EAX = interrupt to direct v86 mode to.
1301 ; the caller must be on a stack frame formed from v86 mode to protected mode transition, or this won't work
1302 vm86_push_interrupt:
1303                 push            es
1304                 mov             bx,FLAT32_SEL
1305                 mov             es,bx
1306                 mov             ecx,[es:eax*4]
1307                 sub             int_v86_ESP,6                   ; IRET stack frame
1308                 jc              .stack_overflow
1309                 call            vm86_ss_esp_to_phys
1310                 mov             ebx,int_EIP                     ; save IP,CS,FLAGS to stack
1311                 mov             word [es:eax+0],bx
1312                 mov             bx,int_CS
1313                 mov             word [es:eax+2],bx
1314                 mov             ebx,int_EFLAGS
1315                 mov             word [es:eax+4],bx
1316
1317                 mov             int_EIP_w,cx                    ; load new CS:IP from interrupt vector
1318                 shr             ecx,16
1319                 mov             int_CS,cx
1320
1321                 pop             es
1322                 ret
1323 .stack_overflow:
1324                 call            interrupt_routine_halt_prepare
1325                 mov             edx,int_INTNUM
1326                 mov             esi,str_stack_overflow
1327                 jmp             fault_jmp_unhandled
1328
1329 interrupt_routine_tf: ; INT 0x01 Trap Interrupt
1330                 int_entry
1331                 test            int_EFLAGS,0x20000              ; did this happen from v86 mode?
1332                 jz              .not_vm86
1333                 mov             eax,1                           ; reflect INT 0x01 to vm86
1334                 call            vm86_push_interrupt
1335                 and             int_EFLAGS,~0x100               ; clear the trap flag
1336                 int_exit
1337 .not_vm86:      call            interrupt_routine_halt_prepare
1338                 mov             edx,int_INTNUM
1339                 mov             esi,str_unexpected_int_not_v86
1340                 jmp             fault_jmp_unhandled
1341
1342 interrupt_routine_irq:
1343                 int_entry
1344                 test            int_EFLAGS,0x20000              ; did this happen from v86 mode?
1345                 jz              .not_vm86
1346                 mov             eax,int_INTNUM
1347                 sub             eax,IRQ_BASE_INT
1348                 movzx           eax,byte [eax+irq_rm_map]
1349                 call            vm86_push_interrupt
1350                 int_exit
1351 .not_vm86:      call            interrupt_routine_halt_prepare
1352                 mov             edx,int_INTNUM
1353                 mov             esi,str_unexpected_int_not_v86
1354                 jmp             fault_jmp_unhandled
1355
1356 vm86_stack_overflow:
1357                 call            interrupt_routine_halt_prepare
1358                 mov             edx,int_INTNUM
1359                 mov             esi,str_stack_overflow
1360                 jmp             fault_jmp_unhandled
1361
1362 ; General Protection Fault
1363 interrupt_routine_gpf:
1364                 int_entry
1365                 mov             ax,FLAT32_SEL
1366                 mov             es,ax
1367                 test            int_EFLAGS,0x20000              ; did this happen from v86 mode?
1368                 jz              .not_vm86
1369
1370                 call            vm86_cs_eip_to_phys             ; else, read the opcode at CS:IP to tell between exceptions and explicit INT xxh instructions
1371                 mov             eax,[es:eax]                    ; EAX = first 4 bytes of the opcode
1372
1373 ; INT 3?
1374                 cmp             al,0xCC
1375                 jz              .vm86_int3
1376 ; INT N?
1377                 cmp             al,0xCD
1378                 jz              .vm86_int_n
1379 ; IN AL,<imm>
1380                 cmp             al,0xE4
1381                 jz              .vm86_in_al_imm
1382 ; IN AX,<imm>
1383                 cmp             al,0xE5
1384                 jz              .vm86_in_ax_imm
1385 ; OUT <imm>,AL
1386                 cmp             al,0xE6
1387                 jz              .vm86_out_imm_al
1388 ; OUT <imm>,AX
1389                 cmp             al,0xE7
1390                 jz              .vm86_out_imm_ax
1391 ; IN AL,DX
1392                 cmp             al,0xEC
1393                 jz              .vm86_in_al_dx
1394 ; IN AX,DX
1395                 cmp             al,0xED
1396                 jz              .vm86_in_ax_dx
1397 ; OUT DX,AL
1398                 cmp             al,0xEE
1399                 jz              .vm86_out_dx_al
1400 ; OUT DX,AX
1401                 cmp             al,0xEF
1402                 jz              .vm86_out_dx_ax
1403 ; CLI?
1404                 cmp             al,0xFA
1405                 jz              .vm86_cli
1406 ; STI?
1407                 cmp             al,0xFB
1408                 jz              .vm86_sti
1409 ; PUSHF?
1410                 cmp             al,0x9C
1411                 jz              .vm86_pushf
1412 ; POPF?
1413                 cmp             al,0x9D
1414                 jz              .vm86_popf
1415 ; PUSHFD?
1416                 cmp             ax,0x9C66
1417                 jz              .vm86_pushfd
1418 ; POPFD?
1419                 cmp             ax,0x9D66
1420                 jz              .vm86_popfd
1421 ; IRET?
1422                 cmp             al,0xCF
1423                 jz              .vm86_iret
1424 ; well then I don't know
1425                 call            interrupt_routine_halt_prepare
1426                 mov             edx,0xD
1427                 mov             esi,str_v86_unknown
1428                 jmp             fault_jmp_unhandled
1429
1430 .vm86_in_al_imm:add             int_EIP,2                       ; EIP += 2
1431                 movzx           edx,ah                          ; I/O port number in second byte
1432                 mov             ecx,1                           ; ecx = size of I/O
1433                 call            on_vm86_io_read
1434                 mov             int_AL,al                       ; eax = byte value
1435                 int_exit
1436
1437 .vm86_in_ax_imm:add             int_EIP,2                       ; EIP += 2
1438                 movzx           edx,ah                          ; I/O port number in second byte
1439                 mov             ecx,2                           ; ecx = size of I/O
1440                 call            on_vm86_io_read
1441                 mov             int_AX,ax                       ; eax = byte value
1442                 int_exit
1443
1444 .vm86_in_al_dx: inc             int_EIP                         ; EIP += 1
1445                 mov             edx,int_EDX                     ; edx = port number
1446                 and             edx,0xFFFF
1447                 mov             ecx,1                           ; ecx = size of I/O
1448                 call            on_vm86_io_read
1449                 mov             int_AL,al                       ; eax = byte value
1450                 int_exit
1451
1452 .vm86_in_ax_dx: inc             int_EIP                         ; EIP += 1
1453                 mov             edx,int_EDX                     ; edx = port number
1454                 and             edx,0xFFFF
1455                 mov             ecx,2                           ; ecx = size of I/O
1456                 call            on_vm86_io_read
1457                 mov             int_AX,ax                       ; eax = byte value
1458                 int_exit
1459
1460 .vm86_out_imm_al:add            int_EIP,2                       ; EIP += 2
1461                 movzx           edx,ah                          ; I/O port number in second byte
1462                 movzx           eax,int_AL                      ; eax = byte value
1463                 mov             ecx,1                           ; ecx = size of I/O
1464                 call            on_vm86_io_write
1465                 int_exit
1466
1467 .vm86_out_imm_ax:add            int_EIP,2                       ; EIP += 2
1468                 movzx           edx,ah                          ; I/O port number in second byte
1469                 mov             eax,int_EAX
1470                 and             eax,0xFFFF
1471                 mov             ecx,2                           ; ecx = size of I/O
1472                 call            on_vm86_io_write
1473                 int_exit
1474
1475 .vm86_out_dx_al:inc             int_EIP                         ; EIP += 1
1476                 mov             edx,int_EDX                     ; edx = port number
1477                 and             edx,0xFFFF
1478                 movzx           eax,int_AL                      ; eax = byte value
1479                 mov             ecx,1                           ; ecx = size of I/O
1480                 call            on_vm86_io_write
1481                 int_exit
1482
1483 .vm86_out_dx_ax:inc             int_EIP                         ; EIP += 1
1484                 mov             edx,int_EDX                     ; edx = port number
1485                 and             edx,0xFFFF
1486                 mov             eax,int_EAX
1487                 and             eax,0xFFFF
1488                 mov             ecx,2                           ; ecx = size of I/O
1489                 call            on_vm86_io_write
1490                 int_exit
1491
1492 .vm86_pushfd:   push            es
1493                 mov             bx,FLAT32_SEL
1494                 mov             es,bx
1495                 sub             int_v86_ESP,4                   ; push DWORD
1496                 jc              vm86_stack_overflow
1497                 call            vm86_ss_esp_to_phys             ; EAX = SS:SP physical mem location
1498                 mov             ebx,int_EFLAGS                  ; retrieve FLAGS
1499                 mov             [es:eax],ebx                    ; store on stack
1500                 pop             es
1501                 add             int_EIP,2
1502                 int_exit
1503
1504 .vm86_popfd:    push            es
1505                 mov             bx,FLAT32_SEL
1506                 mov             es,bx
1507                 call            vm86_ss_esp_to_phys             ; EAX = SS:SP physical mem location
1508                 add             int_v86_ESP,4                   ; pop DWORD
1509                 jc              vm86_stack_overflow
1510                 mov             ebx,[es:eax]                    ; retrieve from stack
1511                 or              ebx,0x20000                     ; don't let the guest disable virtual 8086 mode
1512                 mov             int_EFLAGS,ebx                  ; place in FLAGS
1513                 pop             es
1514                 add             int_EIP,2
1515                 int_exit
1516
1517 .vm86_pushf:    push            es
1518                 mov             bx,FLAT32_SEL
1519                 mov             es,bx
1520                 sub             int_v86_ESP,2                   ; push WORD
1521                 jc              vm86_stack_overflow
1522                 call            vm86_ss_esp_to_phys             ; EAX = SS:SP physical mem location
1523                 mov             bx,int_FLAGS                    ; retrieve FLAGS
1524                 mov             [es:eax],bx                     ; store on stack
1525                 pop             es
1526                 add             int_EIP,1
1527                 int_exit
1528
1529 .vm86_popf:     push            es
1530                 mov             bx,FLAT32_SEL
1531                 mov             es,bx
1532                 call            vm86_ss_esp_to_phys             ; EAX = SS:SP physical mem location
1533                 add             int_v86_ESP,2                   ; pop WORD
1534                 jc              vm86_stack_overflow
1535                 mov             bx,[es:eax]                     ; retrieve from stack
1536                 mov             int_FLAGS,bx                    ; place in FLAGS
1537                 pop             es
1538                 add             int_EIP,1
1539                 int_exit
1540
1541 .vm86_iret:     push            es
1542                 mov             bx,FLAT32_SEL
1543                 mov             es,bx
1544                 call            vm86_ss_esp_to_phys             ; EAX = SS:SP physical mem location
1545                 add             int_v86_ESP,6                   ; pop interrupt stack frame
1546                 jc              vm86_stack_overflow
1547                 xor             ebx,ebx
1548                 mov             bx,[es:eax+0]                   ; EBX = IP
1549                 mov             int_EIP,ebx
1550                 mov             bx,[es:eax+2]                   ; EBX = CS
1551                 mov             int_CS,bx
1552                 mov             bx,[es:eax+4]                   ; EBX = FLAGS
1553                 mov             int_FLAGS,bx
1554                 pop             es
1555                 int_exit
1556
1557 .vm86_cli:      and             int_EFLAGS,~0x200               ; IF=0
1558                 inc             int_EIP
1559                 int_exit
1560
1561 .vm86_sti:      or              int_EFLAGS,0x200                ; IF=1
1562                 inc             int_EIP
1563                 int_exit
1564
1565 .vm86_int3:     inc             int_EIP                         ; EIP++
1566                 mov             eax,3
1567                 call            vm86_push_interrupt
1568                 int_exit
1569
1570 .vm86_int_n:    movzx           eax,ah                          ; AL=CD AH=intnum
1571                 add             int_EIP,2                       ; EIP += 2
1572                 call            vm86_push_interrupt
1573                 int_exit
1574
1575 .not_vm86:      call            interrupt_routine_halt_prepare
1576                 call            edx_esi_default_int_str
1577                 jmp             fault_jmp_unhandled
1578
1579 interrupt_routine:
1580                 int_entry
1581                 mov             ax,FLAT32_SEL
1582                 mov             es,ax
1583                 cmp             int_INTNUM,RM_INT_API           ; interrupts sent to our RM_INT_API are handled directly
1584                 jz              .rm_int_api
1585                 test            int_EFLAGS,0x20000              ; did this happen from v86 mode?
1586                 jz              .not_vm86
1587
1588 ; okay then, there are several reasons such interrupts fire.
1589                 mov             al,int_INTNUM_b
1590 ; INT 15H interception
1591                 cmp             al,0x15
1592                 jz              .vm86_int15
1593 ; Anything int >= 0x14 on
1594                 cmp             al,0x14
1595                 jae             .vm86_intN
1596 ; INT 0x03 debug interrupt FIXME is this needed?
1597                 cmp             al,0x03
1598                 jz              .vm86_int3
1599 ; INT 0x01 debug trap FIXME is this needed?
1600                 cmp             al,0x01
1601                 jz              .vm86_int1
1602 ; INT 0x07 COPROCESSOR NOT PRESENT (vm86 task used a floating point instruction)
1603                 cmp             al,0x07
1604                 jz              .fpu_7
1605
1606                 call            interrupt_routine_halt_prepare
1607                 call            edx_esi_default_int_str
1608                 jmp             fault_jmp_unhandled
1609
1610 ; COPROCESSOR NOT PRESENT. USUALLY SIGNALLED AFTER TASK SWITCH ON FIRST FPU INSTRUCTION.
1611 .fpu_7:         clts
1612                 int_exit
1613
1614 .vm86_int1:     mov             eax,1
1615                 call            vm86_push_interrupt
1616                 and             int_EFLAGS,~0x100               ; clear trap flag
1617                 int_exit
1618
1619 .vm86_int3:     mov             eax,3
1620                 call            vm86_push_interrupt
1621                 int_exit
1622
1623 .vm86_intN:     mov             eax,int_INTNUM
1624                 call            vm86_push_interrupt
1625                 int_exit
1626
1627 ; INT 15h; we must intercept AH=87h for extended memory programs to work properly within our VM86 environment
1628 .vm86_int15:    mov             ebx,int_EAX
1629                 cmp             bh,0x87
1630                 jz              .vm86_int15_87
1631 ; carry it forward to the BIOS
1632                 mov             eax,int_INTNUM
1633                 call            vm86_push_interrupt
1634                 int_exit
1635
1636 ; INT 15H AH=87h
1637 .vm86_int15_87: xor             eax,eax
1638                 mov             ax,int_v86_ES
1639                 shl             eax,4
1640                 and             int_ESI,0xFFFF
1641                 add             eax,int_ESI
1642                 mov             ecx,int_ECX
1643                 and             ecx,0xFFFF
1644 ; ECX = count of WORDs, EAX = physical memory addr of GDT
1645                 mov             esi,[es:eax+0x10+2]
1646                 and             esi,0xFFFFFF
1647                 mov             edi,[es:eax+0x18+2]
1648                 and             edi,0xFFFFFF
1649 ; ESI = physical memory addr (src), EDI = physical memory addr (dst)
1650                 movzx           ebx,byte [es:eax+0x10+7]
1651                 shl             ebx,24
1652                 or              esi,ebx
1653                 movzx           ebx,byte [es:eax+0x18+7]
1654                 shl             ebx,24
1655                 or              edi,ebx
1656 ; carry out the actual copy
1657                 push            ds
1658                 mov             ax,es
1659                 mov             ds,ax
1660                 cld
1661                 rep             movsw
1662                 pop             ds
1663 ; signal success
1664                 mov             int_EAX,0
1665                 int_exit
1666
1667 .not_vm86:      call            interrupt_routine_halt_prepare
1668                 call            edx_esi_default_int_str
1669                 jmp             fault_jmp_unhandled
1670 .rm_int_api:    mov             eax,int_EAX
1671                 cmp             eax,0xAABBAABB                  ; command to unload
1672                 jz              .rm_int_api_unload
1673                 cmp             eax,0xAABBAA55                  ; detection
1674                 jz              .rm_int_api_detect
1675 ; unknown command
1676                 mov             int_EAX,eax
1677                 call            interrupt_routine_halt_prepare
1678                 call            edx_esi_default_int_str
1679                 jmp             fault_jmp_unhandled
1680 .rm_int_api_unload:
1681                 mov             eax,int_v86_ESP
1682                 mov             [unload_int_stk+0],ax
1683                 mov             ax,int_v86_SS
1684                 mov             [unload_int_stk+2],ax
1685
1686                 mov             eax,int_EIP
1687                 mov             [unload_int_ret+0],ax
1688                 mov             ax,int_CS
1689                 mov             [unload_int_ret+2],ax
1690
1691                 mov             ax,DATA32_SEL
1692                 mov             ds,ax
1693                 mov             es,ax
1694                 mov             fs,ax
1695                 mov             gs,ax
1696                 mov             esp,stack_init
1697                 jmp             CODE32_SEL:_exit_from_prot32
1698 .rm_int_api_detect:
1699                 mov             int_EAX,0xBBAABB33
1700                 int_exit
1701 edx_esi_default_int_str:
1702                 mov             edx,int_INTNUM
1703                 cmp             edx,0x13
1704                 jle             .l1
1705                 mov             edx,0x13
1706 .l1:            xor             esi,esi
1707                 mov             si,word [(edx*2)+interrupt_0x00_strs]
1708                 mov             edx,int_INTNUM
1709                 ret
1710 interrupt_routine_halt_prepare: ; <====== SET ESI = address of string to print. Trashes EAX.
1711                 xor             eax,eax
1712                 mov             dword [unhandled_fault_var_opcode],eax
1713                 mov             eax,int_EAX
1714                 mov             dword [unhandled_fault_var_eax],eax
1715                 mov             eax,int_EBX
1716                 mov             dword [unhandled_fault_var_ebx],eax
1717                 mov             eax,int_ECX
1718                 mov             dword [unhandled_fault_var_ecx],eax
1719                 mov             eax,int_EDX
1720                 mov             dword [unhandled_fault_var_edx],eax
1721                 mov             eax,int_ESI
1722                 mov             dword [unhandled_fault_var_esi],eax
1723                 mov             eax,int_EDI
1724                 mov             dword [unhandled_fault_var_edi],eax
1725                 mov             eax,int_EBP
1726                 mov             dword [unhandled_fault_var_ebp],eax
1727                 mov             eax,int_ESP
1728                 mov             dword [unhandled_fault_var_esp],eax
1729                 mov             eax,int_EIP
1730                 mov             dword [unhandled_fault_var_eip],eax
1731                 mov             eax,int_ERRCODE
1732                 mov             dword [unhandled_fault_var_errcode],eax
1733                 xor             eax,eax
1734                 mov             ax,int_ES
1735                 mov             word [unhandled_fault_var_es],ax
1736                 mov             ax,int_DS
1737                 mov             word [unhandled_fault_var_ds],ax
1738                 mov             ax,int_CS
1739                 mov             word [unhandled_fault_var_cs],ax
1740                 mov             ax,fs
1741                 mov             word [unhandled_fault_var_fs],ax
1742                 mov             ax,gs
1743                 mov             word [unhandled_fault_var_gs],ax
1744                 mov             ax,ss
1745                 mov             word [unhandled_fault_var_ss],ax
1746                 mov             eax,cr0
1747                 mov             dword [unhandled_fault_var_cr0],eax
1748                 mov             eax,cr3
1749                 mov             dword [unhandled_fault_var_cr3],eax
1750                 mov             eax,cr4
1751                 mov             dword [unhandled_fault_var_cr4],eax
1752                 mov             eax,int_EFLAGS
1753                 mov             dword [unhandled_fault_var_eflags],eax
1754                 test            eax,0x20000             ; was the VM flag set in EFLAGS when it happened?
1755                 jnz             .v86_mode
1756                 test            int_CS,3                ; did we come from ring 1/2/3?
1757                 jz              .no_escalation
1758 ; ESP needs to reflect it's contents prior to jumping to ring 0
1759                 mov             eax,int_v86_ESP
1760                 mov             dword [unhandled_fault_var_esp],eax
1761 .no_escalation: ret
1762 ; the segment registers need to reflect what they were in vm86 mode
1763 .v86_mode:      xor             eax,eax
1764                 mov             ax,int_v86_ES
1765                 mov             word [unhandled_fault_var_es],ax
1766                 mov             ax,int_v86_DS
1767                 mov             word [unhandled_fault_var_ds],ax
1768                 mov             ax,int_v86_FS
1769                 mov             word [unhandled_fault_var_fs],ax
1770                 mov             ax,int_v86_GS
1771                 mov             word [unhandled_fault_var_gs],ax
1772                 mov             ax,int_v86_SS
1773                 mov             word [unhandled_fault_var_ss],ax
1774                 mov             eax,int_v86_ESP
1775                 mov             dword [unhandled_fault_var_esp],eax
1776 ; we also might want to know what opcodes were involved
1777                 push            es
1778                 mov             ax,FLAT32_SEL
1779                 call            vm86_cs_eip_to_phys
1780                 mov             ebx,[es:eax]
1781                 mov             dword [unhandled_fault_var_opcode],ebx
1782                 pop             es
1783                 ret
1784
1785 ; ================ INT 67h EMM/VCPI entry point
1786 ; TODO: At some point this code needs to be written to a) reflect the INT 67H call back to v86 mode if it's not EMM/VCPI functions
1787 ;       and b) chain to v86 mode if the return address CS:IP is not the real-mode INT 67H handler we installed (so that other
1788 ;       programs can hook the interrupt on top of us)
1789 interrupt_routine_emm:
1790                 int_entry
1791                 mov             eax,int_EAX
1792                 cmp             ah,0DEh
1793                 jz              .vcpi
1794                 cmp             ah,040h
1795                 jl              .pass
1796                 cmp             ah,05Eh
1797                 jae             .pass
1798                 jmp             .emm
1799                 int_exit
1800 .pass:          jmp             emm_unhandled
1801 ; VCPI processing (AH=0xDE)
1802 .vcpi:          cmp             al,0x0C
1803                 ja              emm_unhandled
1804                 movzx           eax,al
1805                 jmp             word [vcpi_functions+(eax*2)]
1806 ; EMM processing (AH=0x40...0x5D)
1807 .emm:           sub             ah,0x40
1808                 movzx           eax,ah
1809                 jmp             word [emm_functions+(eax*2)]
1810
1811 emm_unhandled:
1812 vcpi_unhandled:
1813                 call            interrupt_routine_halt_prepare
1814                 mov             edx,67h
1815                 mov             esi,str_unknown_emm_vcpi_call
1816                 jmp             fault_jmp_unhandled
1817
1818 ; VCPI call table
1819 vcpi_functions: dw              vcpi00_detect                   ; 0x00
1820                 dw              vcpi01_get_pm_if                ; 0x01
1821                 dw              vcpi_unhandled                  ; 0x02
1822                 dw              vcpi03_free_pages               ; 0x03
1823                 dw              vcpi04_alloc_page               ; 0x04
1824                 dw              vcpi05_free_page                ; 0x05
1825                 dw              vcpi06_get_phys_addr            ; 0x06
1826                 dw              vcpi_unhandled                  ; 0x07
1827                 dw              vcpi_unhandled                  ; 0x08
1828                 dw              vcpi_unhandled                  ; 0x09
1829                 dw              vcpi0A_8259A_mapping            ; 0x0A
1830                 dw              vcpi_unhandled                  ; 0x0B
1831                 dw              vcpi0C_enter_pm                 ; 0x0C
1832
1833 ; emm call table
1834 emm_functions:  dw              emm40_get_status                ; 0x40
1835                 dw              emm41_get_pfa                   ; 0x41
1836                 dw              emm42_get_unalloc_page_count    ; 0x42
1837                 dw              emm43_alloc_pages               ; 0x43
1838                 dw              emm44_map_handle_page           ; 0x44
1839                 dw              emm45_dealloc_pages             ; 0x45
1840                 dw              emm46_get_version               ; 0x46
1841                 dw              emm47_save_page_map             ; 0x47
1842                 dw              emm48_restore_page_map          ; 0x48
1843                 dw              emm_unhandled                   ; 0x49
1844                 dw              emm_unhandled                   ; 0x4A
1845                 dw              emm4B_get_handle_count          ; 0x4B
1846                 dw              emm4C_get_handle_pages          ; 0x4C
1847                 dw              emm4D_get_all_handle_pages      ; 0x4D
1848                 dw              emm4E_combo                     ; 0x4E
1849                 dw              emm4F_combo                     ; 0x4F
1850                 dw              emm50_combo                     ; 0x50
1851                 dw              emm51_realloc_pages             ; 0x51
1852                 dw              emm52_combo                     ; 0x52
1853                 dw              emm53_combo                     ; 0x53
1854                 dw              emm54_combo                     ; 0x54
1855                 dw              emm55_combo                     ; 0x55
1856                 dw              emm56_combo                     ; 0x56
1857                 dw              emm57_combo                     ; 0x57
1858                 dw              emm58_combo                     ; 0x58
1859                 dw              emm59_combo                     ; 0x59
1860                 dw              emm5A_combo                     ; 0x5A
1861                 dw              emm5B_combo                     ; 0x5B
1862                 dw              emm5C_prepare_warmboot          ; 0x5C
1863                 dw              emm5D_combo                     ; 0x5D
1864
1865 ; 8042 trapping state
1866 trap_8042_mode                  db              0x00
1867 ; port 92 trapping
1868 fake_port_92                    db              0x02            ; the reason we save this is some programs insist on
1869                                                                 ; reading back to check. if we forced bits the program
1870                                                                 ; will get stuck in a loop.
1871
1872 ; VCPI I/O write
1873 ; in: EDX=port number EAX/AX/AL=byte value to write  ECX=I/O size
1874 ; out: EAX/AX/AL=byte value to return
1875 on_vm86_io_write:               cmp             edx,0x64
1876                                 jz              .write_8042_controller
1877                                 cmp             edx,0x92
1878                                 jz              .write_port_A_92h
1879 .unknown_port:                  ret             ; IGNORE
1880 .write_8042_controller:         cmp             byte [trap_8042_mode],0xD1              ; trap Write Output Port bitfield
1881                                 jz              .write_8042_controller_out
1882 ; now match the command directly
1883                                 cmp             al,0xF0                                 ; trap attempts to pulse output bits
1884                                 jae             .write_8042_controller_outpulse
1885                                 cmp             al,0xD1                                 ; trap Write Output Port
1886                                 jz              .write_8042_controller_out_and_latch
1887 ; command is OK, pass it through
1888                                 out             0x64,al
1889                                 ret
1890 ; the previous byte was the Write Output command, so the next byte is the actual bits to write. We must ensure A20 is on.
1891 .write_8042_controller_out:     or              al,0x02         ; ensure bit 1 (A20 gate) is always on
1892                                 out             0x64,al
1893                                 mov             byte [trap_8042_mode],0x00
1894                                 ret
1895 ; the program attempted to pulse output bits. silently ignore.
1896 .write_8042_controller_outpulse:ret
1897 ; the program sent us a command that we pass through, but we must modify the data byte as it passes
1898 .write_8042_controller_out_and_latch:
1899                                 mov             byte [trap_8042_mode],al
1900                                 out             0x64,al         ; then pass the command through
1901                                 ret
1902 ; the program is writing port 0x92 to try and fiddle with A20
1903 .write_port_A_92h:              mov             byte [fake_port_92],al ; store what the program wrote
1904                                 or              al,2            ; make sure A20 gate is on
1905                                 and             al,0xFE         ; don't let them reset CPU
1906                                 out             0x92,al         ; then let it through
1907                                 ret
1908
1909 ; VCPI I/O read
1910 ; in: EDX=port number ECX=I/O size
1911 ; out: EAX/AX/AL=byte value to return
1912 on_vm86_io_read:                cmp             edx,0x64
1913                                 jz              .read_8042_controller
1914                                 cmp             edx,0x92
1915                                 jz              .read_port_A_92h
1916 .unknown_port:                  xor             eax,eax         ; return 0xFF
1917                                 dec             eax
1918                                 ret
1919 .read_8042_controller:          in              al,0x64         ; let it through
1920                                 movzx           eax,al
1921                                 ret
1922 .read_port_A_92h:               movzx           eax,byte [fake_port_92] ; return last written value
1923                                 ret
1924
1925 ; VCPI protected mode entry (32-bit)
1926 vcpi_pm_entry:                  pm_int_entry
1927                                 call            interrupt_routine_halt_prepare
1928                                 mov             edx,67h
1929                                 mov             esi,str_unknown_emm_vcpi_pm_call
1930                                 jmp             fault_jmp_unhandled                     
1931                                 pm_int_exit
1932
1933 ; AX=0xDE00 VCPI detect
1934 vcpi00_detect:                  mov             int_EAX,0       ; present
1935                                 mov             int_EBX,0x0100  ; version v1.0
1936                                 int_exit
1937
1938 ; AX=0xDE01 VCPI get protected mode interface
1939 vcpi01_get_pm_if:               mov             ax,FLAT32_SEL
1940                                 mov             es,ax
1941 ; fill page table ES:DI and advance DI += 4096 on return to client
1942                                 xor             edi,edi
1943                                 mov             di,int_v86_ES
1944                                 shl             edi,4
1945                                 add             edi,int_EDI                     ; EDI = (ES<<4)+DI
1946                                 add             int_EDI,4096                    ; client (E)DI += 4
1947 ; make fake page table for first 4MB that is 1:1 identity mapping
1948                                 mov             ecx,4096>>2
1949                                 xor             ebx,ebx
1950                                 cld
1951 .pmfill:                        lea             eax,[ebx+0x007]                 ; EAX = PTE: base address + present + read/write, user
1952                                 add             ebx,0x1000
1953                                 stosd
1954                                 loop            .pmfill
1955
1956 ; then fill in the GDT table given by the client
1957                                 cld
1958                                 xor             edi,edi
1959                                 mov             di,int_v86_DS
1960                                 shl             edi,4
1961                                 add             edi,int_ESI                     ; EDI = (DS<<4)+SI
1962                                 ; CODE
1963                                 lea             esi,[gdt+(CODE32_SEL&0xFFF8)]   ; ESI = our code32 GDT
1964                                 lodsd                                           ; ****COPY****
1965                                 stosd
1966                                 lodsd
1967                                 stosd
1968                                 ; DATA (not that it matters)
1969                                 lea             esi,[gdt+(DATA32_SEL&0xFFF8)]   ; ESI = our data32 GDT
1970                                 lodsd                                           ; ****COPY****
1971                                 stosd
1972                                 lodsd
1973                                 stosd
1974                                 ; DATA (not that it matters)
1975                                 lea             esi,[gdt+(FLAT32_SEL&0xFFF8)]   ; ESI = our data32 GDT
1976                                 lodsd                                           ; ****COPY****
1977                                 stosd
1978                                 lodsd
1979                                 stosd
1980
1981                                 mov             int_EAX,0                       ; OK, no error
1982                                 mov             int_EBX,vcpi_pm_entry           ; our VCPI entry point
1983                                 int_exit
1984
1985 ; AX=0xDE03 VCPI get free pages:
1986 vcpi03_free_pages:              mov             int_EAX,0       ; OK
1987                                 mov             int_EDX,(4*1024*1024)/4096      ; just report 4MB and be done with it
1988                                 int_exit
1989
1990 ; AX=0xDE04 VCPI alloc page:
1991 vcpi04_alloc_page:              mov             ax,FLAT32_SEL
1992                                 mov             es,ax
1993                                 mov             esi,[vcpi_alloc_bitmap_phys]
1994                                 mov             ecx,[vcpi_alloc_bitmap_size]
1995                                 cld
1996 .scan:                          mov             al,byte [es:esi]
1997                                 cmp             al,0xFF
1998                                 jnz             .found
1999                                 inc             esi
2000                                 dec             ecx
2001                                 jnz             .scan
2002 .not_found:                     mov             int_EAX,0x8800                  ; not found
2003                                 mov             int_EDX,0
2004                                 int_exit
2005 .found:                         not             al
2006                                 xor             ah,ah
2007                                 xor             ebx,ebx
2008                                 bsf             bx,ax                           ; BX=# of bit that is zero
2009                                 jz              .bmp_error                      ; ZF=0 or else
2010                                 mov             cl,bl
2011                                 mov             al,1
2012                                 shl             al,cl
2013                                 or              byte [es:esi],al
2014                                 sub             esi,[vcpi_alloc_bitmap_phys]
2015                                 lea             eax,[esi*8+ebx]                 ; EAX = page number
2016                                 shl             eax,12                          ; EAX = page address
2017                                 add             eax,[vcpi_alloc_pages_phys]
2018                                 mov             int_EDX,eax                     ; EDX = physical page #
2019                                 mov             int_EAX,0
2020                                 int_exit
2021 .bmp_error:                     call            interrupt_routine_halt_prepare
2022                                 mov             edx,67h
2023                                 mov             esi,str_vcpi_alloc_page_bug
2024                                 jmp             fault_jmp_unhandled                     
2025
2026 ; AX=0xDE05 VCPI alloc page:
2027 vcpi05_free_page:               mov             ax,FLAT32_SEL
2028                                 mov             es,ax
2029
2030                                 mov             edx,int_EDX
2031                                 sub             edx,[vcpi_alloc_pages_phys]
2032                                 jc              .not_found
2033                                 shr             edx,12
2034                                 mov             esi,edx
2035                                 shr             esi,3
2036                                 cmp             esi,[vcpi_alloc_bitmap_size]
2037                                 jae             .not_found
2038                                 mov             ecx,edx
2039                                 and             ecx,7
2040                                 mov             al,1
2041                                 shl             al,cl
2042                                 not             al
2043                                 add             esi,[vcpi_alloc_bitmap_phys]
2044                                 int             3                               ; <- FIXME: Remove debug b.p. when you verify this works
2045                                 and             [es:esi],al
2046                                 int_exit
2047 .not_found:                     mov             int_EAX,0x8A00
2048                                 mov             eax,0xABCD1234                  ; <- DEBUG: remove when you verify this works
2049                                 int             3
2050                                 int_exit
2051
2052 ; AX=0xDE06 VCPI get physical address of 4K page
2053 vcpi06_get_phys_addr:           mov             eax,int_ECX
2054                                 and             eax,0xFFFF
2055                                 shl             eax,12
2056                                 mov             int_EDX,eax
2057                                 mov             int_EAX,0
2058                                 int_exit
2059
2060 ; AX=0xDE0A VCPI get Interrupt Vector Mappings
2061 vcpi0A_8259A_mapping:           mov             int_EAX,0
2062                                 mov             int_EBX,IRQ_BASE_INT    ; 1st vector for master PIC
2063                                 mov             int_ECX,IRQ_BASE_INT+8  ; 1st vector for slave PIC
2064                                 int_exit
2065
2066 ; AX=0xDE0C VCPI enter protected mode
2067 vcpi0C_enter_pm:                call            interrupt_routine_halt_prepare
2068                                 mov             edx,67h
2069                                 mov             esi,str_vcpi_pm_not_supported
2070                                 jmp             fault_jmp_unhandled
2071
2072 ; AH=0x40 Get Status
2073 emm40_get_status:               mov             int_EAX,0       ; we're OK
2074                                 int_exit
2075
2076 emm41_get_pfa:                  mov             int_EAX,0       ; OK
2077                                 mov             int_EBX,0xC000  ; FIXME: We don't provide ANY pages, just make the VGA BISO the "page frame"
2078                                 int_exit
2079
2080 emm42_get_unalloc_page_count:   mov             int_EAX,0       ; OK
2081                                 mov             int_EBX,0       ; no pages free
2082                                 mov             int_EDX,0       ; no pages at all
2083                                 int_exit
2084
2085 emm43_alloc_pages:              mov             int_EAX,0x8700  ; not enough memory
2086                                 mov             int_EDX,0       ; handle=0
2087                                 int_exit
2088
2089 emm44_map_handle_page:          mov             int_EAX,0x8A00  ; out of range
2090                                 int_exit
2091
2092 emm45_dealloc_pages:            mov             int_EAX,0       ; uhhhh... OK
2093                                 int_exit
2094
2095 emm46_get_version:              mov             int_EAX,0x40    ; version 4.0
2096                                 int_exit
2097
2098 emm47_save_page_map:            jmp     emm_unhandled
2099 emm48_restore_page_map:         jmp     emm_unhandled
2100 emm4B_get_handle_count:         jmp     emm_unhandled
2101 emm4C_get_handle_pages:         jmp     emm_unhandled
2102 emm4D_get_all_handle_pages:     jmp     emm_unhandled
2103 emm4E_combo:                    jmp     emm_unhandled
2104 emm4F_combo:                    jmp     emm_unhandled
2105 emm50_combo:                    jmp     emm_unhandled
2106 emm51_realloc_pages:            jmp     emm_unhandled
2107 emm52_combo:                    jmp     emm_unhandled
2108 emm53_combo:                    jmp     emm_unhandled
2109 emm54_combo:                    jmp     emm_unhandled
2110 emm55_combo:                    jmp     emm_unhandled
2111 emm56_combo:                    jmp     emm_unhandled
2112 emm57_combo:                    jmp     emm_unhandled
2113
2114 emm58_combo:                    mov     eax,int_EAX
2115                                 cmp     al,0    
2116                                 jz      .func_5800
2117                                 jmp     emm_unhandled
2118 .func_5800: ; ========================== INT 67h AX=5800h
2119                                 mov     int_EAX,0               ; OK
2120                                 mov     int_ECX,0               ; the returned array is zero entries in length
2121                                 int_exit
2122
2123 emm59_combo:                    jmp     emm_unhandled
2124 emm5A_combo:                    jmp     emm_unhandled
2125 emm5B_combo:                    jmp     emm_unhandled
2126 emm5C_prepare_warmboot:         jmp     emm_unhandled
2127 emm5D_combo:                    jmp     emm_unhandled
2128
2129
2130 %unmacro int_entry 0
2131 %unmacro int_exit 0
2132 %undef          int_ES
2133 %undef          int_DS
2134 %undef          int_EDI
2135 %undef          int_ESI
2136 %undef          int_EBP
2137 %undef          int_ESP
2138 %undef          int_EBX
2139 %undef          int_EDX
2140 %undef          int_ECX
2141 %undef          int_EAX
2142 %undef          int_INTNUM
2143 %undef          int_INTNUM_b
2144 %undef          int_ERRCODE
2145 %undef          int_EIP_w
2146 %undef          int_EIP
2147 %undef          int_CS
2148 %undef          int_EFLAGS
2149 %undef          int_v86_SS
2150 %undef          int_v86_ES
2151 %undef          int_v86_DS
2152 %undef          int_v86_FS
2153 %undef          int_v86_GS
2154
2155 ; ========== FAULT COLLECTION ROUTINE. SS:ESP should point to fault. If the exception does not push an error code,
2156 ;    then the caller must push a dummy error code
2157 ; if privilege escalation was involved (stack switching) then retrieve SS:ESP at fault from the stack frame.
2158 ; else retrieve from actual SS:ESP registers
2159 fault_jmp_unhandled:
2160                 jmp             CODE16_SEL:.thunk16
2161                 bits            16
2162 .thunk16:       mov             ax,DATA16_SEL
2163                 mov             ds,ax
2164                 mov             es,ax
2165                 mov             ss,ax
2166                 mov             sp,stack_init
2167                 jmp             unhandled_fault_errcode
2168                 bits            32
2169
2170 ; ============= cleanup, exit to DOS (from 32-bit protected mode)
2171 _exit_from_prot32:
2172                 jmp             CODE16_SEL:.entry16
2173                 bits            16
2174 .entry16:       mov             ax,DATA16_SEL
2175                 mov             ds,ax
2176                 mov             es,ax
2177                 mov             fs,ax
2178                 mov             gs,ax
2179                 mov             ss,ax
2180                 mov             esp,stack_init
2181                 
2182 ; ============= cleanup, exit to DOS (from 16-bit protected mode)
2183 _exit_from_prot16:
2184                 mov             ax,DATA16_SEL
2185                 mov             ds,ax
2186                 mov             es,ax
2187                 mov             fs,ax
2188                 mov             gs,ax
2189                 mov             ss,ax
2190
2191                 ; overwrite the far jmp's segment value
2192                 mov             ax,[my_realmode_seg]
2193                 mov             word [.real_hackme+3],ax
2194                 mov             esp,stack_init
2195
2196                 mov             eax,0x00000010
2197                 mov             cr0,eax
2198
2199                 lidt            [idtr_real]
2200                 lgdt            [gdtr_real]
2201
2202 .real_hackme:   jmp             0:.real_entry
2203 .real_entry:    mov             ax,[cs:my_realmode_seg]
2204                 mov             ds,ax
2205                 mov             es,ax
2206                 mov             fs,ax
2207                 mov             gs,ax
2208                 mov             ss,ax
2209                 mov             sp,stack_init
2210
2211 ; reprogram the PIC back to what normal DOS expects: IRQ 0-7 => INT 8-15
2212                 mov             al,0x10                 ; ICW1 A0=0
2213                 out             20h,al
2214                 mov             al,0x08                 ; ICW2 A0=1
2215                 out             21h,al
2216                 mov             al,0x04                 ; ICW3 A0=1 slave on IRQ 2
2217                 out             21h,al
2218
2219                 mov             al,0x10                 ; ICW1 A0=0
2220                 out             0xA0,al
2221                 mov             al,0x70                 ; ICW2 A0=1
2222                 out             0xA1,al
2223                 mov             al,0x02                 ; ICW3 A0=1
2224                 out             0xA1,al
2225
2226 ; remove our INT 66h API
2227                 xor             ax,ax
2228                 mov             es,ax
2229                 mov             word [es:(RM_INT_API*4)],ax
2230                 mov             word [es:(RM_INT_API*4)+2],ax
2231
2232 ; free HIMEM.SYS blocks
2233                 test            word [himem_sys_buffer_handle],0
2234                 jz              .no_himem_sys
2235                 mov             ah,0Dh                  ; HIMEM.SYS function 0Dh unlock memory block
2236                 mov             dx,word [himem_sys_buffer_handle]
2237                 call far        word [himem_sys_entry]
2238                 mov             ah,0Ah                  ; HIMEM.SYS function 0Ah free memory block
2239                 mov             dx,word [himem_sys_buffer_handle]
2240                 call far        word [himem_sys_entry]
2241 .no_himem_sys:
2242
2243 ; if we already exited as a TSR...
2244                 test            byte [i_am_tsr],1
2245                 jnz             .tsr_exit
2246
2247 ; time to exit to DOS
2248                 mov             dx,str_exit_to_dos
2249                 jmp             _exit_with_msg
2250
2251 ; ============= ALTERNATE EXIT IF WE ALREADY EXITED AS TSR
2252 .tsr_exit:      
2253                 mov             ax,cs
2254                 mov             es,ax                   ; ES = our code segment which is also our PSP segment
2255                 mov             ah,0x49                 ; function 49h free memory block
2256                 clc
2257                 int             21h
2258                 jnc             .tsr_exit_free_ok
2259                 mov             dx,str_cannot_free_self
2260                 call            dos_puts
2261 .tsr_exit_free_ok:
2262                 cli
2263                 mov             ax,[cs:unload_int_stk+0]        ; offset
2264                 add             ax,6                            ; discard prior frame
2265                 mov             sp,ax
2266                 mov             ax,[cs:unload_int_stk+2]        ; segment
2267                 mov             ss,ax
2268
2269                 mov             dx,str_exit_to_dos
2270                 call            dos_puts
2271
2272                 cmp             word [himem_sys_buffer_handle],0 ; if there is a handle to free, then do it
2273                 jz              .no_handle
2274                 mov             ah,0Dh                  ; HIMEM.SYS function 0Dh unlock memory block
2275                 mov             dx,word [himem_sys_buffer_handle]
2276                 call far        word [himem_sys_entry]
2277                 mov             ah,0Ah                  ; HIMEM.SYS function 0Ah free memory block
2278                 mov             dx,word [himem_sys_buffer_handle]
2279                 call far        word [himem_sys_entry]
2280 .no_handle:
2281
2282                 cli
2283
2284                 mov             al,020h
2285
2286                 out             0A0h,al
2287                 out             0A0h,al
2288                 out             0A0h,al
2289                 out             0A0h,al
2290                 out             0A0h,al
2291                 out             0A0h,al
2292                 out             0A0h,al
2293                 out             0A0h,al
2294
2295                 out             20h,al
2296                 out             20h,al
2297                 out             20h,al
2298                 out             20h,al
2299                 out             20h,al
2300                 out             20h,al
2301                 out             20h,al
2302                 out             20h,al
2303
2304                 in              al,61h
2305                 in              al,61h
2306                 in              al,61h
2307                 in              al,61h
2308
2309                 sti
2310
2311                 jmp far         word [cs:unload_int_ret]
2312
2313 ; ============= UNHANDLED FAULT HANDLER (16-bit code)
2314 ;   input: EDX = Number of interrupt
2315 ;          SI = Textual string of fault
2316 ;                  
2317 unhandled_fault_errcode:
2318                 cli
2319                 mov             ax,DATA16_SEL
2320                 mov             ds,ax
2321
2322                 mov             ax,[cs:my_realmode_seg]
2323                 mov             word [.real16jmp+3],ax
2324
2325                 mov             ax,FLAT16_SEL
2326                 mov             ds,ax
2327                 mov             es,ax
2328                 mov             ss,ax
2329
2330                 lgdt            [cs:gdtr_real]
2331                 lidt            [cs:idtr_real]
2332
2333                 ; crash-thunk to real mode
2334                 mov             eax,0x00000010
2335                 mov             cr0,eax
2336 .real16jmp:     jmp             0:.real16
2337 .real16:        mov             ax,[cs:my_realmode_seg]
2338                 mov             ds,ax
2339                 mov             ss,ax
2340                 xor             ax,ax
2341                 mov             es,ax
2342
2343                 mov             ax,3
2344                 int             10h
2345
2346                 cld
2347                 mov             ax,0x4E20
2348                 mov             ecx,80*25
2349                 mov             edi,0xB8000
2350                 a32 rep         stosw
2351
2352                 ; print exception name on screen
2353                 mov             edi,0xB8000
2354                 call            .unhandled_print
2355                 mov             al,' '          ; +space plus AH still contains upper byte from .unhandled_print
2356                 a32 stosw
2357
2358                 ; then the number (in EDX) write to DS:DI
2359                 mov             eax,edx
2360                 push            edi
2361                 mov             edi,scratch_str
2362                 call            eax_to_hex_16_dos
2363                 lea             si,[di+6]       ; only the last two hex digits
2364                 pop             edi
2365                 call            .unhandled_print
2366
2367                 ; print the registers.
2368                 ; during this loop: SI = print list  EDI = location on screen to draw   [ESP] = location on screen of row start
2369                 mov             edi,0xB8000+(160*2)     ; two lines down
2370                 push            edi
2371                 mov             si,printlist_32
2372 .regprint32:    lodsw
2373                 or              ax,ax
2374                 jz              .regprint32e            ; AX=0 STOP
2375                 dec             ax
2376                 jz              .regprint32nl           ; AX=1 GO TO NEW LINE
2377                 push            si
2378                 mov             si,ax                   ; SI=AX=address of variable name
2379                 inc             si
2380                 call            .unhandled_print
2381                 pop             si
2382                 mov             ax,0x4E00 | (':')
2383                 a32 stosw
2384                 lodsw                                   ; SI=address of variable
2385                 push            si
2386                 mov             si,ax
2387                 mov             eax,[si]
2388                 push            edi
2389                 mov             edi,scratch_str
2390                 call            eax_to_hex_16_dos
2391                 mov             esi,edi
2392                 pop             edi
2393                 call            .unhandled_print
2394                 pop             si
2395                 mov             ax,0x4E00 | (' ')
2396                 a32 stosw
2397                 jmp             .regprint32
2398 .regprint32nl:  pop             edi
2399                 add             edi,160                 ; move to next line, save back to [ESP]
2400                 push            edi
2401                 jmp             .regprint32
2402 .regprint32e:   pop             edi
2403
2404                 add             edi,160                 ; next line...
2405
2406                 mov             si,printlist_16
2407 .regprint16:    lodsw
2408                 or              ax,ax
2409                 jz              .regprint16e            ; AX=0 STOP
2410                 dec             ax
2411                 jz              .regprint16nl           ; AX=1 GO TO NEW LINE
2412                 push            si
2413                 mov             si,ax                   ; SI=AX=address of variable name
2414                 inc             si
2415                 call            .unhandled_print
2416                 pop             si
2417                 mov             ax,0x4E00 | (':')
2418                 a32 stosw
2419                 lodsw                                   ; SI=address of variable
2420                 push            si
2421                 mov             si,ax
2422                 xor             eax,eax
2423                 mov             ax,[si]
2424                 push            edi
2425                 mov             edi,scratch_str
2426                 call            eax_to_hex_16_dos
2427                 lea             esi,[edi+4]
2428                 pop             edi
2429                 call            .unhandled_print
2430                 pop             si
2431                 mov             ax,0x4E00 | (' ')
2432                 a32 stosw
2433                 jmp             .regprint16
2434 .regprint16nl:  pop             edi
2435                 add             edi,160                 ; move to next line, save back to [ESP]
2436                 push            edi
2437                 jmp             .regprint16
2438 .regprint16e:   mov             si,str_mode_prot        ; CPU mode
2439                 test            dword [unhandled_fault_var_eflags],0x20000
2440                 jz              .regprint_cpu_mode_not_v86
2441                 mov             si,str_mode_v86
2442 .regprint_cpu_mode_not_v86:
2443                 call            .unhandled_print
2444                 pop             edi
2445
2446                 mov             al,020h
2447
2448                 out             0A0h,al
2449                 out             0A0h,al
2450                 out             0A0h,al
2451                 out             0A0h,al
2452                 out             0A0h,al
2453                 out             0A0h,al
2454                 out             0A0h,al
2455                 out             0A0h,al
2456
2457                 out             20h,al
2458                 out             20h,al
2459                 out             20h,al
2460                 out             20h,al
2461                 out             20h,al
2462                 out             20h,al
2463                 out             20h,al
2464                 out             20h,al
2465
2466                 sti
2467                 jmp             short $
2468 ; ===== print on screen from DS:SI to ES:EDI
2469 .unhandled_print:
2470                 lodsb
2471                 cmp             al,'$'
2472                 jz              .unhandled_printe
2473                 mov             ah,0x4E
2474                 a32 stosw
2475                 jmp             .unhandled_print
2476 .unhandled_printe:
2477                 ret
2478
2479 ; ============= Entry point (virtual 8086 mode)
2480 vm86_entry:     mov             ax,ds           ; *DEBUG*
2481                 mov             ss,ax
2482
2483                 cli                             ; make sure the v86 monitor handles CLI
2484                 sti                             ; ...and STI
2485                 pushf                           ; ...and PUSHF
2486                 popf                            ; ...and POPF
2487                 pushfd                          ; ...32-bit PUSHF
2488                 popfd                           ; ...32-bit POPF
2489                 in              al,21h          ; ...IN?
2490                 out             21h,al          ; ...OUT?
2491
2492                 ; can I clear vm86 mode by clearing the VM bit?
2493                 pushfd
2494                 pop             eax
2495                 and             eax,~0x20000
2496                 push            eax
2497                 popfd
2498
2499                 ; NOW MAKE SURE PUSHF/POPF STORE THE VALUE ON-STACK LIKE THEY'RE SUPPOSED TO
2500                 mov             bx,sp
2501                 mov             word [ss:bx-2],0x5A5A
2502                 pushf
2503                 mov             bx,sp
2504                 cmp             word [ss:bx],0x5A5A
2505                 jnz             .pushf_ok       ; if the value DIDN'T CHANGE then the monitor failed to write FLAGS to stack
2506                 mov             ax,0
2507                 jmp             vm86_errcode
2508 .pushf_ok:
2509
2510                 ; DOES POPF WORK?
2511                 mov             ax,0x492
2512                 push            ax
2513                 popf
2514                 pushf
2515                 pop             ax
2516                 and             ax,0xFD6
2517                 cmp             ax,0x492
2518                 jz              .popf_ok
2519                 mov             ax,1
2520                 jmp             vm86_errcode
2521 .popf_ok:
2522
2523                 ; TEST 32-bit PUSHF
2524                 mov             bx,sp
2525                 mov             dword [ss:bx-4],0x5A5A5A5A
2526                 pushfd
2527                 mov             bx,sp
2528                 cmp             dword [ss:bx],0x5A5A5A5A
2529                 jnz             .pushfd_ok      ; if the value DIDN'T CHANGE then the monitor failed to write FLAGS to stack
2530                 mov             ax,2
2531                 jmp             vm86_errcode
2532 .pushfd_ok:
2533
2534                 ; DOES POPFD WORK?
2535                 mov             eax,0x492
2536                 push            eax
2537                 popfd
2538                 pushfd
2539                 pop             eax
2540                 and             eax,0xFD6
2541                 cmp             eax,0x492
2542                 jz              .popfd_ok
2543                 mov             ax,3
2544                 jmp             vm86_errcode
2545 .popfd_ok:
2546
2547                 ; IF I CLEAR INTERRUPT (CLI) AND THEN EXECUTE AN INTERRUPT, DOES IT COME BACK ENABLED?
2548                 cli
2549                 mov             ah,0x0F                 ; INT 10 AH=0x0F which has no visisible effect
2550                 int             10h
2551                 pushf
2552                 pop             ax
2553                 test            ax,0x200
2554                 jz              .int_doesnt_enable
2555                 mov             ax,4
2556                 jmp             vm86_errcode
2557 .int_doesnt_enable:
2558
2559                 ; HELLO WORLD!
2560                 mov             si,str_vm86_hello
2561                 call            bios_puts
2562
2563                 ; TEST DEFERRED IRQ MECHANISM BY DELIBERATLEY HALTING FOR AWHILE
2564                 cli
2565                 mov             ecx,0x1000000           ; delibrate slow countdown loop
2566 .l1:            dec             ecx
2567                 jnz             .l1
2568                 sti
2569
2570                 ; for my next trick, I will exit to DOS as a TSR
2571                 ; and allow the user to run the whole DOS kernel this way :)
2572                 mov             es,[cs:0x2C]            ; locate our environment block and free it
2573                 mov             ah,0x49                 ; function 49h free memory block
2574                 int             21h
2575                 jnc             .env_free_ok
2576                 mov             ax,4
2577                 jmp             vm86_errcode
2578 .env_free_ok:   mov             word [cs:0x2C],0        ; rub out the ENV block
2579
2580                 ; setup our INT 66h API
2581                 xor             ax,ax
2582                 mov             es,ax
2583                 mov             word [es:(RM_INT_API*4)],realmode_api_entry
2584                 mov             ax,cs
2585                 mov             word [es:(RM_INT_API*4)+2],ax
2586
2587                 ; setup our INT 67h vector
2588                 ; REMINDER: On exit you need to restore this vector
2589                 mov             ax,cs
2590                 mov             bx,rm_int_67_entry
2591                 shr             bx,4
2592                 add             ax,bx
2593                 mov             word [es:(0x67*4)+2],ax
2594                 xor             ax,ax
2595                 mov             word [es:(0x67*4)+0],ax
2596
2597                 ; finally, terminate and stay resident
2598                 mov             byte [i_am_tsr],1
2599                 mov             edx,the_end             ; DX = memory in paragraphs to save
2600                 add             edx,15
2601                 shr             edx,4
2602                 add             edx,0x11                ; <-- FIXME: IS THIS NECESSARY?
2603                 mov             ah,0x31                 ; function 31h terminate and stay resident
2604                 int             21h
2605
2606 ; ============= "Secret Handshake" to exit back into the v86 monitor and shutdown the program (virtual 8086 mode)
2607 ; TODO: Remove this, call into RM_INT_API instead
2608 vm86_exit:      mov             eax,0xAABBAABB
2609                 int             RM_INT_API
2610                 hlt
2611
2612 ; ============= If any of our self-test fails, we draw DIRECTLY ON VGA RAM and hike back into the vm86 monitor ASAP.
2613 ;   if self-tests fail chances are calling the BIOS/DOS will cause major problems. AX=CODE
2614 vm86_errcode:   mov             bx,0xB800
2615                 mov             es,bx
2616                 and             ax,0xF
2617                 or              ax,0x4E30       ; AX = VGA alphanumeric code for that number
2618                 mov             [es:160],ax
2619                 jmp             vm86_exit
2620
2621 ; ============= Real-mode API entry (reflect to v86 monitor by executing an INT)
2622 ;    this would allow the trick to work even for programs that direct-call instead
2623 realmode_api_entry:
2624                 int             RM_INT_API
2625                 iret
2626
2627 ; ============= Parse command line (from PSP segment)
2628 parse_argv:     cld
2629                 mov             si,81h
2630 .scan:          lodsb
2631                 or              al,al
2632                 jz              .done
2633                 cmp             al,0Dh
2634                 jz              .done
2635                 cmp             al,20h
2636                 jz              .scan
2637                 cmp             al,'-'
2638                 jz              .switch
2639                 cmp             al,'/'
2640                 jz              .switch
2641                 ; FALL THROUGH WITH ZF=0 to return
2642 .done:          ret
2643                 ; AT THIS POINT: SI = just after the / or - in the switch
2644 .switch:        lodsb
2645                 cmp             al,'?'
2646                 jz              .help
2647                 cmp             al,'A'
2648                 jb              .unknown_switch
2649                 cmp             al,'Z'
2650                 ja              .unknown_switch
2651                 ; the A-Z switches are allowed to have "=NNNN" after them where N is some integer in hex or decimal
2652                 sub             al,'A'
2653                 mov             bl,al
2654                 xor             bh,bh           ; BX = index into lookup table
2655                 add             bx,bx
2656                 jmp             word [bx+.switch_az]
2657 .fail:          mov             al,1
2658 .help:          or              al,al           ; AL != 0 => ZF=0
2659                 ret
2660 .unknown_switch:mov             dx,str_unknown_switch
2661                 call            dos_puts
2662                 lea             dx,[si-2]       ; step back two chars
2663                 mov             byte [si],'$'
2664                 call            dos_puts
2665                 mov             dx,str_crlf
2666                 call            dos_puts
2667                 jmp             .fail
2668 ; ========== Switches CALL here if they need a numeric value to follow.
2669 ; returns to caller if so, parsing as 16-bit integer returned in EAX. Else,
2670 ; it discards the return address and jumps to the 'needs param' error message.
2671 .switch_needs_equ_check:
2672                 cmp             byte [si],'='
2673                 jnz             .switch_needs_equ_check_fail
2674                 inc             si
2675                 cli
2676                 xor             eax,eax
2677                 call            ax_strtol_16
2678                 ret
2679 .switch_needs_equ_check_fail:
2680                 add             sp,2            ; fall through
2681 .switch_needs_equ:
2682                 mov             dx,str_needs_equals
2683                 call            dos_puts
2684                 jmp             .fail
2685 ; ========== /B=<number>
2686 .switch_buffer_size:
2687                 call            .switch_needs_equ_check
2688                 shl             eax,10
2689                 mov             [himem_sys_buffer_size],eax
2690                 jmp             .scan
2691 ; ========== /U
2692 .switch_unload: mov             byte [user_req_unload],1
2693                 jmp             .scan
2694 ; ========== /I
2695 .switch_iopl:   mov             byte [user_req_iopl],0
2696                 jmp             .scan
2697 ; ========== /C
2698 .switch_cache_disable:
2699                 or              dword [cr0_more],0x40000000
2700                 jmp             .scan
2701 ; ========== /W
2702 .switch_writeback_disable:
2703                 or              dword [cr0_more],0x20000000
2704                 jmp             .scan
2705 ; switch A-Z jump table
2706 .switch_az:     dw              .unknown_switch                 ; /A
2707                 dw              .switch_buffer_size             ; /B=<number>
2708                 dw              .switch_cache_disable           ; /C
2709                 dw              .unknown_switch                 ; /D
2710                 dw              .unknown_switch                 ; /E
2711                 dw              .unknown_switch                 ; /F
2712                 dw              .unknown_switch                 ; /G
2713                 dw              .unknown_switch                 ; /H
2714                 dw              .switch_iopl                    ; /I
2715                 dw              .unknown_switch                 ; /J
2716                 dw              .unknown_switch                 ; /K
2717                 dw              .unknown_switch                 ; /L
2718                 dw              .unknown_switch                 ; /M
2719                 dw              .unknown_switch                 ; /N
2720                 dw              .unknown_switch                 ; /O
2721                 dw              .unknown_switch                 ; /P
2722                 dw              .unknown_switch                 ; /Q
2723                 dw              .unknown_switch                 ; /R
2724                 dw              .unknown_switch                 ; /S
2725                 dw              .unknown_switch                 ; /T
2726                 dw              .switch_unload                  ; /U
2727                 dw              .unknown_switch                 ; /V
2728                 dw              .switch_writeback_disable       ; /W
2729                 dw              .unknown_switch                 ; /X
2730                 dw              .unknown_switch                 ; /Y
2731                 dw              .unknown_switch                 ; /Z
2732
2733 ; register print list
2734 printlist_32:   dw              str_eax,        unhandled_fault_var_eax
2735                 dw              str_ebx,        unhandled_fault_var_ebx
2736                 dw              str_ecx,        unhandled_fault_var_ecx
2737                 dw              str_edx,        unhandled_fault_var_edx
2738                 dw              str_esi,        unhandled_fault_var_esi
2739                 dw              str_edi,        unhandled_fault_var_edi
2740                 dw              1
2741                 dw              str_ebp,        unhandled_fault_var_ebp
2742                 dw              str_esp,        unhandled_fault_var_esp
2743                 dw              str_eip,        unhandled_fault_var_eip
2744                 dw              str_eflags,     unhandled_fault_var_eflags
2745                 dw              str_errcode,    unhandled_fault_var_errcode
2746                 dw              str_cr0,        unhandled_fault_var_cr0
2747                 dw              1
2748                 dw              str_cr3,        unhandled_fault_var_cr3
2749                 dw              str_cr4,        unhandled_fault_var_cr4
2750                 dw              str_opcode,     unhandled_fault_var_opcode
2751                 dw              0
2752 printlist_16:   dw              str_cs,         unhandled_fault_var_cs
2753                 dw              str_ds,         unhandled_fault_var_ds
2754                 dw              str_es,         unhandled_fault_var_es
2755                 dw              str_fs,         unhandled_fault_var_fs
2756                 dw              str_gs,         unhandled_fault_var_gs
2757                 dw              str_ss,         unhandled_fault_var_ss
2758                 dw              0
2759
2760 ; ============= bios_puts (print $-terminated string at DS:SI)
2761 bios_puts:      cli
2762                 cld
2763                 push            ax
2764                 push            bx
2765 .putsloop:      lodsb
2766                 cmp             al,'$'
2767                 jz              .putsend
2768                 mov             ah,0x0E
2769                 xor             bx,bx
2770                 int             10h
2771                 jmp             .putsloop
2772 .putsend:       pop             bx
2773                 pop             ax
2774                 ret
2775
2776 ; ============= dos_puts (print $-terminated string at DS:DX)
2777 dos_puts:       mov             ah,09h
2778                 int             21h
2779                 ret
2780
2781 ; ============= read one digit from DS:SI return in AX (16-bit code)
2782 ax_strtol_16_single:mov         al,[si]
2783                 cmp             al,'0'
2784                 jb              .no
2785                 cmp             al,'9'
2786                 ja              .no
2787                 sub             al,'0'
2788                 xor             ah,ah
2789                 inc             si
2790                 clc
2791                 ret
2792 .no:            stc
2793                 ret
2794
2795 ; ============= read from DS:SI and convert numerical string to integer value return in AX (16-bit code)
2796 ax_strtol_16:   xor             cx,cx
2797 .loop:          push            cx
2798                 call            ax_strtol_16_single
2799                 pop             cx
2800                 jc              .done
2801                 mov             bx,cx
2802                 add             bx,bx
2803                 shl             cx,3            ; BX = CX * 2,  CX *= 8
2804                 add             cx,bx           ; CX = (CX * 8) + (CX * 2) = CX * 10
2805                 add             cx,ax           ; CX += new digit
2806                 jmp             .loop
2807 .done:          mov             ax,cx
2808                 ret
2809
2810 ; ============= take AX and write to buffer (DS:SI) as hexadecimal string (16-bit code)
2811 al_to_hex_16_dos:mov            byte [di+2],'$'
2812                 jmp             al_to_hex_16
2813 al_to_hex_16_nul:mov            byte [di+2],0
2814 al_to_hex_16:   push            di
2815                 push            bx
2816                 push            ax
2817                 xor             bh,bh
2818                 mov             ah,al
2819                 and             al,0xF
2820                 mov             bl,al
2821                 mov             al,[bx+str_hex]         ; AL' = str_hex[al]
2822                 shr             ah,4
2823                 mov             bl,ah
2824                 mov             ah,[bx+str_hex]         ; AH' = str_hex[ah]
2825                 mov             [di+0],ah
2826                 mov             [di+1],al
2827                 pop             ax
2828                 pop             bx
2829                 pop             di
2830                 ret
2831
2832 ; ============= take AX and write to buffer (DS:SI) as hexadecimal string (16-bit code)
2833 ax_to_hex_16_dos:mov            byte [di+4],'$'
2834                 jmp             ax_to_hex_16
2835 ax_to_hex_16_nul:mov            byte [di+4],0
2836 ax_to_hex_16:   push            di
2837                 push            ax
2838                 mov             al,ah
2839                 call            al_to_hex_16
2840                 pop             ax
2841                 add             di,2
2842                 call            al_to_hex_16
2843                 pop             di
2844                 ret
2845
2846 ; ============= take EAX and write to buffer (DS:DI) as hexadecimal string (16-bit code)
2847 eax_to_hex_16_dos:mov           byte [di+8],'$'
2848                 jmp             eax_to_hex_16
2849 eax_to_hex_16_nul:mov           byte [di+8],0
2850 eax_to_hex_16:  push            di
2851                 push            eax
2852                 shr             eax,16
2853                 call            ax_to_hex_16
2854                 pop             eax
2855                 add             di,4
2856                 call            ax_to_hex_16
2857                 pop             di
2858                 ret
2859
2860 ; ============= /U Unloading the resident copy of this program
2861 unload_this_program:
2862                 smsw            ax
2863                 test            al,1
2864                 jnz             .v86_active
2865                 mov             dx,str_not_loaded
2866                 jmp             _exit_with_msg
2867 .v86_active:
2868                 xor             ax,ax
2869                 mov             es,ax
2870                 mov             bx,[es:(RM_INT_API*4)]
2871                 or              cx,[es:(RM_INT_API*4)+2]
2872                 cmp             cx,0            ; if pointer is 0000:0000
2873                 jz              .v86_not_me
2874                 mov             eax,0xAABBAA55
2875                 int             RM_INT_API
2876                 cmp             eax,0xBBAABB33
2877                 jnz             .v86_not_me
2878 .v86_is_me:     mov             ax,cs
2879                 mov             ds,ax
2880                 mov             es,ax
2881                 mov             fs,ax
2882                 mov             gs,ax
2883                 mov             dx,str_removing_self
2884                 call            dos_puts
2885 ; instruct it to remove itself
2886                 mov             eax,0xAABBAABB
2887                 int             RM_INT_API
2888 ; exit, having done our job
2889                 mov             dx,str_crlf
2890                 call            dos_puts
2891                 mov             dx,str_unloaded
2892                 jmp             _exit_with_msg
2893 .v86_not_me:    mov             dx,str_v86_but_not_me
2894                 jmp             _exit_with_msg
2895
2896 ; ============= DATA: THESE EXIST IN THE .COM BINARY IMAGE
2897                 section         .data align=2
2898 himem_sys_buffer_size:dd        (256*1024)      ; DWORD [amount of extended memory to allocate]
2899 cr0_more:       dd              0x00000000      ; cache disable, not write through
2900 str_require_386:db              '386 or higher required$'
2901 str_removing_self:db            'Removing from memory',13,10,'$'
2902 str_v86_detected:db             'Virtual 8086 mode already active$'
2903 str_v86_but_not_me:db           'Virtual 8086 active, and its not me$'
2904 str_not_loaded: db              'Not resident in memory$'
2905 str_cannot_free_self:db         'Cannot free self from memory$'
2906 str_need_himem_sys:db           'HIMEM.SYS not installed$'
2907 str_himem_a20_error:db          'HIMEM.SYS failed to enable A20$'
2908 str_himem_alloc_err:db          'Unable to alloc extended memory$'
2909 str_himem_lock_err:db           'Unable to lock extended memory$'
2910 str_buffer_too_small:db         'Buffer too small$'
2911 str_buffer_too_large:db         'Buffer too large$'
2912 str_unloaded:   db              'Unloaded',13,10,'$'
2913 str_buffer_at:  db              'Buffer at: $'
2914 str_crlf:       db              13,10,'$'
2915 str_hex:        db              '0123456789ABCDEF'
2916 str_help:       db              'V86KERN [options]',13,10
2917                 db              'Demonstration Virtual 8086 kernel/monitor',13,10
2918                 db              13,10
2919                 db              'Options start with - or /',13,10
2920                 db              '  /?      Show this help',13,10
2921                 db              '  /B=...  Set buffer size (in KB)',13,10
2922                 db              '  /U      Unload the kernel',13,10
2923                 db              '  /I      Run with IOPL=3 (trap CLI/STI/etc)',13,10
2924                 db              '  /C      Disable memory cache',13,10
2925                 db              '  /W      Disable write-back cache',13,10
2926                 db              '$'
2927 str_unknown_switch:db           'Unknown switch $'
2928 str_needs_equals:db             'Switch missing =...$'
2929 str_eax:        db              'EAX$'
2930 str_ebx:        db              'EBX$'
2931 str_ecx:        db              'ECX$'
2932 str_edx:        db              'EDX$'
2933 str_esi:        db              'ESI$'
2934 str_edi:        db              'EDI$'
2935 str_ebp:        db              'EBP$'
2936 str_esp:        db              'ESP$'
2937 str_eip:        db              'EIP$'
2938 str_errcode:    db              'ERR$'
2939 str_eflags:     db              'FLG$'
2940 str_cr0:        db              'CR0$'
2941 str_cr3:        db              'CR3$'
2942 str_cr4:        db              'CR4$'
2943 str_opcode:     db              'OPC$'
2944 str_cs:         db              'CS$'
2945 str_ds:         db              'DS$'
2946 str_es:         db              'ES$'
2947 str_fs:         db              'FS$'
2948 str_gs:         db              'GS$'
2949 str_ss:         db              'SS$'
2950 str_mode_prot:  db              'Protected mode$'
2951 str_mode_v86:   db              'Virtual 8086 mode$'
2952 str_vm86_hello: db              'This text was printed by the Virtual 8086 mode component of this program',13,10,'$'
2953 str_fault_0x00: db              'Divide by Zero$'
2954 str_fault_0x01: db              'Debug$'
2955 str_fault_0x02: db              'NMI$'
2956 str_fault_0x03: db              'Breakpoint$'
2957 str_fault_0x04: db              'Overflow$'
2958 str_fault_0x05: db              'Boundary Check$'
2959 str_fault_0x06: db              'Invalid Opcode$'
2960 str_fault_0x07: db              'Coprocessor N/A$'
2961 str_fault_0x08: db              'Double Fault$'
2962 str_fault_0x09: db              'Coprocessor Segment Overrun$'
2963 str_fault_0x0A: db              'Invalid TSS$'
2964 str_fault_0x0B: db              'Segment Not Present$'
2965 str_fault_0x0C: db              'Stack Fault$'
2966 str_fault_0x0D: db              'General Protection Fault$'
2967 str_fault_0x0E: db              'Page Fault$'
2968 str_fault_0x0F: db              'Exception F$'
2969 str_fault_0x10: db              'FPU Error$'
2970 str_fault_0x11: db              'Alignment Check$'
2971 str_fault_0x12: db              'Machine Check$'
2972 str_fault_0x13: db              'SIMD/SSE Exception$'
2973 str_fault_unknown:db            'Unknown exception$'
2974 str_v86_unknown:db              'Unknown instruction in v86 mode$'
2975 str_v86_hlt_cli:db              'v86 halt with interrupts disabled$'
2976 str_v86_secret: db              'Inappropriate use of v86 secret handshake$'
2977 str_exit_to_dos:db              'Shutdown successful, exiting to DOS$'
2978 str_unknown_irq:db              'Unknown IRQ$'
2979 str_irq_deferred:db             'Deferred IRQ$'
2980 str_irq_1:      db              'IRQ #1$'
2981 str_stack_overflow:db           'Stack overflow$'
2982 str_himem_corruption:db         'Extended memory corruption$'
2983 str_unexpected_int_not_v86:db   'Unexpected: IRQ not from within v86$'
2984 str_unknown_emm_vcpi_call:db    'Unknown EMM/VCPI call$'
2985 str_unknown_emm_vcpi_pm_call:db 'Unknown VCPI entry point call$'
2986 str_vcpi_alloc_page_bug:db      'VCPI page allocation bug check$'
2987 str_vcpi_pm_not_supported:db    'VCPI protected mode not implemented$'
2988
2989 ; ============= EMM/VCPI entry point redirection and "signature"
2990                 align           16
2991 rm_int_67_entry:mov             ah,0x84         ; AH=0x84 means "not defined"
2992                 iret
2993                 nop
2994                 nop
2995                 nop
2996                 nop
2997                 nop
2998                 nop
2999                 nop
3000 rm_int_67_sig:  db              'EMMXXXX0',0    ; DOS programs look for this signature relative to INT 67h segment
3001
3002 ; ============= VARIABLES: THESE DO NOT EXIST IN THE .COM FILE THEY EXIST IN MEMORY FOLLOWING THE BINARY IMAGE
3003                 section         .bss align=2
3004                 align           8
3005 ; ---------------------- STACK
3006 stack_base:     resb            4096            ; char[4096+4]
3007 stack_init:     resd            1               ; DWORD
3008 stack_top:
3009 scratch_str:    resb            64              ; char[64]
3010 ; ---------------------- STACK
3011 stack_base_vm86:resb            4096            ; char[4096+4]
3012 stack_init_vm86:resd            2               ; DWORD
3013 stack_top_vm86:
3014 ; ---------------------- HIMEM.SYS state
3015 himem_sys_entry:resd            1               ; FAR POINTER
3016 himem_sys_buffer_phys:resd      1               ; DWORD [physical memory address]
3017 himem_sys_buffer_handle:resw    1               ; WORD [HIMEM.SYS handle]
3018 ; ---------------------- my real mode segment
3019 my_realmode_seg:resw            1               ; WORD
3020 my_phys_base:   resd            1               ; DWORD
3021 tss_phys_base:  resd            1               ; DWORD base logical address of TSS
3022 tss_vm86_phys_base:resd         1               ; DWORD base logical address of TSS
3023 v86_raw_opcode: resd            1               ; DWORD
3024 ; ---------------------- GDTR/IDTR
3025 gdtr_pmode:     resq            1               ; LIMIT. BASE
3026 gdtr_real:      resq            1               ; LIMIT, BASE
3027 idtr_pmode:     resq            1               ; LIMIT, BASE
3028 idtr_real:      resq            1               ; LIMIT, BASE
3029 ; ---------------------- STATE
3030 irq_pending:    resw            1
3031 user_req_unload:resb            1
3032 v86_if:         resb            1
3033 ; ---------------------- GLOBAL DESCRIPTOR TABLE
3034                 resd            1
3035                 align           8
3036 gdt:            resq            (MAX_SEL/8)     ; 16 GDT entries
3037 ; ---------------------- INTERRUPT DESCRIPTOR TABLE
3038                 align           8
3039 idt:            resq            256             ; all 256
3040 ; ---------------------- STATE
3041 user_req_iopl:  resb            1
3042 i_am_tsr:       resb            1
3043 unload_int_ret: resd            1
3044 unload_int_stk: resd            1
3045 ; ---------------------- VCPI allocation map and pages
3046 vcpi_alloc_bitmap_phys:resd             1
3047 vcpi_alloc_bitmap_size:resd             1
3048 vcpi_alloc_pages_phys:resd              1
3049 ; ---------------------- WHEN DISPLAYING THE UNHANDLED FAULT DIALOG
3050 unhandled_fault_var_errcode:resd        1
3051 unhandled_fault_var_eax:resd            1
3052 unhandled_fault_var_ebx:resd            1
3053 unhandled_fault_var_ecx:resd            1
3054 unhandled_fault_var_edx:resd            1
3055 unhandled_fault_var_esi:resd            1
3056 unhandled_fault_var_edi:resd            1
3057 unhandled_fault_var_ebp:resd            1
3058 unhandled_fault_var_esp:resd            1
3059 unhandled_fault_var_eip:resd            1
3060 unhandled_fault_var_eflags:resd         1
3061 unhandled_fault_var_cr0:resd            1
3062 unhandled_fault_var_cr3:resd            1
3063 unhandled_fault_var_cr4:resd            1
3064 unhandled_fault_var_cs:resw             1
3065 unhandled_fault_var_ds:resw             1
3066 unhandled_fault_var_es:resw             1
3067 unhandled_fault_var_fs:resw             1
3068 unhandled_fault_var_gs:resw             1
3069 unhandled_fault_var_ss:resw             1
3070 unhandled_fault_var_opcode:resd         1
3071 ; ---------------------- TSS
3072 tss_main:       resb            128
3073 ; ---------------------- VM86 TSS
3074 tss_vm86:       resb            8192+128
3075 ; ---------------------------------------------------------------------
3076 ;                       END POINTER
3077 ; ---------------------------------------------------------------------
3078 padding:        resq            2               ; SAFETY PADDING
3079 the_end: