First post, by evo
While experimenting with ultima7 in dosbox, I came accross some annoying bug that forced the mouse to freeze now and then. Even reseting the mouse driver (int 33, ax=0) wouldn't help and I was forced to restart dosbox. Well, as the bug seemed trivial (which in fact was not the case) I couldn't resist to hunt it down, but frequent lock-ups during u7 start prevented me from doing so. I was generating rapid mouse events by moving the mouse because I knew it was triggering the bug.
It turned out after some debugging (which helped me getting somewhat familiar with the code and the debugging capabilities) that both are actually symptoms of the same, obviously mouse-related bug.
What happens is the following:
Ultima7.com controls the basic program flow and launches several subprocesses, such as intro, mainmenu.exe, main game (u7.exe).
mainmenu.exe registers a mouse handler by means of int 33h, ax=14h (swap mouse handler) and it saves the original one into a variable I will simply call "origHandler" and which it restores before mainmenu.exe terminates (again by means of swap handler).
Now, the problem is that mainmenu.exe tests if origHandler is null pointer to decide whether it has already set its mouse handler (origHandler is 0 at program start). But since swap mouse handler returns 0000:0000 just after a mouse reset (i.e. no mouse handler at all), mainmenu.exe thinks it didn't set the handler yet and calls "swap mouse handler" a second time, but this time, it receives a pointer to its own handler which it set before. It is clear that after termination of mainmenu.exe, the handler will point to some piece of code which will be overwritten after another program is lauched (u7.exe in my case). Now, if I generate a mouse event before u7.exe sets again a mouse handler ... well you can imagine what happens 😀
Either the cpu gets stuck somewhere or, if you're lucky, it eventually "returns" from the "random code handler" if accidentally run over a ret or by some other means, but no EOI (end of interrupt) is generated which results in IRQ 12 never being raised again (not even after a mouse reset), thus freezing the mouse.
After checking some docs on the int 33h interface I couln't find any useful information about what int 33h /ax=14h should return. In fact I checked several mouse drivers and they aswell return 0000:0000 if there's no handler yet. This is clearly a bug in mainmenu.exe and it appears also in a real dos environment (I tried within bochs). For some strange reason I cannot remember having those troubles 10 years ago playing it on real hardware...
Fine, to avoid it simply don't move the mouse when loading. But this doesn't satisfy me.
The following changes came to my mind:
- return a pointer to a dummy handler (just a retf) (Q. to the devs: is there any memory region where one could place such a code, like an area reserved for DOS? Could aswell move it into the BIOS). This would allow also programs that don't check for null pointers to chain mouse handlers. I'm not sure whether any programs do this and the int-33h specs I found keep silent about this. But certainly, the u7 devs relied on the mouse driver returning something non-zero.
- to prevent the mouse from permantly freezing one could send the EOI *before* calling the custom mouse handler (that's also how cutemouse does it). Still, a handler should be called only once we returned from the previous handler (thus, there's a tiny chance to miss an event which is certainly worse - but that's how cutemouse does it if I understood the sources correctly...). To me, it is not clear whether handlers should be locked - but I think they should, just to be on the safe side. Also, a mouse reset should make sure that the corresponding IRQ bit in the ISR register is cleared.
What do other devs think?
Also the current implementation of mouse sensitivity is not satisfactory for me. In fullscreen mode, the OSes mouse sensitivity behavior doesn't propagate (via SDL) to dosbox, so mouse is very slow and I have to set the sensitivity to 400. But if i do so, the mouse jumps in 4-pixel steps which is inacceptable for certain games. The solution is trivial: only apply the acceleration if a certain motion threshold is reached. This way you have a quick motion if moving the mouse fast, but retain a pixel accurate selection.
PS (a little off topic):
- It would be of advantage (for new developers at least) if the code was a *little* better documented. I mean, its not easy to find any comments at all (at least the parts I was looking into, especially the dynamic core), and it really slows down understanding the code. Anyways, I realized I wouldn't understand my own code anymore after some years, that's why I spend more time writing comments than code nowadays ... well almost 😉
- There's lots of room for improvement (dynamic core, video memory access, ... haven't looked into more yet). I've made some experimental changes and I got drastic speed increases in certain situations (such that the dynamic core is no longer slower than the normal one 😀 ). Anyways, it's not easy for me to properly do some optimations without the risk of breaking something. I simply lack the overall understanding of the whole code and the rather complex dependencies that have grown. Thus, things will take time so I postpone this into the future.
- forgive me if I wasted your time reading this long post