I've been doing a lot of experimenting with assembly programming in MS-DOS. I've read that Windows 3.1 acts as a DPMI host for DOS programs, and that DPMI uses interrupt 31h for function calls.
So let's give that a try. I open a DOS prompt in Windows 3.1...
C:\WINDOWS>debug
-a100
23A4:0100 mov ax,0400
23A4:0103 int 31
23A4:0105
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=23A4 ES=23A4 SS=23A4 CS=23A4 IP=0100 NV UP EI PL NZ NA PO NC
23A4:0100 B80004 MOV AX,0400
-p
AX=0400 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=23A4 ES=23A4 SS=23A4 CS=23A4 IP=0103 NV UP EI PL NZ NA PO NC
23A4:0103 CD31 INT 31
-p
So something clearly isn't working.
I try again, using the T
race command instead of P
roceed. This way it will actually step inside of the interrupt handler instead of skipping over it.
AX=0400 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=23A4 ES=23A4 SS=23A4 CS=23A4 IP=0103 NV UP EI PL NZ NA PO NC
23A4:0103 CD31 INT 31
-t
AX=0400 BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=23A4 ES=23A4 SS=23A4 CS=F000 IP=FF01 NV UP DI PL NZ NA PO NC
F000:FF01 7261 JB FF64
-
Let's see what's about to be executed...
-u
F000:FF01 7261 JB FF64
F000:FF03 63 DB 63
F000:FF04 6C DB 6C
F000:FF05 65 DB 65
F000:FF06 20564D AND [BP+4D],DL
F000:FF09 205669 AND [BP+69],DL
⋮
F000:FF18 53 PUSH BX
F000:FF19 0000 ADD [BX+SI],AL
F000:FF1B 0000 ADD [BX+SI],AL
F000:FF1D 0000 ADD [BX+SI],AL
F000:FF1F 0000 ADD [BX+SI],AL
-
...Yeah, that doesn't look like anything that's supposed to be executed. In fact, the byte code looks suspiciously like ASCII text. Sure enough...
-df000:ff01
F000:FF00 72 61 63 6C 65 20 56-4D 20 56 69 72 74 75 61 racle VM Virtua
F000:FF10 6C 42 6F 78 20 42 49 4F-53 00 00 00 00 00 00 00 lBox BIOS.......
F000:FF20 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
F000:FF30 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
F000:FF40 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
F000:FF50 00 58 4D CF CF 89 C0 89-C0 89 C0 89 C0 89 C0 FC .XM.............
F000:FF60 5F 53 4D 5F 7D 1F 02 05-FF 00 00 00 00 00 00 00 _SM_}...........
F000:FF70 5F 44 4D 49 5F 58 C2 01-00 10 0E 00 0A 00 25 00 _DMI_X........%.
F000:FF80 00 .
-
That's clearly not a DPMI handler. Anyway, if I step past the JB FF64
, it'll proceed to the DB 63
(the 'c' in "Oracle") and that's what causes the invalid instruction error. Alternatively, if the Carry flag is set, it'll take the jump and encounter what it interprets as a JGE FF85
instruction. If the flags are such that it doesn't take the jump, it'll eventually reach another invalid instruction. Otherwise, it'll increase the byte at [BX+SI]
by the value in AL
105 times, increase the byte 77 bytes after that by the value in BL
a single time, and finally reboot the VM, because the next instruction is the real-mode reset vector. (In v86 mode, but I guess Windows 3.1 allows it.)
Long story short, it's clear there's no DPMI handler installed at INT 31H like there's apparently supposed to be. A few additional bits of information:
You may have noticed I only typed int 31
, not int 31h
. This is not a mistake; debug
uses and expects hexadecimal everywhere. And as you saw, it didn't try to execute the graphics mode bitmap font as code. :P
I tried it with Qualitas MAX instead of Windows 3.1, as well as with no DPMI host at all, and got the same result in both cases. (Minus the Windows 3.1 error dialog, of course.)
With that in mind, can anyone tell me what I'm doing wrong?
The Real/Virtual 86 Mode interface for DPMI is only interrupt 2Fh service 1687h. That returns an entrypoint which you can use to enter Protected Mode. Interrupt 31h services are only available in Protected Mode. Here's an example of a small DPMI client. Relevant code to enter Protected Mode:
mov ax, 1687h
int 2Fh
test ax, ax ; DPMI host installed?
jnz nohost
push es ; save DPMI entry address
push di
test si, si ; host requires client-specific DOS memory?
jz .nomemneeded ; no -->
mov bx, si
mov ah, 48h
int 21h ; allocate memory
jc nomemory
mov es, ax
.nomemneeded:
; (message and breakpoint omitted)
mov bp, sp
mov ax, 0001h ; start a 32-bit client
call far [bp] ; initial switch to protected-mode
jnc initsuccessful
initfailed:
Protected Mode is generally exited by executing PM interrupt 21h service 4Ch (which subsequently is also passed to the DOS 86M handler and thus terminates the DOS process).
By the way, to debug a DPMI client you may want to use FreeDOS DebugX or my fork lDebugX.