3 ; Test program: Proof-of-concept minimalist virtual 8086 "monitor"
4 ; (C) 2010-2012 Jonathan Campbell.
5 ; Hackipedia DOS library.
7 ; This code is licensed under the LGPL.
8 ; <insert LGPL legal text here>
11 ; MODE: 16-bit real mode MS-DOS .COM executable
12 ; Assumes DS == ES == SS
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
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.
27 ; - Privileged instructions like mov cr0,<reg> trigger an exception and this program makes no
28 ; attempt to emulate those instructions.
30 ; - Whatever the BIOS does in response to CTRL+ALT+DEL it doesn't work well when we are active.
32 ; - Incomplete VCPI implementation and EMM emulation with no pages to alloc
34 ; - Paging is not enabled
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].
43 ; Standard selectors in protected mode
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)
53 TSS_VM86_SEL equ (9 << 3)
56 ; We reprogram the PIC to direct IRQ 0-15 to this base interrupt
60 ; Extensible virtual 8086 mode kernel for DOS
61 ; (C) 2011 Jonathan Campbell
68 ; ============= ENTRY POINT
70 mov word [my_realmode_seg],ax
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
85 ; ============= CPU DETECT
96 ; 286 test: EFLAGS will always have bits 12-15 clear
107 cpu_is_386_not: mov ax,1
111 ; ============= EXIT WITH MESSAGE ($-terminated string at DS:DX)
112 _exit_with_msg: mov ah,9
113 int 21h ; fall through to _exit
117 cmp word [himem_sys_buffer_handle],0 ; if there is a handle to free, then do it
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
128 ; ============= PROGRAM STARTS HERE
129 _entry: call parse_argv
133 .argv_ok: call cpu_is_386 ; CHECK: 386 or higher
135 mov dx,str_require_386
137 .is386: cmp byte [user_req_unload],0; CHECK: Did user request that we unload?
139 jmp unload_this_program
140 .not_unload: smsw ax ; CHECK: Virtual 8086 mode not already enabled
143 mov dx,str_v86_detected
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
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
154 .buffer_size_small_enough:
155 mov ax,4300h ; CHECK: HIMEM.SYS is present
160 .yes_himem_sys: mov ax,4310h ; Get HIMEM.SYS entry point (cannot fail)
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]
168 mov dx,str_himem_a20_error
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]
174 shr edx,10 ; EDX = (X BYTES+1023)/1024 KB
175 call far word [himem_sys_entry]
178 mov dx,str_himem_alloc_err
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)
185 mov dx,str_himem_lock_err
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
196 mov [tss_phys_base],eax ; = 104 bytes
202 mov [tss_vm86_phys_base],eax ; = 8192+104 bytes
204 ; PRINT "BUFFER AT: " + *((DWORD*)himem_sys_buffer_phys) + "\n"
208 mov eax,[himem_sys_buffer_phys]
210 call eax_to_hex_16_dos
215 mov eax,[himem_sys_buffer_phys]
216 add eax,[himem_sys_buffer_size]
218 mov byte [scratch_str],'-'
220 call eax_to_hex_16_dos
231 mov dword [my_phys_base],eax
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
242 add eax,4096 ; assume bitmap size of 4K, enough for 4MB of pages
243 mov dword [vcpi_alloc_pages_phys],eax
245 mov ebx,dword [himem_sys_buffer_size]
247 shr ebx,12+3 ; EBX = size of bitmap (up to 4K)
248 mov dword [vcpi_alloc_bitmap_size],ebx
251 ; clear the IDT and GDT
259 ; prepare the IDTR and GDTR.
260 ; real mode versions: limit=0xFFFF base=0
263 mov word [idtr_real],ax
264 mov word [gdtr_real],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]
273 mov dword [gdtr_pmode+2],eax
274 mov eax,[my_phys_base]
276 mov dword [idtr_pmode+2],eax
280 lea di,[gdt+CODE16_SEL]
281 ; Code selector (CODE_16SEL)
282 dec ax ; 0x0000 - 1 = 0xFFFF
284 mov ax,[my_phys_base]
286 mov al,[my_phys_base+2]
288 stosw ; BASE[23:16] access byte=executable readable
290 mov ah,[my_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
292 ; Data selector (DATA16_SEL)
296 mov ax,[my_phys_base]
298 mov al,[my_phys_base+2]
300 stosw ; BASE[23:16] access byte=data writeable
302 mov ah,[my_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
304 ; Code selector (CODE_32SEL)
305 dec ax ; 0x0000 - 1 = 0xFFFF
307 mov ax,[my_phys_base]
309 mov al,[my_phys_base+2]
311 stosw ; BASE[23:16] access byte=executable readable
313 mov ah,[my_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
315 ; Data selector (DATA32_SEL)
319 mov ax,[my_phys_base]
321 mov al,[my_phys_base+2]
323 stosw ; BASE[23:16] access byte=data writeable
325 mov ah,[my_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
327 ; Data selector (FLAT16_SEL)
334 stosw ; BASE[23:16] access byte=data writeable
338 ; Data selector (FLAT32_SEL)
345 stosw ; BASE[23:16] access byte=data writeable
349 ; LDT selector (LDT_SEL)
350 mov ax,7 ; I have no use for the LDT
352 mov ax,[my_phys_base]
354 mov al,[my_phys_base+2]
356 stosw ; BASE[23:16] access byte=data writeable LDT type 2
358 mov ah,[my_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
360 ; TSS selector (TSS_SEL)
363 mov ax,[tss_phys_base]
365 mov al,[tss_phys_base+2]
367 stosw ; BASE[23:16] access byte=data writeable non-busy TSS type 9
369 mov ah,[tss_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
371 ; TSS selector (TSS_VM86_SEL)
374 mov ax,[tss_vm86_phys_base]
376 mov al,[tss_vm86_phys_base+2]
378 stosw ; BASE[23:16] access byte=data writeable non-busy TSS type 9
380 mov ah,[tss_vm86_phys_base+3] ; LIMIT[19:16] flags=0 BASE[31:24]
383 ; prepare the CPU registers
387 ; enter protected mode
391 jmp CODE16_SEL:pmode16_entry
392 pmode16_entry: mov ax,DATA16_SEL
408 ; now enter 32-bit protected mode
409 jmp CODE32_SEL:pmode32_entry
411 pmode32_entry: mov ax,DATA32_SEL
419 ; at this point: we are in 32-bit protected mode!
421 ; ============= setup the TSS representing our task (for when we return)
423 mov edi,[tss_phys_base]
424 sub edi,[my_phys_base]
426 xor eax,eax ; TSS+0x00 = no backlink
428 mov eax,stack_init ; TSS+0x04 = ESP for CPL0
430 mov eax,DATA32_SEL ; TSS+0x08 = SS for CPL0
432 mov eax,stack_init ; TSS+0x0C = ESP for CPL1
434 mov eax,DATA32_SEL ; TSS+0x10 = SS for CPL1
436 mov eax,stack_init ; TSS+0x14 = ESP for CPL2
438 mov eax,DATA32_SEL ; TSS+0x18 = SS for CPL2
440 xor eax,eax ; TSS+0x1C = CR3
442 mov eax,0 ; TSS+0x20 = EIP [FIXME]
444 mov eax,0x00000002 ; TSS+0x24 = EFLAGS VM=0
446 xor eax,eax ; TSS+0x28 = EAX
448 xor eax,eax ; TSS+0x2C = ECX
450 xor eax,eax ; TSS+0x30 = EDX
452 xor eax,eax ; TSS+0x34 = EBX
454 mov eax,stack_init ; TSS+0x38 = ESP
456 xor eax,eax ; TSS+0x3C = EBP
458 xor eax,eax ; TSS+0x40 = ESI
460 xor eax,eax ; TSS+0x44 = EDI
462 mov ax,DATA32_SEL ; TSS+0x48 = ES
464 mov ax,CODE32_SEL ; TSS+0x4C = CS
466 mov ax,DATA32_SEL ; TSS+0x50 = SS
468 mov ax,DATA32_SEL ; TSS+0x54 = DS
470 mov ax,DATA32_SEL ; TSS+0x58 = FS
472 mov ax,DATA32_SEL ; TSS+0x5C = GS
474 xor eax,eax ; TSS+0x60 = LDTR
476 mov eax,(104 << 16) ; TSS+0x64 = I/O map base
479 ; ============= setup the TSS representing the virtual 8086 mode task
481 mov edi,[tss_vm86_phys_base]
482 sub edi,[my_phys_base]
484 xor eax,eax ; TSS+0x00 = no backlink
486 mov eax,stack_init ; TSS+0x04 = ESP for CPL0
488 mov eax,DATA32_SEL ; TSS+0x08 = SS for CPL0
490 mov eax,stack_init ; TSS+0x0C = ESP for CPL1
492 mov eax,DATA32_SEL ; TSS+0x10 = SS for CPL1
494 mov eax,stack_init ; TSS+0x14 = ESP for CPL2
496 mov eax,DATA32_SEL ; TSS+0x18 = SS for CPL2
498 xor eax,eax ; TSS+0x1C = CR3
500 mov eax,vm86_entry ; TSS+0x20 = EIP
502 mov eax,0x00020202 ; TSS+0x24 = EFLAGS VM=1 IOPL=N IF=1
503 movzx ebx,byte [user_req_iopl]
506 or eax,ebx ; EFLAGS |= user_req_iopl << 12
508 xor eax,eax ; TSS+0x28 = EAX
510 xor eax,eax ; TSS+0x2C = ECX
512 xor eax,eax ; TSS+0x30 = EDX
514 xor eax,eax ; TSS+0x34 = EBX
516 mov eax,stack_init_vm86 ; TSS+0x38 = ESP
518 xor eax,eax ; TSS+0x3C = EBP
520 xor eax,eax ; TSS+0x40 = ESI
522 xor eax,eax ; TSS+0x44 = EDI
524 mov ax,[my_realmode_seg] ; TSS+0x48 = ES
526 mov ax,[my_realmode_seg] ; TSS+0x4C = CS
528 mov ax,[my_realmode_seg] ; TSS+0x50 = SS
530 mov ax,[my_realmode_seg] ; TSS+0x54 = DS
532 mov ax,[my_realmode_seg] ; TSS+0x58 = FS
534 mov ax,[my_realmode_seg] ; TSS+0x5C = GS
536 xor eax,eax ; TSS+0x60 = LDTR
538 mov eax,(104 << 16) ; TSS+0x64 = I/O map base (0x68 == 104)
541 mov ecx,8192 >> 2 ; TSS+0x68 = I/O permission map (pre-set to all open)
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
552 mov eax,dword [vcpi_alloc_bitmap_phys]
560 mov edi,dword [vcpi_alloc_bitmap_phys]
561 mov ecx,dword [vcpi_alloc_bitmap_size]
571 mov esi,interrupt_procs
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
585 mov ax,0x8E00 ; DPL=0 (so that software interrupts 0x00-0x1F can be handled by our GPF)
596 mov ax,0xEE00 ; DPL=3
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
605 mov al,IRQ_BASE_INT ; ICW2 A0=1
607 mov al,0x04 ; ICW3 A0=1 slave on IRQ 2
610 mov al,0x10 ; ICW1 A0=0
612 mov al,IRQ_BASE_INT+8 ; ICW2 A0=1
614 mov al,0x02 ; ICW3 A0=1
617 ; jump into virtual 8086 mode
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
626 jmp interrupt_routine
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
635 jmp interrupt_routine_irq
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
644 jmp interrupt_routine_tf
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
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
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
663 jmp interrupt_routine_emm
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
923 interrupt_procs:dw interrupt_0x00
1187 interrupt_0x00_strs:
1210 %macro pm_int_entry 0
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
1221 %macro pm_int_exit 0
1228 ; this must reside here. Any further down the jmps in the table will be out of range.
1230 ; (EFLAGS & VM) = Happend in v86 mode?
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
1246 add esp,8 ; drop error code + interrupt num
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]
1277 irq_rm_map: db 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
1278 db 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77
1280 ; return: EAX = physical memory address of vm86 SS:SP pointer
1281 vm86_ss_esp_to_phys:
1290 ; return: EAX = physical memory address of vm86 CS:IP pointer
1291 vm86_cs_eip_to_phys:
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:
1307 sub int_v86_ESP,6 ; IRET stack frame
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
1313 mov word [es:eax+2],bx
1315 mov word [es:eax+4],bx
1317 mov int_EIP_w,cx ; load new CS:IP from interrupt vector
1324 call interrupt_routine_halt_prepare
1326 mov esi,str_stack_overflow
1327 jmp fault_jmp_unhandled
1329 interrupt_routine_tf: ; INT 0x01 Trap Interrupt
1331 test int_EFLAGS,0x20000 ; did this happen from v86 mode?
1333 mov eax,1 ; reflect INT 0x01 to vm86
1334 call vm86_push_interrupt
1335 and int_EFLAGS,~0x100 ; clear the trap flag
1337 .not_vm86: call interrupt_routine_halt_prepare
1339 mov esi,str_unexpected_int_not_v86
1340 jmp fault_jmp_unhandled
1342 interrupt_routine_irq:
1344 test int_EFLAGS,0x20000 ; did this happen from v86 mode?
1347 sub eax,IRQ_BASE_INT
1348 movzx eax,byte [eax+irq_rm_map]
1349 call vm86_push_interrupt
1351 .not_vm86: call interrupt_routine_halt_prepare
1353 mov esi,str_unexpected_int_not_v86
1354 jmp fault_jmp_unhandled
1356 vm86_stack_overflow:
1357 call interrupt_routine_halt_prepare
1359 mov esi,str_stack_overflow
1360 jmp fault_jmp_unhandled
1362 ; General Protection Fault
1363 interrupt_routine_gpf:
1367 test int_EFLAGS,0x20000 ; did this happen from v86 mode?
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
1424 ; well then I don't know
1425 call interrupt_routine_halt_prepare
1427 mov esi,str_v86_unknown
1428 jmp fault_jmp_unhandled
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
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
1444 .vm86_in_al_dx: inc int_EIP ; EIP += 1
1445 mov edx,int_EDX ; edx = port number
1447 mov ecx,1 ; ecx = size of I/O
1448 call on_vm86_io_read
1449 mov int_AL,al ; eax = byte value
1452 .vm86_in_ax_dx: inc int_EIP ; EIP += 1
1453 mov edx,int_EDX ; edx = port number
1455 mov ecx,2 ; ecx = size of I/O
1456 call on_vm86_io_read
1457 mov int_AX,ax ; eax = byte value
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
1467 .vm86_out_imm_ax:add int_EIP,2 ; EIP += 2
1468 movzx edx,ah ; I/O port number in second byte
1471 mov ecx,2 ; ecx = size of I/O
1472 call on_vm86_io_write
1475 .vm86_out_dx_al:inc int_EIP ; EIP += 1
1476 mov edx,int_EDX ; edx = port number
1478 movzx eax,int_AL ; eax = byte value
1479 mov ecx,1 ; ecx = size of I/O
1480 call on_vm86_io_write
1483 .vm86_out_dx_ax:inc int_EIP ; EIP += 1
1484 mov edx,int_EDX ; edx = port number
1488 mov ecx,2 ; ecx = size of I/O
1489 call on_vm86_io_write
1492 .vm86_pushfd: push es
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
1504 .vm86_popfd: push es
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
1517 .vm86_pushf: push es
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
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
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
1548 mov bx,[es:eax+0] ; EBX = IP
1550 mov bx,[es:eax+2] ; EBX = CS
1552 mov bx,[es:eax+4] ; EBX = FLAGS
1557 .vm86_cli: and int_EFLAGS,~0x200 ; IF=0
1561 .vm86_sti: or int_EFLAGS,0x200 ; IF=1
1565 .vm86_int3: inc int_EIP ; EIP++
1567 call vm86_push_interrupt
1570 .vm86_int_n: movzx eax,ah ; AL=CD AH=intnum
1571 add int_EIP,2 ; EIP += 2
1572 call vm86_push_interrupt
1575 .not_vm86: call interrupt_routine_halt_prepare
1576 call edx_esi_default_int_str
1577 jmp fault_jmp_unhandled
1583 cmp int_INTNUM,RM_INT_API ; interrupts sent to our RM_INT_API are handled directly
1585 test int_EFLAGS,0x20000 ; did this happen from v86 mode?
1588 ; okay then, there are several reasons such interrupts fire.
1590 ; INT 15H interception
1593 ; Anything int >= 0x14 on
1596 ; INT 0x03 debug interrupt FIXME is this needed?
1599 ; INT 0x01 debug trap FIXME is this needed?
1602 ; INT 0x07 COPROCESSOR NOT PRESENT (vm86 task used a floating point instruction)
1606 call interrupt_routine_halt_prepare
1607 call edx_esi_default_int_str
1608 jmp fault_jmp_unhandled
1610 ; COPROCESSOR NOT PRESENT. USUALLY SIGNALLED AFTER TASK SWITCH ON FIRST FPU INSTRUCTION.
1614 .vm86_int1: mov eax,1
1615 call vm86_push_interrupt
1616 and int_EFLAGS,~0x100 ; clear trap flag
1619 .vm86_int3: mov eax,3
1620 call vm86_push_interrupt
1623 .vm86_intN: mov eax,int_INTNUM
1624 call vm86_push_interrupt
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
1631 ; carry it forward to the BIOS
1633 call vm86_push_interrupt
1637 .vm86_int15_87: xor eax,eax
1644 ; ECX = count of WORDs, EAX = physical memory addr of GDT
1645 mov esi,[es:eax+0x10+2]
1647 mov edi,[es:eax+0x18+2]
1649 ; ESI = physical memory addr (src), EDI = physical memory addr (dst)
1650 movzx ebx,byte [es:eax+0x10+7]
1653 movzx ebx,byte [es:eax+0x18+7]
1656 ; carry out the actual copy
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
1677 call interrupt_routine_halt_prepare
1678 call edx_esi_default_int_str
1679 jmp fault_jmp_unhandled
1682 mov [unload_int_stk+0],ax
1684 mov [unload_int_stk+2],ax
1687 mov [unload_int_ret+0],ax
1689 mov [unload_int_ret+2],ax
1697 jmp CODE32_SEL:_exit_from_prot32
1699 mov int_EAX,0xBBAABB33
1701 edx_esi_default_int_str:
1707 mov si,word [(edx*2)+interrupt_0x00_strs]
1710 interrupt_routine_halt_prepare: ; <====== SET ESI = address of string to print. Trashes EAX.
1712 mov dword [unhandled_fault_var_opcode],eax
1714 mov dword [unhandled_fault_var_eax],eax
1716 mov dword [unhandled_fault_var_ebx],eax
1718 mov dword [unhandled_fault_var_ecx],eax
1720 mov dword [unhandled_fault_var_edx],eax
1722 mov dword [unhandled_fault_var_esi],eax
1724 mov dword [unhandled_fault_var_edi],eax
1726 mov dword [unhandled_fault_var_ebp],eax
1728 mov dword [unhandled_fault_var_esp],eax
1730 mov dword [unhandled_fault_var_eip],eax
1732 mov dword [unhandled_fault_var_errcode],eax
1735 mov word [unhandled_fault_var_es],ax
1737 mov word [unhandled_fault_var_ds],ax
1739 mov word [unhandled_fault_var_cs],ax
1741 mov word [unhandled_fault_var_fs],ax
1743 mov word [unhandled_fault_var_gs],ax
1745 mov word [unhandled_fault_var_ss],ax
1747 mov dword [unhandled_fault_var_cr0],eax
1749 mov dword [unhandled_fault_var_cr3],eax
1751 mov dword [unhandled_fault_var_cr4],eax
1753 mov dword [unhandled_fault_var_eflags],eax
1754 test eax,0x20000 ; was the VM flag set in EFLAGS when it happened?
1756 test int_CS,3 ; did we come from ring 1/2/3?
1758 ; ESP needs to reflect it's contents prior to jumping to ring 0
1760 mov dword [unhandled_fault_var_esp],eax
1762 ; the segment registers need to reflect what they were in vm86 mode
1763 .v86_mode: xor eax,eax
1765 mov word [unhandled_fault_var_es],ax
1767 mov word [unhandled_fault_var_ds],ax
1769 mov word [unhandled_fault_var_fs],ax
1771 mov word [unhandled_fault_var_gs],ax
1773 mov word [unhandled_fault_var_ss],ax
1775 mov dword [unhandled_fault_var_esp],eax
1776 ; we also might want to know what opcodes were involved
1779 call vm86_cs_eip_to_phys
1781 mov dword [unhandled_fault_var_opcode],ebx
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:
1800 .pass: jmp emm_unhandled
1801 ; VCPI processing (AH=0xDE)
1805 jmp word [vcpi_functions+(eax*2)]
1806 ; EMM processing (AH=0x40...0x5D)
1809 jmp word [emm_functions+(eax*2)]
1813 call interrupt_routine_halt_prepare
1815 mov esi,str_unknown_emm_vcpi_call
1816 jmp fault_jmp_unhandled
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
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
1865 ; 8042 trapping state
1866 trap_8042_mode db 0x00
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.
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
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
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
1893 mov byte [trap_8042_mode],0x00
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
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
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
1916 .unknown_port: xor eax,eax ; return 0xFF
1919 .read_8042_controller: in al,0x64 ; let it through
1922 .read_port_A_92h: movzx eax,byte [fake_port_92] ; return last written value
1925 ; VCPI protected mode entry (32-bit)
1926 vcpi_pm_entry: pm_int_entry
1927 call interrupt_routine_halt_prepare
1929 mov esi,str_unknown_emm_vcpi_pm_call
1930 jmp fault_jmp_unhandled
1933 ; AX=0xDE00 VCPI detect
1934 vcpi00_detect: mov int_EAX,0 ; present
1935 mov int_EBX,0x0100 ; version v1.0
1938 ; AX=0xDE01 VCPI get protected mode interface
1939 vcpi01_get_pm_if: mov ax,FLAT32_SEL
1941 ; fill page table ES:DI and advance DI += 4096 on return to client
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
1951 .pmfill: lea eax,[ebx+0x007] ; EAX = PTE: base address + present + read/write, user
1956 ; then fill in the GDT table given by the client
1961 add edi,int_ESI ; EDI = (DS<<4)+SI
1963 lea esi,[gdt+(CODE32_SEL&0xFFF8)] ; ESI = our code32 GDT
1964 lodsd ; ****COPY****
1968 ; DATA (not that it matters)
1969 lea esi,[gdt+(DATA32_SEL&0xFFF8)] ; ESI = our data32 GDT
1970 lodsd ; ****COPY****
1974 ; DATA (not that it matters)
1975 lea esi,[gdt+(FLAT32_SEL&0xFFF8)] ; ESI = our data32 GDT
1976 lodsd ; ****COPY****
1981 mov int_EAX,0 ; OK, no error
1982 mov int_EBX,vcpi_pm_entry ; our VCPI entry point
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
1990 ; AX=0xDE04 VCPI alloc page:
1991 vcpi04_alloc_page: mov ax,FLAT32_SEL
1993 mov esi,[vcpi_alloc_bitmap_phys]
1994 mov ecx,[vcpi_alloc_bitmap_size]
1996 .scan: mov al,byte [es:esi]
2002 .not_found: mov int_EAX,0x8800 ; not found
2008 bsf bx,ax ; BX=# of bit that is zero
2009 jz .bmp_error ; ZF=0 or else
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 #
2021 .bmp_error: call interrupt_routine_halt_prepare
2023 mov esi,str_vcpi_alloc_page_bug
2024 jmp fault_jmp_unhandled
2026 ; AX=0xDE05 VCPI alloc page:
2027 vcpi05_free_page: mov ax,FLAT32_SEL
2031 sub edx,[vcpi_alloc_pages_phys]
2036 cmp esi,[vcpi_alloc_bitmap_size]
2043 add esi,[vcpi_alloc_bitmap_phys]
2044 int 3 ; <- FIXME: Remove debug b.p. when you verify this works
2047 .not_found: mov int_EAX,0x8A00
2048 mov eax,0xABCD1234 ; <- DEBUG: remove when you verify this works
2052 ; AX=0xDE06 VCPI get physical address of 4K page
2053 vcpi06_get_phys_addr: mov eax,int_ECX
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
2066 ; AX=0xDE0C VCPI enter protected mode
2067 vcpi0C_enter_pm: call interrupt_routine_halt_prepare
2069 mov esi,str_vcpi_pm_not_supported
2070 jmp fault_jmp_unhandled
2072 ; AH=0x40 Get Status
2073 emm40_get_status: mov int_EAX,0 ; we're OK
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"
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
2085 emm43_alloc_pages: mov int_EAX,0x8700 ; not enough memory
2086 mov int_EDX,0 ; handle=0
2089 emm44_map_handle_page: mov int_EAX,0x8A00 ; out of range
2092 emm45_dealloc_pages: mov int_EAX,0 ; uhhhh... OK
2095 emm46_get_version: mov int_EAX,0x40 ; version 4.0
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
2114 emm58_combo: mov eax,int_EAX
2118 .func_5800: ; ========================== INT 67h AX=5800h
2120 mov int_ECX,0 ; the returned array is zero entries in length
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
2130 %unmacro int_entry 0
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
2162 .thunk16: mov ax,DATA16_SEL
2167 jmp unhandled_fault_errcode
2170 ; ============= cleanup, exit to DOS (from 32-bit protected mode)
2172 jmp CODE16_SEL:.entry16
2174 .entry16: mov ax,DATA16_SEL
2182 ; ============= cleanup, exit to DOS (from 16-bit protected mode)
2191 ; overwrite the far jmp's segment value
2192 mov ax,[my_realmode_seg]
2193 mov word [.real_hackme+3],ax
2202 .real_hackme: jmp 0:.real_entry
2203 .real_entry: mov ax,[cs:my_realmode_seg]
2211 ; reprogram the PIC back to what normal DOS expects: IRQ 0-7 => INT 8-15
2212 mov al,0x10 ; ICW1 A0=0
2214 mov al,0x08 ; ICW2 A0=1
2216 mov al,0x04 ; ICW3 A0=1 slave on IRQ 2
2219 mov al,0x10 ; ICW1 A0=0
2221 mov al,0x70 ; ICW2 A0=1
2223 mov al,0x02 ; ICW3 A0=1
2226 ; remove our INT 66h API
2229 mov word [es:(RM_INT_API*4)],ax
2230 mov word [es:(RM_INT_API*4)+2],ax
2232 ; free HIMEM.SYS blocks
2233 test word [himem_sys_buffer_handle],0
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]
2243 ; if we already exited as a TSR...
2244 test byte [i_am_tsr],1
2247 ; time to exit to DOS
2248 mov dx,str_exit_to_dos
2251 ; ============= ALTERNATE EXIT IF WE ALREADY EXITED AS TSR
2254 mov es,ax ; ES = our code segment which is also our PSP segment
2255 mov ah,0x49 ; function 49h free memory block
2258 jnc .tsr_exit_free_ok
2259 mov dx,str_cannot_free_self
2263 mov ax,[cs:unload_int_stk+0] ; offset
2264 add ax,6 ; discard prior frame
2266 mov ax,[cs:unload_int_stk+2] ; segment
2269 mov dx,str_exit_to_dos
2272 cmp word [himem_sys_buffer_handle],0 ; if there is a handle to free, then do it
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]
2311 jmp far word [cs:unload_int_ret]
2313 ; ============= UNHANDLED FAULT HANDLER (16-bit code)
2314 ; input: EDX = Number of interrupt
2315 ; SI = Textual string of fault
2317 unhandled_fault_errcode:
2322 mov ax,[cs:my_realmode_seg]
2323 mov word [.real16jmp+3],ax
2333 ; crash-thunk to real mode
2336 .real16jmp: jmp 0:.real16
2337 .real16: mov ax,[cs:my_realmode_seg]
2352 ; print exception name on screen
2354 call .unhandled_print
2355 mov al,' ' ; +space plus AH still contains upper byte from .unhandled_print
2358 ; then the number (in EDX) write to DS:DI
2362 call eax_to_hex_16_dos
2363 lea si,[di+6] ; only the last two hex digits
2365 call .unhandled_print
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
2374 jz .regprint32e ; AX=0 STOP
2376 jz .regprint32nl ; AX=1 GO TO NEW LINE
2378 mov si,ax ; SI=AX=address of variable name
2380 call .unhandled_print
2382 mov ax,0x4E00 | (':')
2384 lodsw ; SI=address of variable
2390 call eax_to_hex_16_dos
2393 call .unhandled_print
2395 mov ax,0x4E00 | (' ')
2398 .regprint32nl: pop edi
2399 add edi,160 ; move to next line, save back to [ESP]
2402 .regprint32e: pop edi
2404 add edi,160 ; next line...
2409 jz .regprint16e ; AX=0 STOP
2411 jz .regprint16nl ; AX=1 GO TO NEW LINE
2413 mov si,ax ; SI=AX=address of variable name
2415 call .unhandled_print
2417 mov ax,0x4E00 | (':')
2419 lodsw ; SI=address of variable
2426 call eax_to_hex_16_dos
2429 call .unhandled_print
2431 mov ax,0x4E00 | (' ')
2434 .regprint16nl: pop edi
2435 add edi,160 ; move to next line, save back to [ESP]
2438 .regprint16e: mov si,str_mode_prot ; CPU mode
2439 test dword [unhandled_fault_var_eflags],0x20000
2440 jz .regprint_cpu_mode_not_v86
2442 .regprint_cpu_mode_not_v86:
2443 call .unhandled_print
2468 ; ===== print on screen from DS:SI to ES:EDI
2472 jz .unhandled_printe
2475 jmp .unhandled_print
2479 ; ============= Entry point (virtual 8086 mode)
2480 vm86_entry: mov ax,ds ; *DEBUG*
2483 cli ; make sure the v86 monitor handles CLI
2485 pushf ; ...and PUSHF
2487 pushfd ; ...32-bit PUSHF
2488 popfd ; ...32-bit POPF
2490 out 21h,al ; ...OUT?
2492 ; can I clear vm86 mode by clearing the VM bit?
2499 ; NOW MAKE SURE PUSHF/POPF STORE THE VALUE ON-STACK LIKE THEY'RE SUPPOSED TO
2501 mov word [ss:bx-2],0x5A5A
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
2525 mov dword [ss:bx-4],0x5A5A5A5A
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
2547 ; IF I CLEAR INTERRUPT (CLI) AND THEN EXECUTE AN INTERRUPT, DOES IT COME BACK ENABLED?
2549 mov ah,0x0F ; INT 10 AH=0x0F which has no visisible effect
2554 jz .int_doesnt_enable
2560 mov si,str_vm86_hello
2563 ; TEST DEFERRED IRQ MECHANISM BY DELIBERATLEY HALTING FOR AWHILE
2565 mov ecx,0x1000000 ; delibrate slow countdown loop
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
2578 .env_free_ok: mov word [cs:0x2C],0 ; rub out the ENV block
2580 ; setup our INT 66h API
2583 mov word [es:(RM_INT_API*4)],realmode_api_entry
2585 mov word [es:(RM_INT_API*4)+2],ax
2587 ; setup our INT 67h vector
2588 ; REMINDER: On exit you need to restore this vector
2590 mov bx,rm_int_67_entry
2593 mov word [es:(0x67*4)+2],ax
2595 mov word [es:(0x67*4)+0],ax
2597 ; finally, terminate and stay resident
2598 mov byte [i_am_tsr],1
2599 mov edx,the_end ; DX = memory in paragraphs to save
2602 add edx,0x11 ; <-- FIXME: IS THIS NECESSARY?
2603 mov ah,0x31 ; function 31h terminate and stay resident
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
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
2617 or ax,0x4E30 ; AX = VGA alphanumeric code for that number
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
2627 ; ============= Parse command line (from PSP segment)
2641 ; FALL THROUGH WITH ZF=0 to return
2643 ; AT THIS POINT: SI = just after the / or - in the switch
2651 ; the A-Z switches are allowed to have "=NNNN" after them where N is some integer in hex or decimal
2654 xor bh,bh ; BX = index into lookup table
2656 jmp word [bx+.switch_az]
2658 .help: or al,al ; AL != 0 => ZF=0
2660 .unknown_switch:mov dx,str_unknown_switch
2662 lea dx,[si-2] ; step back two chars
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:
2673 jnz .switch_needs_equ_check_fail
2679 .switch_needs_equ_check_fail:
2680 add sp,2 ; fall through
2682 mov dx,str_needs_equals
2685 ; ========== /B=<number>
2686 .switch_buffer_size:
2687 call .switch_needs_equ_check
2689 mov [himem_sys_buffer_size],eax
2692 .switch_unload: mov byte [user_req_unload],1
2695 .switch_iopl: mov byte [user_req_iopl],0
2698 .switch_cache_disable:
2699 or dword [cr0_more],0x40000000
2702 .switch_writeback_disable:
2703 or dword [cr0_more],0x20000000
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
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
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
2748 dw str_cr3, unhandled_fault_var_cr3
2749 dw str_cr4, unhandled_fault_var_cr4
2750 dw str_opcode, unhandled_fault_var_opcode
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
2760 ; ============= bios_puts (print $-terminated string at DS:SI)
2776 ; ============= dos_puts (print $-terminated string at DS:DX)
2777 dos_puts: mov ah,09h
2781 ; ============= read one digit from DS:SI return in AX (16-bit code)
2782 ax_strtol_16_single:mov al,[si]
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
2798 call ax_strtol_16_single
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
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],'$'
2813 al_to_hex_16_nul:mov byte [di+2],0
2814 al_to_hex_16: push di
2821 mov al,[bx+str_hex] ; AL' = str_hex[al]
2824 mov ah,[bx+str_hex] ; AH' = str_hex[ah]
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],'$'
2835 ax_to_hex_16_nul:mov byte [di+4],0
2836 ax_to_hex_16: push di
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],'$'
2849 eax_to_hex_16_nul:mov byte [di+8],0
2850 eax_to_hex_16: push di
2860 ; ============= /U Unloading the resident copy of this program
2861 unload_this_program:
2865 mov dx,str_not_loaded
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
2878 .v86_is_me: mov ax,cs
2883 mov dx,str_removing_self
2885 ; instruct it to remove itself
2888 ; exit, having done our job
2893 .v86_not_me: mov dx,str_v86_but_not_me
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
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
2927 str_unknown_switch:db 'Unknown switch $'
2928 str_needs_equals:db 'Switch missing =...$'
2938 str_errcode: db 'ERR$'
2939 str_eflags: db 'FLG$'
2943 str_opcode: db 'OPC$'
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$'
2989 ; ============= EMM/VCPI entry point redirection and "signature"
2991 rm_int_67_entry:mov ah,0x84 ; AH=0x84 means "not defined"
3000 rm_int_67_sig: db 'EMMXXXX0',0 ; DOS programs look for this signature relative to INT 67h segment
3002 ; ============= VARIABLES: THESE DO NOT EXIST IN THE .COM FILE THEY EXIST IN MEMORY FOLLOWING THE BINARY IMAGE
3003 section .bss align=2
3005 ; ---------------------- STACK
3006 stack_base: resb 4096 ; char[4096+4]
3007 stack_init: resd 1 ; DWORD
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
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
3031 user_req_unload:resb 1
3033 ; ---------------------- GLOBAL DESCRIPTOR TABLE
3036 gdt: resq (MAX_SEL/8) ; 16 GDT entries
3037 ; ---------------------- INTERRUPT DESCRIPTOR TABLE
3039 idt: resq 256 ; all 256
3040 ; ---------------------- STATE
3041 user_req_iopl: 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
3073 ; ---------------------- VM86 TSS
3074 tss_vm86: resb 8192+128
3075 ; ---------------------------------------------------------------------
3077 ; ---------------------------------------------------------------------
3078 padding: resq 2 ; SAFETY PADDING