Virtual-8086 mode monitor
Chris Giese	<geezer@execpc.com>	http://my.execpc.com/~geezer
Release date: Oct 12, 2004
This code is public domain (no copyright).
You can do whatever you want with it.

================================================================
Build
================================================================
Pre-built vm86.com and boot/fat12.bin files are supplied if you
don't have the software tools listed here. See 'Running', below.

To build this code, you need DJGPP and NASM:
	http://www.delorie.com/djgpp
	http://nasm.sourceforge.net/

DJGPP is a DOS program so you need a DOS environment. MS-DOS,
FreeDOS, or a Windows DOS box will all work for _building_ the
program, but you will be unable to _run_ it from a Windows DOS
box (see below).

Versions of x86 GCC other than DJGPP may work if the linker
supports an object file format that
- Is supported by NASM
- Supports mixed 16- and 32-bit code in the same file

For DJGPP, I am using the a.out-i386 object file format for the
mixed 16- and 32-bit code (file start.o)

================================================================
Running
================================================================
Run vm86.com from the MS-DOS or FreeDOS command prompt.

Users of Windows 95/98/ME: the MS-DOS prompt is available by
using the boot menu. Press and hold F8 just before
"Starting Windows 95" is displayed, then chose
"Safe mode, command prompt only"

You can NOT run this program from a Windows, DOSEMU, or NTVDM
DOS box, or if EMM386 is loaded. In all of these cases, you will
get this error message:
        CPU already in Virtual-8086 mode 
        (Windows DOS box or EMM386 loaded?)

If you don't have MS-DOS or FreeDOS, you can make a bootable
floppy disk to run this program:

1. If you have Windows, get John Fine's PARTCOPY (PCOPY):
        http://my.execpc.com/~geezer/johnfine/index.htm#zero

2. Put a formatted DOS/Windows (FAT12) floppy disk in the first
   floppy drive (A: or /dev/fd0)

3. Enter the 'boot' directory and type:
        (Windows)       partcopy fat12.bin 0 200 -f0
        (Linux/UNIX)    cat fat12.bin >/dev/fd0
   Type carefully when using PARTCOPY, so you don't overwrite
   your hard disk boot sector. See the PARTCOPY docs.

4. Leave the 'boot' directory, copy vm86.com to the root
   directory of the floppy disk, and rename it 'load.bin':
        (Windows)       copy vm86.com a:\load.bin
        (Linux/UNIX)    mount /dev/fd0 mnt
                        cp vm86.com mnt/load.bin

5. Boot your PC from the resulting floppy disk.

================================================================
Features
================================================================
- Works with these BIOS interrupts:
  - INT 10h (VGA and VBE)
  - INT 13h (disk)
  - INT 16h (keyboard)
  - other interrupts called from the interrupts listed above:
    INT 15h, INT 2Ah
- Works with 16-bit BIOS code accessed by far CALL, e.g. PnP BIOS
- Unlike most other V86 mode monitors, this code does not use
  TSS-based task-switching to switch between protected mode and
  virtual 8086 mode
- V86 code can run at IOPL=0 or IOPL=3
- Kernel can report what I/O ports were used by V86 code
- Faulting I/O operations cause the appropriate port to be
  enabled in the IOPB. The offending instruction is then re-tried.
- Includes code that is useful if screen becomes garbled
  - Kernel text output can be sent to serial port
  - set_text_mode() function sets text mode without using the
    video BIOS

================================================================
Bochs and GCC warnings
================================================================
SP is set to 0xFFFF when the V86 mode session begins. When the
16-bit code tries to pop a value from the empty stack, it causes
a stack fault, which signals the end of the V86 session. The
beauty of this approach is that IRET from an empty stack will
end the V86 session, even if IOPL=3.

Bochs 2.1.1 pops up a dialog box if the simulated CPU attempts
to pop a value from the stack when SP=0xFFFF. I consider this a
bug in Bochs. Click on "Continue and don't ask again" in the
dialog box, then click "OK".

(With each new version of Bochs, it seems I have to spend more
time pressing Enter and clicking on buttons in dialog boxes.
This is _not_ progress.)

A second problem with Bochs is that the new LGPL video BIOS does
not support banked framebuffers, so demo4() will not work.

A third problem is warnings which sometimes appears during demo7():
	Panic at ROMBIOS.C, line 1558
	FATAL: int13_diskette:f02: ctrl not ready
