Atlas - SDL_sysjoystick.c

Home / ext / SDL / src / joystick / dos Lines: 1 | Size: 9967 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_DOS 24 25#include <dpmi.h> /* for __dpmi_regs, __dpmi_int */ 26#include <limits.h> 27#include <pc.h> /* for inportb */ 28 29#include "../SDL_joystick_c.h" 30#include "../SDL_sysjoystick.h" 31 32#define GAMEPORT 0x201 33 34/* Gameport status byte button bits (active low) */ 35#define GAMEPORT_BUTTON1 0x10 /* bit 4 */ 36#define GAMEPORT_BUTTON2 0x20 /* bit 5 */ 37#define GAMEPORT_BUTTON3 0x40 /* bit 6 */ 38#define GAMEPORT_BUTTON4 0x80 /* bit 7 */ 39 40/* Static state for detection */ 41static bool dos_joystick_detected = false; 42static SDL_JoystickID dos_joystick_id = 0; 43static SDL_JoystickID dos_next_instance_id = 1; 44static Uint64 dos_joystick_next_poll_ns = 0; 45#define DOS_JOYSTICK_POLL_INTERVAL_NS SDL_MS_TO_NS(16) /* ~60 Hz is plenty for a 2-axis gameport stick */ 46 47struct joystick_hwdata 48{ 49 int axis_min[2]; /* minimum raw axis values seen */ 50 int axis_max[2]; /* maximum raw axis values seen */ 51 int axis_center[2]; /* center raw axis values (captured on first read) */ 52 bool calibrated; /* whether we've seen enough range */ 53}; 54 55/* 56 * Probe for joystick presence using BIOS INT 15h, function 84h, subfunction 0. 57 * This reads the button state — if the BIOS supports it, a joystick is present. 58 * Returns true if the BIOS call succeeds (carry flag clear). 59 */ 60static bool ProbeGameport(void) 61{ 62 __dpmi_regs regs; 63 SDL_zero(regs); 64 regs.x.ax = 0x8400; /* INT 15h AH=84h */ 65 regs.x.dx = 0x0000; /* subfunction 0: read button state */ 66 __dpmi_int(0x15, &regs); 67 /* Carry flag set = no joystick BIOS support */ 68 return !(regs.x.flags & 0x01); 69} 70 71/* 72 * Read joystick axes using BIOS INT 15h, function 84h, subfunction 1. 73 * Returns calibrated raw values in AX (X axis) and BX (Y axis). 74 * This avoids direct port I/O timing loops — the BIOS handles the 75 * one-shot timer polling internally. 76 */ 77static void ReadGameportAxes(int *axis_x, int *axis_y) 78{ 79 __dpmi_regs regs; 80 SDL_zero(regs); 81 regs.x.ax = 0x8400; /* INT 15h AH=84h */ 82 regs.x.dx = 0x0001; /* subfunction 1: read axis values */ 83 __dpmi_int(0x15, &regs); 84 if (regs.x.flags & 0x01) { 85 /* BIOS call failed */ 86 *axis_x = -1; 87 *axis_y = -1; 88 } else { 89 *axis_x = (int)regs.x.ax; /* joystick 1 X axis */ 90 *axis_y = (int)regs.x.bx; /* joystick 1 Y axis */ 91 } 92} 93 94static Sint16 CalibrateAxis(int raw, struct joystick_hwdata *hwdata, int axis) 95{ 96 int range; 97 int centered; 98 99 if (raw < 0) { 100 return 0; /* axis not connected, report center */ 101 } 102 103 if (raw < hwdata->axis_min[axis]) { 104 hwdata->axis_min[axis] = raw; 105 } 106 if (raw > hwdata->axis_max[axis]) { 107 hwdata->axis_max[axis] = raw; 108 } 109 110 if (!hwdata->calibrated) { 111 hwdata->axis_center[axis] = raw; 112 /* Consider calibrated once we've seen some range on either axis */ 113 if ((hwdata->axis_max[0] - hwdata->axis_min[0]) > 20 || 114 (hwdata->axis_max[1] - hwdata->axis_min[1]) > 20) { 115 hwdata->calibrated = true; 116 } 117 } 118 119 range = hwdata->axis_max[axis] - hwdata->axis_min[axis]; 120 if (range < 10) { 121 range = 10; /* avoid division issues */ 122 } 123 124 /* Map to -32768..32767 */ 125 centered = raw - hwdata->axis_center[axis]; 126 return (Sint16)SDL_clamp(((Sint64)centered * 65535) / range, -32768, 32767); 127} 128 129static bool DOS_JoystickInit(void) 130{ 131 dos_joystick_detected = ProbeGameport(); 132 if (dos_joystick_detected) { 133 dos_joystick_id = dos_next_instance_id++; 134 } 135 return true; 136} 137 138static int DOS_JoystickGetCount(void) 139{ 140 return dos_joystick_detected ? 1 : 0; 141} 142 143static void DOS_JoystickDetect(void) 144{ 145 /* Don't re-probe every frame — ProbeGameport() does a tight loop of up to 146 65536 port reads, which is very expensive and can interfere with SB16 IRQ 147 timing. DOS gameport joysticks are not hot-pluggable anyway. Detection 148 happens once in DOS_JoystickInit(). */ 149} 150 151static bool DOS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) 152{ 153 return false; 154} 155 156static const char *DOS_JoystickGetDeviceName(int device_index) 157{ 158 if (device_index == 0 && dos_joystick_detected) { 159 return "DOS Gameport Joystick"; 160 } 161 return NULL; 162} 163 164static const char *DOS_JoystickGetDevicePath(int device_index) 165{ 166 if (device_index == 0 && dos_joystick_detected) { 167 return "gameport:0x201"; 168 } 169 return NULL; 170} 171 172static int DOS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) 173{ 174 return -1; 175} 176 177static int DOS_JoystickGetDevicePlayerIndex(int device_index) 178{ 179 return -1; 180} 181 182static void DOS_JoystickSetDevicePlayerIndex(int device_index, int player_index) 183{ 184} 185 186static SDL_GUID DOS_JoystickGetDeviceGUID(int device_index) 187{ 188 return SDL_CreateJoystickGUID( 189 SDL_HARDWARE_BUS_UNKNOWN, /* bus */ 190 0x0000, /* vendor */ 191 0x0201, /* product (port number) */ 192 0x0001, /* version */ 193 NULL, /* vendor_name */ 194 "DOS Gameport Joystick", /* product_name */ 195 0, /* driver_signature */ 196 0 /* driver_data */ 197 ); 198} 199 200static SDL_JoystickID DOS_JoystickGetDeviceInstanceID(int device_index) 201{ 202 if (device_index == 0 && dos_joystick_detected) { 203 return dos_joystick_id; 204 } 205 return 0; 206} 207 208static bool DOS_JoystickOpen(SDL_Joystick *joystick, int device_index) 209{ 210 struct joystick_hwdata *hwdata; 211 212 hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); 213 if (!hwdata) { 214 return false; 215 } 216 217 hwdata->axis_min[0] = INT_MAX; 218 hwdata->axis_min[1] = INT_MAX; 219 hwdata->axis_max[0] = 0; 220 hwdata->axis_max[1] = 0; 221 hwdata->axis_center[0] = 0; 222 hwdata->axis_center[1] = 0; 223 hwdata->calibrated = false; 224 225 joystick->hwdata = hwdata; 226 joystick->naxes = 2; 227 joystick->nbuttons = 4; 228 joystick->nhats = 0; 229 joystick->nballs = 0; 230 231 return true; 232} 233 234static bool DOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 235{ 236 return SDL_Unsupported(); 237} 238 239static bool DOS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 240{ 241 return SDL_Unsupported(); 242} 243 244static bool DOS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 245{ 246 return SDL_Unsupported(); 247} 248 249static bool DOS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) 250{ 251 return SDL_Unsupported(); 252} 253 254static bool DOS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) 255{ 256 return SDL_Unsupported(); 257} 258 259static void DOS_JoystickUpdate(SDL_Joystick *joystick) 260{ 261 struct joystick_hwdata *hwdata = joystick->hwdata; 262 int axis_x, axis_y; 263 Uint8 val; 264 Uint64 now; 265 266 if (!hwdata) { 267 return; 268 } 269 270 /* Buttons are a passive port read (no timing loop), always safe to poll */ 271 val = inportb(GAMEPORT); 272 SDL_SendJoystickButton(0, joystick, 0, !(val & GAMEPORT_BUTTON1)); 273 SDL_SendJoystickButton(0, joystick, 1, !(val & GAMEPORT_BUTTON2)); 274 SDL_SendJoystickButton(0, joystick, 2, !(val & GAMEPORT_BUTTON3)); 275 SDL_SendJoystickButton(0, joystick, 3, !(val & GAMEPORT_BUTTON4)); 276 277 /* Throttle axis reads — BIOS INT 15h subfunction 1 does an internal 278 timing loop that is very expensive. ~60 Hz is more than enough for 279 a 2-axis analog gameport stick. */ 280 now = SDL_GetTicksNS(); 281 if (now < dos_joystick_next_poll_ns) { 282 return; 283 } 284 dos_joystick_next_poll_ns = now + DOS_JOYSTICK_POLL_INTERVAL_NS; 285 286 ReadGameportAxes(&axis_x, &axis_y); 287 288 if (axis_x >= 0) { 289 Sint16 cal_x = CalibrateAxis(axis_x, hwdata, 0); 290 SDL_SendJoystickAxis(0, joystick, 0, cal_x); 291 } 292 293 if (axis_y >= 0) { 294 Sint16 cal_y = CalibrateAxis(axis_y, hwdata, 1); 295 SDL_SendJoystickAxis(0, joystick, 1, cal_y); 296 } 297} 298 299static void DOS_JoystickClose(SDL_Joystick *joystick) 300{ 301 if (joystick->hwdata) { 302 SDL_free(joystick->hwdata); 303 joystick->hwdata = NULL; 304 } 305} 306 307static void DOS_JoystickQuit(void) 308{ 309 dos_joystick_detected = false; 310 dos_joystick_id = 0; 311} 312 313static bool DOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) 314{ 315 return false; 316} 317 318SDL_JoystickDriver SDL_DOS_JoystickDriver = { 319 DOS_JoystickInit, 320 DOS_JoystickGetCount, 321 DOS_JoystickDetect, 322 DOS_JoystickIsDevicePresent, 323 DOS_JoystickGetDeviceName, 324 DOS_JoystickGetDevicePath, 325 DOS_JoystickGetDeviceSteamVirtualGamepadSlot, 326 DOS_JoystickGetDevicePlayerIndex, 327 DOS_JoystickSetDevicePlayerIndex, 328 DOS_JoystickGetDeviceGUID, 329 DOS_JoystickGetDeviceInstanceID, 330 DOS_JoystickOpen, 331 DOS_JoystickRumble, 332 DOS_JoystickRumbleTriggers, 333 DOS_JoystickSetLED, 334 DOS_JoystickSendEffect, 335 DOS_JoystickSetSensorsEnabled, 336 DOS_JoystickUpdate, 337 DOS_JoystickClose, 338 DOS_JoystickQuit, 339 DOS_JoystickGetGamepadMapping 340}; 341 342#endif /* SDL_JOYSTICK_DOS */ 343
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.