Question about djgpp's int-wrapper

1 view
Skip to first unread message

Martin Steuer

unread,
Jun 8, 2002, 8:25:28 AM6/8/02
to dj...@delorie.com
Hi folks,

While examining the source of the wrapper in gopint.c I found that it does
not issue a 'sti' before returning with 'iret'.
So I wonder if this is ok, as the DPMI Spec. says that 'popf' and thus also
'iret' may not modify the interrupt flag, wouldn't this leave the
interrupts disabled?

I realize that the wrapper is also written for software interrupts where
the interrupts aren't disabled. Then of course this is the right behaviour,
but it would mean that one must issue a 'sti' within your interrupt handler
if the wrapper is used for hardware interrupts. Is this right?

Please tell me if i'm mistaken.

Martin Str|mberg

unread,
Jun 8, 2002, 3:07:26 PM6/8/02
to
Martin Steuer <martin...@gmx.de> wrote:
: While examining the source of the wrapper in gopint.c I found that it does

If interrupts were enabled before the interrupt came along, the iret
that returns control to the interrupted code should enable them again
as the IF is set in the image on the stack.


Right,

MartinS

CBFalconer

unread,
Jun 8, 2002, 3:16:45 PM6/8/02
to
Martin Steuer wrote:
>
> While examining the source of the wrapper in gopint.c I found
> that it does not issue a 'sti' before returning with 'iret'.

x86 machines store the interrupt state with the flags on the
stack. Thus the iret restores the entry state (which may not have
been enabled - think software interrupts).