The disk read appears to work correctly, however.

A fourth (minor) problem with Bochs is that 90x60 text mode
doesn't work properly

At one point during the development of this program, it stopped
working. Adding debug kprintf()s caused the code to start
working again. This erratic behavior went away when I switched
from GCC 2.95.2 to GCC 3.2. I don't know if it was a compiler
bug or not.

Similar erratic behavior appeared when I tried to make some
functions __inline__ in VM86.C

================================================================
Testing
================================================================
All I/O initially disabled

IOPL=0
	Demo	Pentium	Pentium	486SX[1]486SX	Bochs	Bochs
	number	MS-DOS	boot	MS-DOS	boot	FreeDOS	boot
	------	-------	-------	------	-----	-------	-----
	4       OK      OK      OK      OK      [2]     [2]
	3       OK      OK      OK      OK      ?       ?
	1       OK      OK      OK      OK      OK      OK
	2       OK      OK      OK      OK      OK      OK
	6       OK      OK      OK      OK      OK      OK
	7       OK      OK      OK      OK      OK      [3]
	PnP	OK	OK      [4]     [4]     [4]     [4]

IOPL=3
	Demo	Pentium	Pentium	486SX[1]486SX	Bochs	Bochs
	number	MS-DOS	boot	MS-DOS	boot	FreeDOS	boot
	------	-------	-------	------	-----	-------	-----
	4       OK      OK      OK      OK      [2]     [2]
	3       OK      OK      OK      OK      ?       ?
	1       OK      OK      OK      OK      OK      OK
	2       OK      OK      OK      OK      OK      OK
	6       OK      OK      OK      OK      OK      OK
	7       OK      OK      OK      OK      OK      [3]
	PnP	OK	OK      [4]     [4]     [4]     [4]

[1] Computer freezes when VM86.COM exits to DOS.
    Code works OK on 486SX system when run from bootsector.

[2] Bochs LGPL video BIOS does not support banked framebuffer,
    so demo4() and demo3() did not execute

