Skip to content

Commit

Permalink
INT 33h: Games like Lemmings do things with INT 33h that confuse the …
Browse files Browse the repository at this point in the history
…auto-range detction code. Rather than make the code complicated and convoluted, add dosbox.conf options to manually specify ranges and other INT 33h adjustments.
  • Loading branch information
joncampbell123 committed Mar 26, 2024
1 parent 2ee49dd commit 61e4460
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 4 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
Next:
- INT 33 mouse emulation: Add dosbox.conf options to force a specific
coordinate system for the DOS game with respect to host/guest mouse
cursor integration (getting the DOS cursor to match the host cursor
within the window). Add dosbox.conf option where additional adjustments
are available. The maximum x/y and max-adjust options are ideal for
Lemmings 2, which uses INT 33h in a way that confuses the automatic
range detection code used to synchronize cursor position and therefore
needs this option, and the first Lemmings as well. (joncampbell123).
- PIT timer: Fix restart_counter(), the delay computation was completely
backwards. This is particularly important where it concerns programs
that use the PIT to detect the rate at which the Pentium time stamp
Expand Down
16 changes: 16 additions & 0 deletions src/dosbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4564,6 +4564,22 @@ void DOSBOX_SetupConfigSections(void) {
Pbool->Set_help("Enable INT 33H for mouse support.");
Pbool->SetBasic(true);

Pint = secprop->Add_int("int33 max x", Property::Changeable::WhenIdle, 0);
Pint->Set_help("For guest to host cursor integration with DOS games, assume the maximum reported X coordinate has this value, if this is nonzero.\n"
"Use this option if the automatic range detection fails to work correctly with the DOS game and the cursor doesn't match the host cursor position.\n"
"- Lemmings 2: Use X=640 Y=400");

Pint = secprop->Add_int("int33 max y", Property::Changeable::WhenIdle, 0);
Pint->Set_help("For guest to host cursor integration with DOS games, assume the maximum reported Y coordinate has this value, if this is nonzero.\n"
"Use this option if the automatic range detection fails to work correctly with the DOS game and the cursor doesn't match the host cursor position.\n"
"- See the max X setting for a list of games");

Pstring = secprop->Add_string("int33 xy adjust",Property::Changeable::OnlyAtStart,"");
Pstring->Set_help("A list of adjustments to INT 33h position adjustments to help align the guest cursor to the host. name=value, comma separated\n"
"x=spec, y=spec\n"
"spec: <number> position adjust, can be positive or negative\n"
" max-excess if game sets maximum larger than int33 max x/y, adjust the position forward by the difference");

Pint = secprop->Add_int("mouse report rate",Property::Changeable::WhenIdle,0);
Pint->Set_help("Mouse reporting rate, or 0 for auto. This affects how often mouse events are reported to the guest through the mouse interrupt.\n"
"Some games including CyClone need a lower reporting rate to function correctly. Auto mode allows the guest to change the report rate through the PS/2 mouse emulation.\n"
Expand Down
85 changes: 81 additions & 4 deletions src/ints/mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
#endif

static bool adjust_x_max_excess = false,adjust_y_max_excess = false;
static unsigned int assume_max_x = 0,assume_max_y = 0;
static int adjust_x = 0,adjust_y = 0;

#define VMWARE_PORT 0x5658u // communication port
#define VMWARE_PORTHB 0x5659u // communication port, high bandwidth
static Bitu PortRead(Bitu port, Bitu iolen);
Expand Down Expand Up @@ -895,6 +899,29 @@ void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) {
mouse.x = x1 * mouse.max_screen_x;
mouse.y = y1 * mouse.max_screen_y;

/* Cursor adjustment to help in-game cursor match host:
*
* x=[-]adjustment i.e. x=5 or x=-8
* y=[-]adjustment i.e. y=8 or y=-10 */
mouse.x += adjust_x;
mouse.y += adjust_y;
/* When used with int33 max x/y:
* max-excess can be specified to state that if the game set the maximum
* past the dosbox.conf max value, that the x/y coordinates are adjusted
* by the difference, for use with games that make use of the min/max
* constraint per screen and offset the min/max by some offset (Lemmings 2).
*
* x=max-excess
* y=max-excess */
if (adjust_x_max_excess) {
if (mouse.max_x > (int)assume_max_x)
mouse.x += (mouse.max_x - assume_max_x);
}
if (adjust_y_max_excess) {
if (mouse.max_y > (int)assume_max_y)
mouse.y += (mouse.max_y - assume_max_y);
}

if (mouse.x < mouse.min_x)
mouse.x = mouse.min_x;
if (mouse.y < mouse.min_y)
Expand Down Expand Up @@ -1455,8 +1482,15 @@ void Mouse_AfterNewVideoMode(bool setmode) {
mouse.cursorType = 0;
mouse.enabled=true;

mouse.max_screen_x = mouse.max_x;
mouse.max_screen_y = mouse.max_y;
if (assume_max_x > 0)
mouse.max_screen_x = assume_max_x;
else
mouse.max_screen_x = mouse.max_x;

if (assume_max_y > 0)
mouse.max_screen_y = assume_max_y;
else
mouse.max_screen_y = mouse.max_y;
}

//Much too empty, Mouse_NewVideoMode contains stuff that should be in here
Expand Down Expand Up @@ -1650,7 +1684,7 @@ static Bitu INT33_Handler(void) {
* set after mode set is the only way to make sure mouse pointer integration
* tracks the guest pointer properly. */
if (mouse.first_range_setx || mouse.buttons == 0) {
if (mouse.min_x == 0 && mouse.max_x > 0) {
if (mouse.min_x == 0 && mouse.max_x > 0 && assume_max_x == 0) {
// most games redefine the range so they can use a saner range matching the screen
int16_t nval = mouse.max_x;

Expand Down Expand Up @@ -1719,7 +1753,7 @@ static Bitu INT33_Handler(void) {
* set after mode set is the only way to make sure mouse pointer integration
* tracks the guest pointer properly. */
if (mouse.first_range_sety || mouse.buttons == 0) {
if (mouse.min_y == 0 && mouse.max_y > 0) {
if (mouse.min_y == 0 && mouse.max_y > 0 && assume_max_y == 0) {
// most games redefine the range so they can use a saner range matching the screen
int16_t nval = mouse.max_y;

Expand Down Expand Up @@ -2316,6 +2350,49 @@ void MOUSE_Startup(Section *sec) {
user_mouse_report_rate=section->Get_int("mouse report rate");
UpdateMouseReportRate();

assume_max_x=section->Get_int("int33 max x");
assume_max_y=section->Get_int("int33 max y");

{
const char *s = section->Get_string("int33 xy adjust");
while (*s != 0) {
if (*s == ' ') {
s++;
}
else {
const char *base = s;
while (*s != 0 && *s != ',' && *s != '=') s++;
std::string name = std::string(base,(size_t)(s-base)),value;
if (*s == '=') {
s++;
base = s;
while (*s != 0 && *s != ',') s++;
value = std::string(base,(size_t)(s-base));
}
if (*s == ',') s++;

if (name == "x") {
const char *v = value.c_str();
if (isdigit(*v) || (*v == '-' && isdigit(v[1]))) {
adjust_x = atoi(v);
}
else if (!strcmp(v,"max-excess")) {
adjust_x_max_excess = true;
}
}
else if (name == "y") {
const char *v = value.c_str();
if (isdigit(*v) || (*v == '-' && isdigit(v[1]))) {
adjust_y = atoi(v);
}
else if (!strcmp(v,"max-excess")) {
adjust_y_max_excess = true;
}
}
}
}
}

en_int33_hide_if_intsub=section->Get_bool("int33 hide host cursor if interrupt subroutine");

en_int33_hide_if_polling=section->Get_bool("int33 hide host cursor when polling");
Expand Down

0 comments on commit 61e4460

Please sign in to comment.