--
Chuck F (cbfal...@yahoo.com) (cbfal...@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

Martin Steuer

unread,
Jun 9, 2002, 5:05:27 AM6/9/02
to dj...@delorie.com
Hello,

>If interrupts were enabled before the interrupt came along, the iret
>that returns control to the interrupted code should enable them again
>as the IF is set in the image on the stack.

The problem i mean is that the DPMI Specification says that non-priviledged
code may not be able to alter the IF via 'popf' or 'iret'.
Therefore the DPMI Spec. suggests to insert a 'sti' before an 'iret' in
hardware interrupt handlers, because 'sti' is a priviledged instruction it
will be trapped and the DPMI Host will then execute an 'sti' in Ring0.

Martin Stromberg

unread,
Jun 9, 2002, 6:22:57 AM6/9/02
to
Martin Steuer (martin...@gmx.de) wrote:
: The problem i mean is that the DPMI Specification says that non-priviledged
: code may not be able to alter the IF via 'popf' or 'iret'.
: Therefore the DPMI Spec. suggests to insert a 'sti' before an 'iret' in
: hardware interrupt handlers, because 'sti' is a priviledged instruction it
: will be trapped and the DPMI Host will then execute an 'sti' in Ring0.

1. Where does it say that?

2. IIRC, if sti is a priviledged instruction (because of IOPL < 3),
then iret and popf are too. So why would an emulated sti set IF and
not an emulated iret or popf?


Right,

MartinS

Lawrence Rust

unread,
Jun 9, 2002, 12:51:23 PM6/9/02
to
"Martin Stromberg" <epl...@lu.erisoft.se> wrote in message
news:advaa1$cgm$1...@antares.lu.erisoft.se...

> Martin Steuer (martin...@gmx.de) wrote:
> : The problem i mean is that the DPMI Specification says that
non-priviledged
> : code may not be able to alter the IF via 'popf' or 'iret'.
> : Therefore the DPMI Spec. suggests to insert a 'sti' before an 'iret' in
> : hardware interrupt handlers, because 'sti' is a priviledged instruction
it
> : will be trapped and the DPMI Host will then execute an 'sti' in Ring0.
>
> 1. Where does it say that?

http://www.delorie.com/djgpp/doc/dpmi/ch4.4.2.html


> 2. IIRC, if sti is a priviledged instruction (because of IOPL < 3),
> then iret and popf are too. So why would an emulated sti set IF and
> not an emulated iret or popf?

If you look at Intel's IA32 architecture manual:

ftp://download.intel.com/design/pentium4/manuals/24547107.pdf

you'll see on page 3-49 that in protected mode, iret only restores
EFLAGS(IF) if CPL <= IOPL. This is also true of popf. So if the code is
run on a system such as Windows NT, 2K or XP where CPL is 3 and IOPL is 0
then the task will be left with virtual interrupts disabled. The system
will remain responsive but the task will not receive any more interrupts
:-(.

However, sti signals GP(0) if CPL > IOPL, which on NT is handled by the
NTVDM and will enable virtual interrupts to the task.

IMHO this is a design fault in the IA32. The correct action would have been
to signal GP(0) if EFLAGS(IF) is altered. This gives the host OS (NTVDM)
the chance to update the state of virtual interrupts for the task.

This design fault prevents many protected mode games, like Doom and
DukeNukem3D, from running with sound on NT, Win2K and XP. In many cases the
sound driver disables interrupts around sound card h/w accesses with code
like...

pushf
cli
...
popf ; Restore IRQ state

Here the cli traps to NTVDM which obligingly disables the virtual interrupt
state. However the popf that should restore the interrupt state does
nothing so virtual interrupts remain disabled.

If you want to run these games with a sound card emulator like SoundFX
(www.softsystem.co.uk/sndfx202.zip) then it's necessary to patch the code to
remove the cli opcodes.

--
Lawrence Rust
Software Systems, www.softsystem.co.uk

The problem with Windows XP - http://www.arachnoid.com/boycott


Charles Sandmann

unread,
Jun 9, 2002, 1:21:03 PM6/9/02
to

Yes, the whole thing is a mess. But the DPMI provider reflects these
interrupts to you (unless they were chained) - and the Windows DPMI providers
have done things internally to try and fix the mess.

Adding a STI can cause problems also when chaining or fcall'ing (and you
aren't ready for them to be enabled yet). There is no good universal fix,

We've recently found out that Windows 2000 seems to ignore the DPMI 0x9
call to block interrupts (and this interrupt is actually recommended
in the spec to avoid the emulation overhead of STI/CLI).

Martin Stromberg

unread,
Jun 9, 2002, 3:09:30 PM6/9/02
to
Lawrence Rust (l...@nospam.softsystem.co.uk) wrote:
: If you look at Intel's IA32 architecture manual:

: ftp://download.intel.com/design/pentium4/manuals/24547107.pdf

: you'll see on page 3-49 that in protected mode, iret only restores

Well, I don't. But perhaps you meant page 3-349 or near that?

: EFLAGS(IF) if CPL <= IOPL. This is also true of popf. So if the code is


: run on a system such as Windows NT, 2K or XP where CPL is 3 and IOPL is 0
: then the task will be left with virtual interrupts disabled. The system
: will remain responsive but the task will not receive any more interrupts
: :-(.

Ouch!

: However, sti signals GP(0) if CPL > IOPL, which on NT is handled by the


: NTVDM and will enable virtual interrupts to the task.

: IMHO this is a design fault in the IA32. The correct action would have been

Yes. I wonder what they were thinking at Intel when they designed
this? Those five pages of pseudo-code showing us what is happening is
impressive (in a baaaad way). I only wish they could have thought it
through a little more before making the chips.


Right,

MartinS

Vlad R.

unread,
Jun 9, 2002, 9:19:52 PM6/9/02
to
For the record: starting with Windows XP there is a built-in
(undocumented) workaround to this problem, but you have to perform a
system call from within NTVDM.EXE in order to activate it
(NTDLL.DLL:NtVdmControl with function 0x0d and parameter 0x01
activates the workaround, and function 0x0d with parameter 0x00
deactivates it). Still a big pain, though.

V.

"Lawrence Rust" <l...@nospam.softsystem.co.uk> wrote in message news:<DILM8.896$ca2.1...@newsfep1-win.server.ntli.net>...

Martin Stromberg

unread,
Jun 10, 2002, 4:09:07 AM6/10/02
to
Vlad R. (vl...@usa.net) wrote:
: For the record: starting with Windows XP there is a built-in

: (undocumented) workaround to this problem, but you have to perform a
: system call from within NTVDM.EXE in order to activate it
: (NTDLL.DLL:NtVdmControl with function 0x0d and parameter 0x01
: activates the workaround, and function 0x0d with parameter 0x00
: deactivates it). Still a big pain, though.

Not only a big pain, but impossible. Or do you know how to call that
from a plain DOZE program?

Please don't top post and cut irrelevant parts.


Right,

MartinS

Martin Steuer

unread,
Jun 10, 2002, 5:31:49 AM6/10/02
to dj...@delorie.com
Charles Sandmann (sand...@clio.rice.edu) wrote:

>We've recently found out that Windows 2000 seems to ignore the DPMI 0x9
>call to block interrupts (and this interrupt is actually recommended
>in the spec to avoid the emulation overhead of STI/CLI).

This directs my view to the enable()/disable() implementations, there we
leave on the IF but not on the virtual one.
As you say Win2k ignores the DPMI Function calls for the virtual interrupt
flag, so the implementation of disable() is "half right" for Win2k, it at
least disables Virtual Interrupts to the program but doesn't return the
correct state of the virtual interrupt flag.
But on Win9x we should use calls to
__dpmi_get_and_disable_virtual_interrupt_state() for efficiency. But i
think on those systems there seams to be no real Virtual Interrupt Flag at
all, at least on Win95 I can disable interrupts and the whole system is
blocked... so there is not much to worry about enable()/disable() there, in
this case it is really only a matter of efficiency.

The conclusion: there is no overall workaround and we can't just blindly
leave on enable()/disable() in Win2k during HW-Access sequences.
But for hardware interrupt handlers there is no problem with issuing a
'sti' before your 'iret', if you chain then you should just let the
interrupts disabled.

Alex Yeryomin

unread,
Jun 11, 2002, 1:44:23 AM6/11/02
to
Hi all.

So, it means that some of DPMI providers may "block" sti/cli
instruction. But if your program runs under "pure" DOS & CWSDPMI
(usual DJGPP configuration), you can rely upon the fact that it works
fine with privelage instructions like sti/cli, and without any CPU
time overhead.

Since cli and sti are privileged instructions, they will cause a
protection violation and the DPMI provider will simulate the
instruction. Because of the overhead involved in processing the
exception, cli and sti should be used as little as possible. In
general, you should expect either of these instructions to require at
least 300 clocks (see below how you can test it).

As a rule (near 100% of probability) the interrupt controller on your
computer will work in FNI mode (Fully Nested Interrupt mode). It means
that when an intterupt is raised, the interrupt controller (f.e. Intel
8259 chip) is immediatelly blocked - it will not accept any interrupt
request from peripheral devices till the handler processing the
current interrupt tells NON_SPECIFIC_END_OF_INTERRUPT (=0x20) to
interrupt controller's control register (0x20 port for leading, 0xA0
port for driven IC). And the program should enable CPU to process
interrupts to set IF flag.

Of course, when the instuction "iret" is executed, it restores IF flag
of CPU (and all context previously stored to the stack when the
inturrupt was raised). But don't forget that if some of hardware
interrupt handlers, which your program establishes, may work a long
time you must give a change to others interrupts to be proccessed. It
is inadmissibly to lost some important interrupts, f.e, timer, I/O
interruprs and so on.

An interrupt handler may look like this (it is real hardware interrupt
handler for Real Time Clock to count milliseconds):

static void RTC_Interrupt_Handler (void)
{
INTERRUPT_ENTER ();

// Read status register 3 of RTC, otherwise
// the interrupt will not be enabled
readReg (RTC_STATUS_REG_3);

// Read status register 2 of RTC
// timer interrupt?
if (readReg (RTC_STATUS_REG_2) & 0x40)
++TimerCount;

INTERRUPT_LEAVE_BOTH ();
}
END_OF_STATIC_FUNCTION (RTC_Interrupt_Handler);


where:


#define INTERRUPT_ENTER(); asm ("sti"); /* enable maskable interrupts
*/

#define INTERRUPT_LEAVE(); asm ("cli"); /* disable maskable interrupts
*/ \
outportb (0x20, 0x20); /* End of interrupt
*/

#define INTERRUPT_LEAVE_BOTH(); asm ("cli"); /* disable maskable
interrupts */ \
outportb (0x20, 0x20); /* End of
interrupt */ \
outportb (0xA0, 0x20); /* End of
interrupt */

Here, we must unblock both the leading Interrupt Controller and driven
one because the RTC interrupt is mapped to the second IC.


Again, in general, you should expect that "sti" or "cli" instructions
to require many clocks under some DPMI provider. But used pure DOS it
takes only several CPU ticks. How can you test it?It is easy. Use
"rdtsc" instruction. It allows to take CPU ticks at the moment, and
returns 64-bits tick counter from the computer has rebooted. It shows
that "sti" takes only ~30 CPU ticks to be executed. It proves that a
program under DOS & CWSDMPI has enough privelage to execute "sti" or
"cli" instructions.

Best regards,

Alex

PS Here is "rdtsc" code.

-------------------------------------------
[BITS 32]
[GLOBAL _Read_TSC]
[SECTION .text]
_Read_TSC:
mov ecx, [esp+4]
rdtsc
mov [ecx], eax
mov [ecx+4], edx
ret
;_Read_TSC
-------------------------------------------
typedef unsigned long long int TICK;
extern void Read_TSC (TICK *tick);
-------------------------------------------

CBFalconer

unread,
Jun 11, 2002, 4:20:39 AM6/11/02
to
Alex Yeryomin wrote:
>
... snip ...

>
> -------------------------------------------
> [BITS 32]
> [GLOBAL _Read_TSC]
> [SECTION .text]
> _Read_TSC:
> mov ecx, [esp+4]
> rdtsc
> mov [ecx], eax
> mov [ecx+4], edx
> ret
> ;_Read_TSC
> -------------------------------------------
> typedef unsigned long long int TICK;
> extern void Read_TSC (TICK *tick);
> -------------------------------------------

You need to do some guarding. rdtsc will immediately crash a '486
as an illegal instruction.

Lars Erdmann

unread,
Jun 11, 2002, 7:11:40 PM6/11/02
to
Hi,

I have had a discussion with Martin Kiewitz (who patched a couple of DOS
games to make sound work under OS/2).
The stuff below is just bad coding. If you use "cli" you should use
"sti", since once you used "cli" you don't know the state of the irq
flag anyways (apart from having it pushed on the stack) and in that
case, you should always use "sti" to reenable them.
If you use "cli" anywhere else but in an interrupt handler, you know
that irqs will have been enabled before (so the sequence cli,sti will
definitely return you to the state that the irq flag was before).
For an ISR (as far as I remember), interrupts are always disabled on
entry (aren't they ?), so only the sequence sti,cli would make sense.

So, for usage outside of an ISR this would be:
pushf
cli
...
sti
popf

Martin did exactly that to patch the DOS games (hexen,heretic,doom,doom
2) and guess what, they work perfectly well under OS/2 now.

Lars

Martin Stromberg

unread,
Jun 12, 2002, 3:54:20 AM6/12/02
to
Lars Erdmann (lars.e...@arcor.de) wrote:
: If you use "cli" anywhere else but in an interrupt handler, you know

: that irqs will have been enabled before (so the sequence cli,sti will
: definitely return you to the state that the irq flag was before).

No. Not in an XMS provider which might be called from ISR.


Right,

MartinS

Martin Stromberg

unread,
Jun 12, 2002, 4:43:46 AM6/12/02
to
Martin Stromberg (epl...@lu.erisoft.se) wrote:

Too fast as usual...

Neither in ISR called from another ISR.

Right,

MartinS

Vlad R.

unread,
Jun 12, 2002, 8:40:33 PM6/12/02
to
> Not only a big pain, but impossible. Or do you know how to call that
> from a plain DOZE program?

Very possible. And yes, I do know how.

> Please don't top post and cut irrelevant parts.

I beg your pardon?!

--

To call that from a DOS program:
- write a VDD (a .dll)
- from a DOS program tell NTVDM to load that DLL, and then have the
DLL make the relevant system call.

For an example of how to do that:

- DOS loader (DOS, real-mode, but should also work in p-mode)
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vdmsound/VDMSCore/Sources/VDDLoader/DOSDrv.cpp?rev=1.5&content-type=text/vnd.viewcvs-markup

- VDD (Win32)
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vdmsound/VDMSCore/Sources/VDDLoader/VDDDispatch.cpp?rev=1.6&content-type=text/vnd.viewcvs-markup

- System call to NTDLL (Win32)
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vdmsound/VDMSCore/Sources/VDDLoader/VDMServices.cpp?rev=1.8&content-type=text/vnd.viewcvs-markup

V.

Vlad R.

unread,
Jul 10, 2002, 11:19:14 PM7/10/02
to

Update: only works in "real" (well, v86 really) mode.

V.

Andrew Cottrell

unread,
Jul 11, 2002, 8:12:49 AM7/11/02
to
>> - DOS loader (DOS, real-mode, but should also work in p-mode)
>> http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/vdmsound/VDMSCore/Sources/VDDLoader/DOSDrv.cpp?rev=1.5&content-type=text/vnd.viewcvs-markup
>
>Update: only works in "real" (well, v86 really) mode.
I missed the original post. Sorry if this is off topic.

If you want to have sound in a DJGPP app under Win 2K or XP then check
out VDMSound at http://www.ece.mcgill.ca/~vromas/vdmsound/

It works and I have used it with a motherboard with buiult in Creative
128 something sound card. I can't remember the exact spec.

Andrew

Reply all
Reply to author
Forward
0 new messages