[3] "Panic at ROMBIOS.C, line 1558"
    "FATAL: int13_diskette:f02: ctrl not ready"
    disk read still seems to work...?
    (enabling IRQ 0 doesn't help)

[4] No 16-bit PnP BIOS support in this environment,
    so scan_pnp16() did not execute

================================================================
To do
================================================================
- Emulate I/O instead of just patching IOPB and re-starting
  instruction

Later:
- Virtualized control registers:
  - Virtual bits in EFLAGS (IF and maybe IOPL)
  - Virtual bits in CR0 (e.g. TS; cleared by CLTS instruction)
- Virtualized hardware
  - 8259 interrupt controller chips
  - 8237 DMA controller chips???
  - 8243 timer chip???
  - 8042 keyboard controller chip???
  - A20 gate?
  - VGA???
  - SoundBlaster
- Let v86_emulate() emulate code running in ring 3 pmode?
- Support Pentium VME
- Call DOS interrupts in V86 mode
- Make a full-blown DOS extender

================================================================
Notes
================================================================
A bug that I fixed:
If HIMEM.SYS is loaded, this code returns to DOS successfully only if:
- pmode code uses the stack supplied by DOS, not the built-in stack
- no 16-bit DPMI tools (e.g. TC, BC) run before running VM86.COM
It works fine if HIMEM.SYS is NOT loaded, or if XMSMMGR.EXE
is loaded instead -- even if built-in stack is used instead of DOS stack.
*** FIXED IT: zero top 16 bits of ESP before exiting to DOS.

Testing the 8259 chip to distinguish hardware IRQs from
exceptions doesn't work:
	outportb(0x20, 0x0B);
	if(inportb(0x20) != 0)
		/* hardware IRQ */;
	else
		/* CPU exception */;
(probably because exception can be nested inside IRQ)

================================================================
Summary of illegal, privileged, and sensitive instructions
================================================================

illegal in real mode: LOCK with improper instruction

			REAL-	RING 0	RING 3	V86	WIN 95
INSTRUCTCTION		MODE	PMODE	PMODE	MODE	V86 MODE
-------------------	-----	------	------	----	--------
GROUP 0: UNPRIVILEGED IN ALL MODES

SGDT			OK	OK	OK	OK	OK
SIDT			OK	OK	OK	OK	OK
SMSW			OK	OK	OK	OK	OK
(many others; only the non-obvious instructions are listed here)
---------------------------------------------------------------------
GROUP 1: UNPRIVILEGED IN PMODE, ILLEGAL IN REAL MODE AND V86 MODE

SLDT			-#06-	OK	OK	-#06-	-#06-
STR			-#06-	OK	OK	-#06-	-#06-
VERR			-#06-	OK	OK	-#06-	-#06-
VERW			-#06-	OK	OK	-#06-	-#06-
LAR			-#06-	OK	OK	-#06-	-#06-
LSL			-#06-	OK	OK	-#06-	-#06-
ARPL			-#06-	OK	OK	-#06-	-#06-
---------------------------------------------------------------------
GROUP 2: PRIVILEGED IN PMODE, ILLEGAL IN REAL MODE AND V86 MODE

LLDT			-#06-	OK	-#0D-	-#06-	-#06-
LTR			-#06-	OK	-#0D-	-#06-	-#06-
---------------------------------------------------------------------
GROUP 3: PRIVILEGED IN PMODE AND V86 MODE (EXCEPTION 0Dh IF CPL > 0)

LGDT			OK	OK	-#0D-	-#0D-	"MS-DOS mode" prompt
LIDT			OK	OK	-#0D-	-#0D-	"MS-DOS mode" prompt
LMSW			OK	OK	-#0D-	-#0D-	"MS-DOS mode" prompt

HLT                     OK      OK      -#0D-   -#0D-   OK if IF=1; -#0D- if IF=0
MOV to/from CRn         OK      OK      -#0D-   -#0D-   OK (virtualized?)
MOV to/from DRn		OK	OK	-#0D-	-#0D-	?
MOV to/from TRn		OK	OK	-#0D-	-#0D-?	?
---------------------------------------------------------------------
GROUP 4: IOPL-SENSITIVE IN PMODE AND V86 MODE (EXCEPTION 0Dh IF CPL > IOPL)

CLI			OK	OK	-#0D-	-#0D-	OK (virtual IF bit)
STI			OK	OK	-#0D-	-#0D-	OK (virtual IF bit)
---------------------------------------------------------------------
GROUP 5: IOPL- AND IOPB-SENSITIVE IN PMODE; IOPB-SENSITIVE IN V86 MODE

IN			OK	OK	-#0D-	-#0D-	OK (virtual I/O)
INS			OK	OK	-#0D-	-#0D-	OK (virtual I/O)
OUT			OK	OK	-#0D-	-#0D-	OK (virtual I/O)
OUTS			OK	OK	-#0D-	-#0D-	OK (virtual I/O)
---------------------------------------------------------------------
GROUP 6: IOPL-SENSITIVE IN V86 MODE

PUSHF                   OK      OK      OK      -#0D-   ?
POPF [1]                OK      OK      [2]     -#0D-   ?
INT                     OK      OK      [3]     -#0D-   ?
IRET                    OK      OK      [4]     -#0D-   ?

[1] POPF never changes EFLAGS.VM or EFLAG.RF
[2] POPF does not change EFLAGS.IOPL unless CPL == 0
    POPF does not change EFLAGS.IF   unless CPL <= IOPL
[3] INT from Ring 3 through non-Ring 3 interrupt gate
    causes GPF instead
[4] Ring 3 code can IRET only to Ring 3 code

---------------------------------------------------------------------
GROUP 7: MISCELLANEOUS

CLTS                    OK      OK      -#0D-   OK      OK
LOCK                    [1]     [1]     [1]     [1]     ?

[1] LOCK prefix causes -#06- unless the following instruction
    is one of these:
        BT, BTS, BTR, BTC                   mem, reg/imm
        XCHG                                reg, mem
        XCHG                                mem, reg
        ADD, OR, ADC, SBB, AND, SUB, XOR    mem, reg/imm
        NOT, NEG, INC, DEC                  mem
    XCHG always assert the LOCK signal, even without the prefix.


486:
* INVD-Invalidate cache, without writeback.
* WBINVD-Invalidate cache, with writeback.
* INVLPG-Invalidate TLB entry.
Pentium:
* RDMSR-Read Model-Specific Registers.
* WRMSR-Write Model-Specific Registers.
* RDPMC-Read Performance-Monitoring Counter.
* RDTSC-Read Time-Stamp Counter.

