Jukka Heinonen jhei@iki.fi writes:
This patch speeds up software interrupts in real mode considerably, in addition to making interrupt handling simpler (at least in my opinion). Instead of throwing an exception, vm86 handler calls winedos callback as is done in case of protected mode software interrupts.
I think it would be cleaner to make __wine_enter_vm86 simply return in that case and let winedos deal with that. We don't want the signal handling to have to know about winedos if possible.
I think it would be cleaner to make __wine_enter_vm86 simply return in that case and let winedos deal with that. We don't want the signal handling to have to know about winedos if possible.
Well, that was actually one of the possibilities I did consider. Would it be acceptable, if __wine_enter_vm86 returns an integer between 0x00 and 0xff if software interrupt is to be emulated and -1 if hardware interrupt (or callback) is pending? Not a pretty interface, I'm afraid, but it would be independent of vm86 syscall details. It is unfortunate that vm86 stuff needs to be inside ntdll, but that probably cannot be helped.
Anyway, __wine_enter_vm86 is not that interesting and I don't think I'm going to post patches against it right now. More interesting is that DOS emulation needs to be able to hook to SIGUSR2/SIGALRM even in protected mode so some kind of callback to winedos is needed in set_vm86_pend. How to handle TEB.vm86_pending flag in this case is something I don't know. There are lots of messy ways but doing it in a sane way is not easy.
ons, 2003-03-12 kl. 05:42 skrev Jukka Heinonen:
I think it would be cleaner to make __wine_enter_vm86 simply return in that case and let winedos deal with that. We don't want the signal handling to have to know about winedos if possible.
Well, that was actually one of the possibilities I did consider. Would it be acceptable, if __wine_enter_vm86 returns an integer between 0x00 and 0xff if software interrupt is to be emulated and -1 if hardware interrupt (or callback) is pending? Not a pretty interface, I'm afraid, but it would be independent of vm86 syscall details. It is unfortunate that vm86 stuff needs to be inside ntdll, but that probably cannot be helped.
Anyway, __wine_enter_vm86 is not that interesting and I don't think I'm going to post patches against it right now. More interesting is that DOS emulation needs to be able to hook to SIGUSR2/SIGALRM even in protected mode so some kind of callback to winedos is needed in set_vm86_pend. How to handle TEB.vm86_pending flag in this case is something I don't know. There are lots of messy ways but doing it in a sane way is not easy.
What I might have done if we sticked to exceptions would be to have the whole DOS VM thread (that runs the code) be protected by a __TRY/__EXCEPT block, not just DOSVM_Enter, and then have a flag (maybe in the TEB) that the DPMI could set whenever executing in app protected mode, and either set_vm86_pend or the exception handler could check; the point being that exceptions would still be thrown in protected mode, which the thread-wide exception handler could then catch and handle appropriately. If you really don't want to use exceptions, then I suppose you'll have to do a whole lot of stack frame mangling (change the context so the code resumes at some custom winedos handler, which would then dispatch to the right stuff) instead, which could get ugly.
Why not rather address the root cause of why you don't like to use exceptions, that they're slow? I don't see any fundamental reason they have to be, since as I understand, after the frame is set up, they basically just need to look up some memory addresses and jump to the appropriate code. There isn't an obvious reason a direct call should be that much faster. I'm pretty sure Windows does it reasonably fast, why can't Wine? I think that'd be worth looking into.
On Wed, Mar 12, Ove Kaaven wrote:
What I might have done if we sticked to exceptions would be to have the whole DOS VM thread (that runs the code) be protected by a __TRY/__EXCEPT block, not just DOSVM_Enter, and then have a flag (maybe in the TEB) that the DPMI could set whenever executing in app protected mode, and either set_vm86_pend or the exception handler could check; the point being that exceptions would still be thrown in protected mode, which the thread-wide exception handler could then catch and handle appropriately. If you really don't want to use exceptions, then I suppose you'll have to do a whole lot of stack frame mangling (change the context so the code resumes at some custom winedos handler, which would then dispatch to the right stuff) instead, which could get ugly.
Why not rather address the root cause of why you don't like to use exceptions, that they're slow? I don't see any fundamental reason they have to be, since as I understand, after the frame is set up, they basically just need to look up some memory addresses and jump to the appropriate code. There isn't an obvious reason a direct call should be that much faster. I'm pretty sure Windows does it reasonably fast, why can't Wine? I think that'd be worth looking into.
Well, an educated guess would be that SEH is going to be 10-100 times slower than direct function call, even if the implementation was optimal. However, this would be acceptable. Unfortunately, Wine SEH handling makes a server call in order to notify debugger and I suspect this is what makes SEH slow (using SEH also makes running DOS programs under winedbg hard, but this is most likely because I don't know how to make winedbg ignore VM86 exceptions).
It is likely that I shall not try to change the mechanism from SEH to something faster because it is not a particularly interesting problem. It would improve the performance of real mode DOS programs, but I guess I have more important things to do. However, I think replacing SEH would be much easier than you believe. As far as I understand it, something like the following would be enough:
* Replace custom vm86_enter with glibc vm86 function. * Make set_vm86_pend to return immediately if context is in VM86 (vm86 syscall will automatically return with return value of VM86_SIGNAL). If, instead teb->vm86_ptr is set and interrupts are enabled, push CS/IP to stack and replace them with pointer to real mode stub that does "sti and ret". This makes vm86 return with VM86_STI. * Make __wine_enter_vm86 check after return from vm86 that CS/IP does not point to stub defined above. If this is the case, just pop old CS/IP from stack. This fixes race if signal is received during return from vm86 and before teb->vm86_ptr is cleared. * Add some mechanism (return or direct call) to __wine_enter_vm86 for handling software interrupts and pending events (this is unlikely to make the function more than a few lines longer).
søn, 2003-03-16 kl. 19:25 skrev Jukka Heinonen:
Well, an educated guess would be that SEH is going to be 10-100 times slower than direct function call, even if the implementation was optimal. However, this would be acceptable. Unfortunately, Wine SEH handling makes a server call in order to notify debugger and I suspect this is what makes SEH slow (using SEH also makes running DOS programs under winedbg hard, but this is most likely because I don't know how to make winedbg ignore VM86 exceptions).
Perhaps that could be avoided if, when a debugger is attached, the appropriate TEB entry is written to (as in Windows) using ptrace or something, and such server calls are only done if that TEB entry is present.
It is likely that I shall not try to change the mechanism from SEH to something faster because it is not a particularly interesting problem. It would improve the performance of real mode DOS programs, but I guess I have more important things to do. However, I think replacing SEH would be much easier than you believe. As far as I understand it, something like the following would be enough:
- Replace custom vm86_enter with glibc vm86 function.
- Make set_vm86_pend to return immediately if context is in VM86 (vm86 syscall will automatically return with return value of VM86_SIGNAL). If, instead teb->vm86_ptr is set and interrupts are enabled, push CS/IP to stack and replace them with pointer to real mode stub that does "sti and ret". This makes vm86 return with VM86_STI.
- Make __wine_enter_vm86 check after return from vm86 that CS/IP does not point to stub defined above. If this is the case, just pop old CS/IP from stack. This fixes race if signal is received during return from vm86 and before teb->vm86_ptr is cleared.
- Add some mechanism (return or direct call) to __wine_enter_vm86 for handling software interrupts and pending events (this is unlikely to make the function more than a few lines longer).
But I was talking about interrupts in DPMI protected-mode. I was already sure that exceptions could be easily avoided in real-mode, the only reason I used them in the first place was because that's how Alexandre seemed to like it (he had set it up like that in his original winedos separation patch, which I then based my own winedos separation work on). Protected mode is a much harder beast to deal with if set_vm86_pend isn't supposed to know about DPMI directly.