1 /*
2 * Copyright (C) 2002-2015 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19
20 #include <string.h>
21 #include <math.h>
22
23
24 #include "dosbox.h"
25 #include "callback.h"
26 #include "mem.h"
27 #include "regs.h"
28 #include "cpu.h"
29 #include "mouse.h"
30 #include "pic.h"
31 #include "inout.h"
32 #include "int10.h"
33 #include "bios.h"
34 #include "dos_inc.h"
35
36 static Bitu call_int33,call_int74,int74_ret_callback,call_mouse_bd;
37 static Bit16u ps2cbseg,ps2cbofs;
38 static bool useps2callback,ps2callbackinit;
39 static Bitu call_ps2;
40 static RealPt ps2_callback;
41 static Bit16s oldmouseX, oldmouseY;
42 // forward
43 void WriteMouseIntVector(void);
44
45 struct button_event {
46 Bit8u type;
47 Bit8u buttons;
48 };
49
50 #define QUEUE_SIZE 32
51 #define MOUSE_BUTTONS 3
52 #define MOUSE_IRQ 12
53 #define POS_X (static_cast<Bit16s>(mouse.x) & mouse.gran_x)
54 #define POS_Y (static_cast<Bit16s>(mouse.y) & mouse.gran_y)
55
56 #define CURSORX 16
57 #define CURSORY 16
58 #define HIGHESTBIT (1<<(CURSORX-1))
59
60 static Bit16u defaultTextAndMask = 0x77FF;
61 static Bit16u defaultTextXorMask = 0x7700;
62
63 static Bit16u defaultScreenMask[CURSORY] = {
64 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
65 0x03FF, 0x01FF, 0x00FF, 0x007F,
66 0x003F, 0x001F, 0x01FF, 0x00FF,
67 0x30FF, 0xF87F, 0xF87F, 0xFCFF
68 };
69
70 static Bit16u defaultCursorMask[CURSORY] = {
71 0x0000, 0x4000, 0x6000, 0x7000,
72 0x7800, 0x7C00, 0x7E00, 0x7F00,
73 0x7F80, 0x7C00, 0x6C00, 0x4600,
74 0x0600, 0x0300, 0x0300, 0x0000
75 };
76
77 static Bit16u userdefScreenMask[CURSORY];
78 static Bit16u userdefCursorMask[CURSORY];
79
80 static struct {
81 Bit8u buttons;
82 Bit16u times_pressed[MOUSE_BUTTONS];
83 Bit16u times_released[MOUSE_BUTTONS];
84 Bit16u last_released_x[MOUSE_BUTTONS];
85 Bit16u last_released_y[MOUSE_BUTTONS];
86 Bit16u last_pressed_x[MOUSE_BUTTONS];
87 Bit16u last_pressed_y[MOUSE_BUTTONS];
88 Bit16u hidden;
89 float add_x,add_y;
90 Bit16s min_x,max_x,min_y,max_y;
91 float mickey_x,mickey_y;
92 float x,y;
93 button_event event_queue[QUEUE_SIZE];
94 Bit8u events;//Increase if QUEUE_SIZE >255 (currently 32)
95 Bit16u sub_seg,sub_ofs;
96 Bit16u sub_mask;
97
98 bool background;
99 Bit16s backposx, backposy;
100 Bit8u backData[CURSORX*CURSORY];
101 Bit16u* screenMask;
102 Bit16u* cursorMask;
103 Bit16s clipx,clipy;
104 Bit16s hotx,hoty;
105 Bit16u textAndMask, textXorMask;
106
107 float mickeysPerPixel_x;
108 float mickeysPerPixel_y;
109 float pixelPerMickey_x;
110 float pixelPerMickey_y;
111 Bit16u senv_x_val;
112 Bit16u senv_y_val;
113 Bit16u dspeed_val;
114 float senv_x;
115 float senv_y;
116 Bit16u updateRegion_x[2];
117 Bit16u updateRegion_y[2];
118 Bit16u doubleSpeedThreshold;
119 Bit16u language;
120 Bit16u cursorType;
121 Bit16u oldhidden;
122 Bit8u page;
123 bool enabled;
124 bool inhibit_draw;
125 bool timer_in_progress;
126 bool in_UIR;
127 Bit8u mode;
128 Bit16s gran_x,gran_y;
129 } mouse;
130
Mouse_SetPS2State(bool use)131 bool Mouse_SetPS2State(bool use) {
132 if (use && (!ps2callbackinit)) {
133 useps2callback = false;
134 PIC_SetIRQMask(MOUSE_IRQ,true);
135 return false;
136 }
137 useps2callback = use;
138 PIC_SetIRQMask(MOUSE_IRQ,!useps2callback);
139 return true;
140 }
141
Mouse_ChangePS2Callback(Bit16u pseg,Bit16u pofs)142 void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs) {
143 if ((pseg==0) && (pofs==0)) {
144 ps2callbackinit = false;
145 } else {
146 ps2callbackinit = true;
147 ps2cbseg = pseg;
148 ps2cbofs = pofs;
149 }
150 }
151
DoPS2Callback(Bit16u data,Bit16s mouseX,Bit16s mouseY)152 void DoPS2Callback(Bit16u data, Bit16s mouseX, Bit16s mouseY) {
153 if (useps2callback) {
154 Bit16u mdat = (data & 0x03) | 0x08;
155 Bit16s xdiff = mouseX-oldmouseX;
156 Bit16s ydiff = oldmouseY-mouseY;
157 oldmouseX = mouseX;
158 oldmouseY = mouseY;
159 if ((xdiff>0xff) || (xdiff<-0xff)) mdat |= 0x40; // x overflow
160 if ((ydiff>0xff) || (ydiff<-0xff)) mdat |= 0x80; // y overflow
161 xdiff %= 256;
162 ydiff %= 256;
163 if (xdiff<0) {
164 xdiff = (0x100+xdiff);
165 mdat |= 0x10;
166 }
167 if (ydiff<0) {
168 ydiff = (0x100+ydiff);
169 mdat |= 0x20;
170 }
171 CPU_Push16((Bit16u)mdat);
172 CPU_Push16((Bit16u)(xdiff % 256));
173 CPU_Push16((Bit16u)(ydiff % 256));
174 CPU_Push16((Bit16u)0);
175 CPU_Push16(RealSeg(ps2_callback));
176 CPU_Push16(RealOff(ps2_callback));
177 SegSet16(cs, ps2cbseg);
178 reg_ip = ps2cbofs;
179 }
180 }
181
PS2_Handler(void)182 Bitu PS2_Handler(void) {
183 CPU_Pop16();CPU_Pop16();CPU_Pop16();CPU_Pop16();// remove the 4 words
184 return CBRET_NONE;
185 }
186
187
188 #define X_MICKEY 8
189 #define Y_MICKEY 8
190
191 #define MOUSE_HAS_MOVED 1
192 #define MOUSE_LEFT_PRESSED 2
193 #define MOUSE_LEFT_RELEASED 4
194 #define MOUSE_RIGHT_PRESSED 8
195 #define MOUSE_RIGHT_RELEASED 16
196 #define MOUSE_MIDDLE_PRESSED 32
197 #define MOUSE_MIDDLE_RELEASED 64
198 #define MOUSE_DELAY 5.0
199
MOUSE_Limit_Events(Bitu)200 void MOUSE_Limit_Events(Bitu /*val*/) {
201 mouse.timer_in_progress = false;
202 if (mouse.events) {
203 mouse.timer_in_progress = true;
204 PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
205 PIC_ActivateIRQ(MOUSE_IRQ);
206 }
207 }
208
Mouse_AddEvent(Bit8u type)209 INLINE void Mouse_AddEvent(Bit8u type) {
210 if (mouse.events<QUEUE_SIZE) {
211 if (mouse.events>0) {
212 /* Skip duplicate events */
213 if (type==MOUSE_HAS_MOVED) return;
214 /* Always put the newest element in the front as that the events are
215 * handled backwards (prevents doubleclicks while moving)
216 */
217 for(Bitu i = mouse.events ; i ; i--)
218 mouse.event_queue[i] = mouse.event_queue[i-1];
219 }
220 mouse.event_queue[0].type=type;
221 mouse.event_queue[0].buttons=mouse.buttons;
222 mouse.events++;
223 }
224 if (!mouse.timer_in_progress) {
225 mouse.timer_in_progress = true;
226 PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
227 PIC_ActivateIRQ(MOUSE_IRQ);
228 }
229 }
230
231 // ***************************************************************************
232 // Mouse cursor - text mode
233 // ***************************************************************************
234 /* Write and read directly to the screen. Do no use int_setcursorpos (LOTUS123) */
235 extern void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr);
236 extern void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result);
237
RestoreCursorBackgroundText()238 void RestoreCursorBackgroundText() {
239 if (mouse.hidden || mouse.inhibit_draw) return;
240
241 if (mouse.background) {
242 WriteChar(mouse.backposx,mouse.backposy,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE),mouse.backData[0],mouse.backData[1],true);
243 mouse.background = false;
244 }
245 }
246
DrawCursorText()247 void DrawCursorText() {
248 // Restore Background
249 RestoreCursorBackgroundText();
250
251
252 // Save Background
253 mouse.backposx = POS_X>>3;
254 mouse.backposy = POS_Y>>3;
255 if (mouse.mode < 2) mouse.backposx >>= 1;
256
257 //use current page (CV program)
258 Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
259
260 if (mouse.cursorType == 0) {
261 Bit16u result;
262 ReadCharAttr(mouse.backposx,mouse.backposy,page,&result);
263 mouse.backData[0] = (Bit8u)(result & 0xFF);
264 mouse.backData[1] = (Bit8u)(result>>8);
265 mouse.background = true;
266 // Write Cursor
267 result = (result & mouse.textAndMask) ^ mouse.textXorMask;
268 WriteChar(mouse.backposx,mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true);
269 } else {
270 Bit16u address=page * real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
271 address += (mouse.backposy * real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS) + mouse.backposx) * 2;
272 address /= 2;
273 Bit16u cr = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
274 IO_Write(cr , 0xe);
275 IO_Write(cr + 1, (address>>8) & 0xff);
276 IO_Write(cr , 0xf);
277 IO_Write(cr + 1, address & 0xff);
278 }
279 }
280
281 // ***************************************************************************
282 // Mouse cursor - graphic mode
283 // ***************************************************************************
284
285 static Bit8u gfxReg3CE[9];
286 static Bit8u index3C4,gfxReg3C5;
SaveVgaRegisters()287 void SaveVgaRegisters() {
288 if (IS_VGA_ARCH) {
289 for (Bit8u i=0; i<9; i++) {
290 IO_Write (0x3CE,i);
291 gfxReg3CE[i] = IO_Read(0x3CF);
292 }
293 /* Setup some default values in GFX regs that should work */
294 IO_Write(0x3CE,3); IO_Write(0x3Cf,0); //disable rotate and operation
295 IO_Write(0x3CE,5); IO_Write(0x3Cf,gfxReg3CE[5]&0xf0); //Force read/write mode 0
296
297 //Set Map to all planes. Celtic Tales
298 index3C4 = IO_Read(0x3c4); IO_Write(0x3C4,2);
299 gfxReg3C5 = IO_Read(0x3C5); IO_Write(0x3C5,0xF);
300 } else if (machine==MCH_EGA) {
301 //Set Map to all planes.
302 IO_Write(0x3C4,2);
303 IO_Write(0x3C5,0xF);
304 }
305 }
306
RestoreVgaRegisters()307 void RestoreVgaRegisters() {
308 if (IS_VGA_ARCH) {
309 for (Bit8u i=0; i<9; i++) {
310 IO_Write(0x3CE,i);
311 IO_Write(0x3CF,gfxReg3CE[i]);
312 }
313
314 IO_Write(0x3C4,2);
315 IO_Write(0x3C5,gfxReg3C5);
316 IO_Write(0x3C4,index3C4);
317 }
318 }
319
ClipCursorArea(Bit16s & x1,Bit16s & x2,Bit16s & y1,Bit16s & y2,Bit16u & addx1,Bit16u & addx2,Bit16u & addy)320 void ClipCursorArea(Bit16s& x1, Bit16s& x2, Bit16s& y1, Bit16s& y2,
321 Bit16u& addx1, Bit16u& addx2, Bit16u& addy) {
322 addx1 = addx2 = addy = 0;
323 // Clip up
324 if (y1<0) {
325 addy += (-y1);
326 y1 = 0;
327 }
328 // Clip down
329 if (y2>mouse.clipy) {
330 y2 = mouse.clipy;
331 };
332 // Clip left
333 if (x1<0) {
334 addx1 += (-x1);
335 x1 = 0;
336 };
337 // Clip right
338 if (x2>mouse.clipx) {
339 addx2 = x2 - mouse.clipx;
340 x2 = mouse.clipx;
341 };
342 }
343
RestoreCursorBackground()344 void RestoreCursorBackground() {
345 if (mouse.hidden || mouse.inhibit_draw) return;
346
347 SaveVgaRegisters();
348 if (mouse.background) {
349 // Restore background
350 Bit16s x,y;
351 Bit16u addx1,addx2,addy;
352 Bit16u dataPos = 0;
353 Bit16s x1 = mouse.backposx;
354 Bit16s y1 = mouse.backposy;
355 Bit16s x2 = x1 + CURSORX - 1;
356 Bit16s y2 = y1 + CURSORY - 1;
357
358 ClipCursorArea(x1, x2, y1, y2, addx1, addx2, addy);
359
360 dataPos = addy * CURSORX;
361 for (y=y1; y<=y2; y++) {
362 dataPos += addx1;
363 for (x=x1; x<=x2; x++) {
364 INT10_PutPixel(x,y,mouse.page,mouse.backData[dataPos++]);
365 };
366 dataPos += addx2;
367 };
368 mouse.background = false;
369 };
370 RestoreVgaRegisters();
371 }
372
DrawCursor()373 void DrawCursor() {
374 if (mouse.hidden || mouse.inhibit_draw) return;
375 INT10_SetCurMode();
376 // In Textmode ?
377 if (CurMode->type==M_TEXT) {
378 DrawCursorText();
379 return;
380 }
381
382 // Check video page. Seems to be ignored for text mode.
383 // hence the text mode handled above this
384 if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)!=mouse.page) return;
385 // Check if cursor in update region
386 /* if ((POS_X >= mouse.updateRegion_x[0]) && (POS_X <= mouse.updateRegion_x[1]) &&
387 (POS_Y >= mouse.updateRegion_y[0]) && (POS_Y <= mouse.updateRegion_y[1])) {
388 if (CurMode->type==M_TEXT16)
389 RestoreCursorBackgroundText();
390 else
391 RestoreCursorBackground();
392 mouse.shown--;
393 return;
394 }
395 */ /*Not sure yet what to do update region should be set to ??? */
396
397 // Get Clipping ranges
398
399
400 mouse.clipx = (Bit16s)((Bits)CurMode->swidth-1); /* Get from bios ? */
401 mouse.clipy = (Bit16s)((Bits)CurMode->sheight-1);
402
403 /* might be vidmode == 0x13?2:1 */
404 Bit16s xratio = 640;
405 if (CurMode->swidth>0) xratio/=CurMode->swidth;
406 if (xratio==0) xratio = 1;
407
408 RestoreCursorBackground();
409
410 SaveVgaRegisters();
411
412 // Save Background
413 Bit16s x,y;
414 Bit16u addx1,addx2,addy;
415 Bit16u dataPos = 0;
416 Bit16s x1 = POS_X / xratio - mouse.hotx;
417 Bit16s y1 = POS_Y - mouse.hoty;
418 Bit16s x2 = x1 + CURSORX - 1;
419 Bit16s y2 = y1 + CURSORY - 1;
420
421 ClipCursorArea(x1,x2,y1,y2, addx1, addx2, addy);
422
423 dataPos = addy * CURSORX;
424 for (y=y1; y<=y2; y++) {
425 dataPos += addx1;
426 for (x=x1; x<=x2; x++) {
427 INT10_GetPixel(x,y,mouse.page,&mouse.backData[dataPos++]);
428 };
429 dataPos += addx2;
430 };
431 mouse.background= true;
432 mouse.backposx = POS_X / xratio - mouse.hotx;
433 mouse.backposy = POS_Y - mouse.hoty;
434
435 // Draw Mousecursor
436 dataPos = addy * CURSORX;
437 for (y=y1; y<=y2; y++) {
438 Bit16u scMask = mouse.screenMask[addy+y-y1];
439 Bit16u cuMask = mouse.cursorMask[addy+y-y1];
440 if (addx1>0) { scMask<<=addx1; cuMask<<=addx1; dataPos += addx1; };
441 for (x=x1; x<=x2; x++) {
442 Bit8u pixel = 0;
443 // ScreenMask
444 if (scMask & HIGHESTBIT) pixel = mouse.backData[dataPos];
445 scMask<<=1;
446 // CursorMask
447 if (cuMask & HIGHESTBIT) pixel = pixel ^ 0x0F;
448 cuMask<<=1;
449 // Set Pixel
450 INT10_PutPixel(x,y,mouse.page,pixel);
451 dataPos++;
452 };
453 dataPos += addx2;
454 };
455 RestoreVgaRegisters();
456 }
457
Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate)458 void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) {
459 float dx = xrel * mouse.pixelPerMickey_x;
460 float dy = yrel * mouse.pixelPerMickey_y;
461
462 if((fabs(xrel) > 1.0) || (mouse.senv_x < 1.0)) dx *= mouse.senv_x;
463 if((fabs(yrel) > 1.0) || (mouse.senv_y < 1.0)) dy *= mouse.senv_y;
464 if (useps2callback) dy *= 2;
465
466 mouse.mickey_x += (dx * mouse.mickeysPerPixel_x);
467 mouse.mickey_y += (dy * mouse.mickeysPerPixel_y);
468 if (mouse.mickey_x >= 32768.0) mouse.mickey_x -= 65536.0;
469 else if (mouse.mickey_x <= -32769.0) mouse.mickey_x += 65536.0;
470 if (mouse.mickey_y >= 32768.0) mouse.mickey_y -= 65536.0;
471 else if (mouse.mickey_y <= -32769.0) mouse.mickey_y += 65536.0;
472 if (emulate) {
473 mouse.x += dx;
474 mouse.y += dy;
475 } else {
476 if (CurMode->type == M_TEXT) {
477 mouse.x = x*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8;
478 mouse.y = y*(real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1)*8;
479 } else if ((mouse.max_x < 2048) || (mouse.max_y < 2048) || (mouse.max_x != mouse.max_y)) {
480 if ((mouse.max_x > 0) && (mouse.max_y > 0)) {
481 mouse.x = x*mouse.max_x;
482 mouse.y = y*mouse.max_y;
483 } else {
484 mouse.x += xrel;
485 mouse.y += yrel;
486 }
487 } else { // Games faking relative movement through absolute coordinates. Quite surprising that this actually works..
488 mouse.x += xrel;
489 mouse.y += yrel;
490 }
491 }
492
493 /* ignore constraints if using PS2 mouse callback in the bios */
494
495 if (!useps2callback) {
496 if (mouse.x > mouse.max_x) mouse.x = mouse.max_x;
497 if (mouse.x < mouse.min_x) mouse.x = mouse.min_x;
498 if (mouse.y > mouse.max_y) mouse.y = mouse.max_y;
499 if (mouse.y < mouse.min_y) mouse.y = mouse.min_y;
500 } else {
501 if (mouse.x >= 32768.0) mouse.x -= 65536.0;
502 else if (mouse.x <= -32769.0) mouse.x += 65536.0;
503 if (mouse.y >= 32768.0) mouse.y -= 65536.0;
504 else if (mouse.y <= -32769.0) mouse.y += 65536.0;
505 }
506 Mouse_AddEvent(MOUSE_HAS_MOVED);
507 DrawCursor();
508 }
509
Mouse_CursorSet(float x,float y)510 void Mouse_CursorSet(float x,float y) {
511 mouse.x=x;
512 mouse.y=y;
513 DrawCursor();
514 }
515
Mouse_ButtonPressed(Bit8u button)516 void Mouse_ButtonPressed(Bit8u button) {
517 switch (button) {
518 #if (MOUSE_BUTTONS >= 1)
519 case 0:
520 mouse.buttons|=1;
521 Mouse_AddEvent(MOUSE_LEFT_PRESSED);
522 break;
523 #endif
524 #if (MOUSE_BUTTONS >= 2)
525 case 1:
526 mouse.buttons|=2;
527 Mouse_AddEvent(MOUSE_RIGHT_PRESSED);
528 break;
529 #endif
530 #if (MOUSE_BUTTONS >= 3)
531 case 2:
532 mouse.buttons|=4;
533 Mouse_AddEvent(MOUSE_MIDDLE_PRESSED);
534 break;
535 #endif
536 default:
537 return;
538 }
539 mouse.times_pressed[button]++;
540 mouse.last_pressed_x[button]=POS_X;
541 mouse.last_pressed_y[button]=POS_Y;
542 }
543
Mouse_ButtonReleased(Bit8u button)544 void Mouse_ButtonReleased(Bit8u button) {
545 switch (button) {
546 #if (MOUSE_BUTTONS >= 1)
547 case 0:
548 mouse.buttons&=~1;
549 Mouse_AddEvent(MOUSE_LEFT_RELEASED);
550 break;
551 #endif
552 #if (MOUSE_BUTTONS >= 2)
553 case 1:
554 mouse.buttons&=~2;
555 Mouse_AddEvent(MOUSE_RIGHT_RELEASED);
556 break;
557 #endif
558 #if (MOUSE_BUTTONS >= 3)
559 case 2:
560 mouse.buttons&=~4;
561 Mouse_AddEvent(MOUSE_MIDDLE_RELEASED);
562 break;
563 #endif
564 default:
565 return;
566 }
567 mouse.times_released[button]++;
568 mouse.last_released_x[button]=POS_X;
569 mouse.last_released_y[button]=POS_Y;
570 }
571
Mouse_SetMickeyPixelRate(Bit16s px,Bit16s py)572 static void Mouse_SetMickeyPixelRate(Bit16s px, Bit16s py){
573 if ((px!=0) && (py!=0)) {
574 mouse.mickeysPerPixel_x = (float)px/X_MICKEY;
575 mouse.mickeysPerPixel_y = (float)py/Y_MICKEY;
576 mouse.pixelPerMickey_x = X_MICKEY/(float)px;
577 mouse.pixelPerMickey_y = Y_MICKEY/(float)py;
578 }
579 }
580
Mouse_SetSensitivity(Bit16u px,Bit16u py,Bit16u dspeed)581 static void Mouse_SetSensitivity(Bit16u px, Bit16u py, Bit16u dspeed){
582 if(px>100) px=100;
583 if(py>100) py=100;
584 if(dspeed>100) dspeed=100;
585 // save values
586 mouse.senv_x_val=px;
587 mouse.senv_y_val=py;
588 mouse.dspeed_val=dspeed;
589 if ((px!=0) && (py!=0)) {
590 px--; //Inspired by cutemouse
591 py--; //Although their cursor update routine is far more complex then ours
592 mouse.senv_x=(static_cast<float>(px)*px)/3600.0f +1.0f/3.0f;
593 mouse.senv_y=(static_cast<float>(py)*py)/3600.0f +1.0f/3.0f;
594 }
595 }
596
597
Mouse_ResetHardware(void)598 static void Mouse_ResetHardware(void){
599 PIC_SetIRQMask(MOUSE_IRQ,false);
600 }
601
Mouse_BeforeNewVideoMode(bool setmode)602 void Mouse_BeforeNewVideoMode(bool setmode) {
603 if (CurMode->type!=M_TEXT) RestoreCursorBackground();
604 else RestoreCursorBackgroundText();
605 mouse.hidden = 1;
606 mouse.oldhidden = 1;
607 mouse.background = false;
608 }
609
610 //Does way to much. Many things should be moved to mouse reset one day
Mouse_AfterNewVideoMode(bool setmode)611 void Mouse_AfterNewVideoMode(bool setmode) {
612 mouse.inhibit_draw = false;
613 /* Get the correct resolution from the current video mode */
614 Bit8u mode = mem_readb(BIOS_VIDEO_MODE);
615 if (setmode && mode == mouse.mode) LOG(LOG_MOUSE,LOG_NORMAL)("New video mode is the same as the old");
616 mouse.gran_x = (Bit16s)0xffff;
617 mouse.gran_y = (Bit16s)0xffff;
618 switch (mode) {
619 case 0x00:
620 case 0x01:
621 case 0x02:
622 case 0x03:
623 case 0x07: {
624 mouse.gran_x = (mode<2)?0xfff0:0xfff8;
625 mouse.gran_y = (Bit16s)0xfff8;
626 Bitu rows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS);
627 if ((rows == 0) || (rows > 250)) rows = 25 - 1;
628 mouse.max_y = 8*(rows+1) - 1;
629 break;
630 }
631 case 0x04:
632 case 0x05:
633 case 0x06:
634 case 0x08:
635 case 0x09:
636 case 0x0a:
637 case 0x0d:
638 case 0x0e:
639 case 0x13:
640 if (mode == 0x0d || mode == 0x13) mouse.gran_x = (Bit16s)0xfffe;
641 mouse.max_y = 199;
642 break;
643 case 0x0f:
644 case 0x10:
645 mouse.max_y = 349;
646 break;
647 case 0x11:
648 case 0x12:
649 mouse.max_y = 479;
650 break;
651 default:
652 LOG(LOG_MOUSE,LOG_ERROR)("Unhandled videomode %X on reset",mode);
653 mouse.inhibit_draw = true;
654 return;
655 }
656 mouse.mode = mode;
657 mouse.max_x = 639;
658 mouse.min_x = 0;
659 mouse.min_y = 0;
660
661 mouse.events = 0;
662 mouse.timer_in_progress = false;
663 PIC_RemoveEvents(MOUSE_Limit_Events);
664
665 mouse.hotx = 0;
666 mouse.hoty = 0;
667 mouse.screenMask = defaultScreenMask;
668 mouse.cursorMask = defaultCursorMask;
669 mouse.textAndMask= defaultTextAndMask;
670 mouse.textXorMask= defaultTextXorMask;
671 mouse.language = 0;
672 mouse.page = 0;
673 mouse.doubleSpeedThreshold = 64;
674 mouse.updateRegion_x[0] = 1;
675 mouse.updateRegion_y[0] = 1;
676 mouse.updateRegion_x[1] = 1;
677 mouse.updateRegion_y[1] = 1;
678 mouse.cursorType = 0; //Test
679 mouse.enabled=true;
680
681 oldmouseX = static_cast<Bit16s>(mouse.x);
682 oldmouseY = static_cast<Bit16s>(mouse.y);
683
684
685 }
686
687 //Much too empty, Mouse_NewVideoMode contains stuff that should be in here
Mouse_Reset(void)688 static void Mouse_Reset(void) {
689 Mouse_BeforeNewVideoMode(false);
690 Mouse_AfterNewVideoMode(false);
691 Mouse_SetMickeyPixelRate(8,16);
692
693 mouse.mickey_x = 0;
694 mouse.mickey_y = 0;
695
696 // Dont set max coordinates here. it is done by SetResolution!
697 mouse.x = static_cast<float>((mouse.max_x + 1)/ 2);
698 mouse.y = static_cast<float>((mouse.max_y + 1)/ 2);
699 mouse.sub_mask = 0;
700 mouse.in_UIR = false;
701 }
702
INT33_Handler(void)703 static Bitu INT33_Handler(void) {
704 // LOG(LOG_MOUSE,LOG_NORMAL)("MOUSE: %04X %X %X %d %d",reg_ax,reg_bx,reg_cx,POS_X,POS_Y);
705 switch (reg_ax) {
706 case 0x00: /* Reset Driver and Read Status */
707 Mouse_ResetHardware(); /* fallthrough */
708 case 0x21: /* Software Reset */
709 reg_ax=0xffff;
710 reg_bx=MOUSE_BUTTONS;
711 Mouse_Reset();
712 break;
713 case 0x01: /* Show Mouse */
714 if(mouse.hidden) mouse.hidden--;
715 DrawCursor();
716 break;
717 case 0x02: /* Hide Mouse */
718 {
719 if (CurMode->type!=M_TEXT) RestoreCursorBackground();
720 else RestoreCursorBackgroundText();
721 mouse.hidden++;
722 }
723 break;
724 case 0x03: /* Return position and Button Status */
725 reg_bx=mouse.buttons;
726 reg_cx=POS_X;
727 reg_dx=POS_Y;
728 break;
729 case 0x04: /* Position Mouse */
730 /* If position isn't different from current position
731 * don't change it then. (as position is rounded so numbers get
732 * lost when the rounded number is set) (arena/simulation Wolf) */
733 if ((Bit16s)reg_cx >= mouse.max_x) mouse.x = static_cast<float>(mouse.max_x);
734 else if (mouse.min_x >= (Bit16s)reg_cx) mouse.x = static_cast<float>(mouse.min_x);
735 else if ((Bit16s)reg_cx != POS_X) mouse.x = static_cast<float>(reg_cx);
736
737 if ((Bit16s)reg_dx >= mouse.max_y) mouse.y = static_cast<float>(mouse.max_y);
738 else if (mouse.min_y >= (Bit16s)reg_dx) mouse.y = static_cast<float>(mouse.min_y);
739 else if ((Bit16s)reg_dx != POS_Y) mouse.y = static_cast<float>(reg_dx);
740 DrawCursor();
741 break;
742 case 0x05: /* Return Button Press Data */
743 {
744 Bit16u but=reg_bx;
745 reg_ax=mouse.buttons;
746 if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
747 reg_cx=mouse.last_pressed_x[but];
748 reg_dx=mouse.last_pressed_y[but];
749 reg_bx=mouse.times_pressed[but];
750 mouse.times_pressed[but]=0;
751 break;
752 }
753 case 0x06: /* Return Button Release Data */
754 {
755 Bit16u but=reg_bx;
756 reg_ax=mouse.buttons;
757 if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
758 reg_cx=mouse.last_released_x[but];
759 reg_dx=mouse.last_released_y[but];
760 reg_bx=mouse.times_released[but];
761 mouse.times_released[but]=0;
762 break;
763 }
764 case 0x07: /* Define horizontal cursor range */
765 { //lemmings set 1-640 and wants that. iron seeds set 0-640 but doesn't like 640
766 //Iron seed works if newvideo mode with mode 13 sets 0-639
767 //Larry 6 actually wants newvideo mode with mode 13 to set it to 0-319
768 Bit16s max,min;
769 if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
770 else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
771 mouse.min_x=min;
772 mouse.max_x=max;
773 /* Battlechess wants this */
774 if(mouse.x > mouse.max_x) mouse.x = mouse.max_x;
775 if(mouse.x < mouse.min_x) mouse.x = mouse.min_x;
776 /* Or alternatively this:
777 mouse.x = (mouse.max_x - mouse.min_x + 1)/2;*/
778 LOG(LOG_MOUSE,LOG_NORMAL)("Define Hortizontal range min:%d max:%d",min,max);
779 }
780 break;
781 case 0x08: /* Define vertical cursor range */
782 { // not sure what to take instead of the CurMode (see case 0x07 as well)
783 // especially the cases where sheight= 400 and we set it with the mouse_reset to 200
784 //disabled it at the moment. Seems to break syndicate who want 400 in mode 13
785 Bit16s max,min;
786 if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
787 else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
788 mouse.min_y=min;
789 mouse.max_y=max;
790 /* Battlechess wants this */
791 if(mouse.y > mouse.max_y) mouse.y = mouse.max_y;
792 if(mouse.y < mouse.min_y) mouse.y = mouse.min_y;
793 /* Or alternatively this:
794 mouse.y = (mouse.max_y - mouse.min_y + 1)/2;*/
795 LOG(LOG_MOUSE,LOG_NORMAL)("Define Vertical range min:%d max:%d",min,max);
796 }
797 break;
798 case 0x09: /* Define GFX Cursor */
799 {
800 PhysPt src = SegPhys(es)+reg_dx;
801 MEM_BlockRead(src ,userdefScreenMask,CURSORY*2);
802 MEM_BlockRead(src+CURSORY*2,userdefCursorMask,CURSORY*2);
803 mouse.screenMask = userdefScreenMask;
804 mouse.cursorMask = userdefCursorMask;
805 mouse.hotx = reg_bx;
806 mouse.hoty = reg_cx;
807 mouse.cursorType = 2;
808 DrawCursor();
809 }
810 break;
811 case 0x0a: /* Define Text Cursor */
812 mouse.cursorType = (reg_bx?1:0);
813 mouse.textAndMask = reg_cx;
814 mouse.textXorMask = reg_dx;
815 if (reg_bx) {
816 INT10_SetCursorShape(reg_cl,reg_dl);
817 LOG(LOG_MOUSE,LOG_NORMAL)("Hardware Text cursor selected");
818 }
819 DrawCursor();
820 break;
821 case 0x0b: /* Read Motion Data */
822 reg_cx=static_cast<Bit16s>(mouse.mickey_x);
823 reg_dx=static_cast<Bit16s>(mouse.mickey_y);
824 mouse.mickey_x=0;
825 mouse.mickey_y=0;
826 break;
827 case 0x0c: /* Define interrupt subroutine parameters */
828 mouse.sub_mask=reg_cx;
829 mouse.sub_seg=SegValue(es);
830 mouse.sub_ofs=reg_dx;
831 break;
832 case 0x0f: /* Define mickey/pixel rate */
833 Mouse_SetMickeyPixelRate(reg_cx,reg_dx);
834 break;
835 case 0x10: /* Define screen region for updating */
836 mouse.updateRegion_x[0]=reg_cx;
837 mouse.updateRegion_y[0]=reg_dx;
838 mouse.updateRegion_x[1]=reg_si;
839 mouse.updateRegion_y[1]=reg_di;
840 break;
841 case 0x11: /* Get number of buttons */
842 reg_ax=0xffff;
843 reg_bx=MOUSE_BUTTONS;
844 break;
845 case 0x13: /* Set double-speed threshold */
846 mouse.doubleSpeedThreshold=(reg_bx ? reg_bx : 64);
847 break;
848 case 0x14: /* Exchange event-handler */
849 {
850 Bit16u oldSeg = mouse.sub_seg;
851 Bit16u oldOfs = mouse.sub_ofs;
852 Bit16u oldMask= mouse.sub_mask;
853 // Set new values
854 mouse.sub_mask= reg_cx;
855 mouse.sub_seg = SegValue(es);
856 mouse.sub_ofs = reg_dx;
857 // Return old values
858 reg_cx = oldMask;
859 reg_dx = oldOfs;
860 SegSet16(es,oldSeg);
861 }
862 break;
863 case 0x15: /* Get Driver storage space requirements */
864 reg_bx = sizeof(mouse);
865 break;
866 case 0x16: /* Save driver state */
867 {
868 LOG(LOG_MOUSE,LOG_WARN)("Saving driver state...");
869 PhysPt dest = SegPhys(es)+reg_dx;
870 MEM_BlockWrite(dest, &mouse, sizeof(mouse));
871 }
872 break;
873 case 0x17: /* load driver state */
874 {
875 LOG(LOG_MOUSE,LOG_WARN)("Loading driver state...");
876 PhysPt src = SegPhys(es)+reg_dx;
877 MEM_BlockRead(src, &mouse, sizeof(mouse));
878 }
879 break;
880 case 0x1a: /* Set mouse sensitivity */
881 // ToDo : double mouse speed value
882 Mouse_SetSensitivity(reg_bx,reg_cx,reg_dx);
883
884 LOG(LOG_MOUSE,LOG_WARN)("Set sensitivity used with %d %d (%d)",reg_bx,reg_cx,reg_dx);
885 break;
886 case 0x1b: /* Get mouse sensitivity */
887 reg_bx = mouse.senv_x_val;
888 reg_cx = mouse.senv_y_val;
889 reg_dx = mouse.dspeed_val;
890
891 LOG(LOG_MOUSE,LOG_WARN)("Get sensitivity %d %d",reg_bx,reg_cx);
892 break;
893 case 0x1c: /* Set interrupt rate */
894 /* Can't really set a rate this is host determined */
895 break;
896 case 0x1d: /* Set display page number */
897 mouse.page=reg_bl;
898 break;
899 case 0x1e: /* Get display page number */
900 reg_bx=mouse.page;
901 break;
902 case 0x1f: /* Disable Mousedriver */
903 /* ES:BX old mouse driver Zero at the moment TODO */
904 reg_bx=0;
905 SegSet16(es,0);
906 mouse.enabled=false; /* Just for reporting not doing a thing with it */
907 mouse.oldhidden=mouse.hidden;
908 mouse.hidden=1;
909 break;
910 case 0x20: /* Enable Mousedriver */
911 mouse.enabled=true;
912 mouse.hidden=mouse.oldhidden;
913 break;
914 case 0x22: /* Set language for messages */
915 /*
916 * Values for mouse driver language:
917 *
918 * 00h English
919 * 01h French
920 * 02h Dutch
921 * 03h German
922 * 04h Swedish
923 * 05h Finnish
924 * 06h Spanish
925 * 07h Portugese
926 * 08h Italian
927 *
928 */
929 mouse.language=reg_bx;
930 break;
931 case 0x23: /* Get language for messages */
932 reg_bx=mouse.language;
933 break;
934 case 0x24: /* Get Software version and mouse type */
935 reg_bx=0x805; //Version 8.05 woohoo
936 reg_ch=0x04; /* PS/2 type */
937 reg_cl=0; /* PS/2 (unused) */
938 break;
939 case 0x26: /* Get Maximum virtual coordinates */
940 reg_bx=(mouse.enabled ? 0x0000 : 0xffff);
941 reg_cx=(Bit16u)mouse.max_x;
942 reg_dx=(Bit16u)mouse.max_y;
943 break;
944 case 0x2a: /* Get cursor hot spot */
945 reg_al=(Bit8u)-mouse.hidden; // Microsoft uses a negative byte counter for cursor visibility
946 reg_bx=(Bit16u)mouse.hotx;
947 reg_cx=(Bit16u)mouse.hoty;
948 reg_dx=0x04; // PS/2 mouse type
949 break;
950 case 0x31: /* Get Current Minimum/Maximum virtual coordinates */
951 reg_ax=(Bit16u)mouse.min_x;
952 reg_bx=(Bit16u)mouse.min_y;
953 reg_cx=(Bit16u)mouse.max_x;
954 reg_dx=(Bit16u)mouse.max_y;
955 break;
956 default:
957 LOG(LOG_MOUSE,LOG_ERROR)("Mouse Function %04X not implemented!",reg_ax);
958 break;
959 }
960 return CBRET_NONE;
961 }
962
MOUSE_BD_Handler(void)963 static Bitu MOUSE_BD_Handler(void) {
964 // the stack contains offsets to register values
965 Bit16u raxpt=real_readw(SegValue(ss),reg_sp+0x0a);
966 Bit16u rbxpt=real_readw(SegValue(ss),reg_sp+0x08);
967 Bit16u rcxpt=real_readw(SegValue(ss),reg_sp+0x06);
968 Bit16u rdxpt=real_readw(SegValue(ss),reg_sp+0x04);
969
970 // read out the actual values, registers ARE overwritten
971 Bit16u rax=real_readw(SegValue(ds),raxpt);
972 reg_ax=rax;
973 reg_bx=real_readw(SegValue(ds),rbxpt);
974 reg_cx=real_readw(SegValue(ds),rcxpt);
975 reg_dx=real_readw(SegValue(ds),rdxpt);
976 // LOG_MSG("MOUSE BD: %04X %X %X %X %d %d",reg_ax,reg_bx,reg_cx,reg_dx,POS_X,POS_Y);
977
978 // some functions are treated in a special way (additional registers)
979 switch (rax) {
980 case 0x09: /* Define GFX Cursor */
981 case 0x16: /* Save driver state */
982 case 0x17: /* load driver state */
983 SegSet16(es,SegValue(ds));
984 break;
985 case 0x0c: /* Define interrupt subroutine parameters */
986 case 0x14: /* Exchange event-handler */
987 if (reg_bx!=0) SegSet16(es,reg_bx);
988 else SegSet16(es,SegValue(ds));
989 break;
990 case 0x10: /* Define screen region for updating */
991 reg_cx=real_readw(SegValue(ds),rdxpt);
992 reg_dx=real_readw(SegValue(ds),rdxpt+2);
993 reg_si=real_readw(SegValue(ds),rdxpt+4);
994 reg_di=real_readw(SegValue(ds),rdxpt+6);
995 break;
996 default:
997 break;
998 }
999
1000 INT33_Handler();
1001
1002 // save back the registers, too
1003 real_writew(SegValue(ds),raxpt,reg_ax);
1004 real_writew(SegValue(ds),rbxpt,reg_bx);
1005 real_writew(SegValue(ds),rcxpt,reg_cx);
1006 real_writew(SegValue(ds),rdxpt,reg_dx);
1007 switch (rax) {
1008 case 0x1f: /* Disable Mousedriver */
1009 real_writew(SegValue(ds),rbxpt,SegValue(es));
1010 break;
1011 case 0x14: /* Exchange event-handler */
1012 real_writew(SegValue(ds),rcxpt,SegValue(es));
1013 break;
1014 default:
1015 break;
1016 }
1017
1018 reg_ax=rax;
1019 return CBRET_NONE;
1020 }
1021
INT74_Handler(void)1022 static Bitu INT74_Handler(void) {
1023 if (mouse.events>0) {
1024 mouse.events--;
1025 /* Check for an active Interrupt Handler that will get called */
1026 if (mouse.sub_mask & mouse.event_queue[mouse.events].type) {
1027 reg_ax=mouse.event_queue[mouse.events].type;
1028 reg_bx=mouse.event_queue[mouse.events].buttons;
1029 reg_cx=POS_X;
1030 reg_dx=POS_Y;
1031 reg_si=static_cast<Bit16s>(mouse.mickey_x);
1032 reg_di=static_cast<Bit16s>(mouse.mickey_y);
1033 CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
1034 CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
1035 SegSet16(cs, mouse.sub_seg);
1036 reg_ip = mouse.sub_ofs;
1037 if(mouse.in_UIR) LOG(LOG_MOUSE,LOG_ERROR)("Already in UIR!");
1038 mouse.in_UIR = true;
1039 //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 %X",mouse.event_queue[mouse.events].type );
1040 } else if (useps2callback) {
1041 CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
1042 CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
1043 DoPS2Callback(mouse.event_queue[mouse.events].buttons, static_cast<Bit16s>(mouse.x), static_cast<Bit16s>(mouse.y));
1044 } else {
1045 SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
1046 reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
1047 //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 not interested");
1048 }
1049 } else {
1050 SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
1051 reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
1052 //LOG(LOG_MOUSE,LOG_ERROR)("INT 74 no events");
1053 }
1054 return CBRET_NONE;
1055 }
1056
MOUSE_UserInt_CB_Handler(void)1057 Bitu MOUSE_UserInt_CB_Handler(void) {
1058 mouse.in_UIR = false;
1059 if (mouse.events) {
1060 if (!mouse.timer_in_progress) {
1061 mouse.timer_in_progress = true;
1062 PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
1063 }
1064 }
1065 return CBRET_NONE;
1066 }
1067
MOUSE_Init(Section *)1068 void MOUSE_Init(Section* /*sec*/) {
1069 // Callback for mouse interrupt 0x33
1070 call_int33=CALLBACK_Allocate();
1071 // RealPt i33loc=RealMake(CB_SEG+1,(call_int33*CB_SIZE)-0x10);
1072 RealPt i33loc=RealMake(DOS_GetMemory(0x1)-1,0x10);
1073 CALLBACK_Setup(call_int33,&INT33_Handler,CB_MOUSE,Real2Phys(i33loc),"Mouse");
1074 // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0
1075 real_writed(0,0x33<<2,i33loc);
1076
1077 call_mouse_bd=CALLBACK_Allocate();
1078 CALLBACK_Setup(call_mouse_bd,&MOUSE_BD_Handler,CB_RETF8,
1079 PhysMake(RealSeg(i33loc),RealOff(i33loc)+2),"MouseBD");
1080 // pseudocode for CB_MOUSE (including the special backdoor entry point):
1081 // jump near i33hd
1082 // callback MOUSE_BD_Handler
1083 // retf 8
1084 // label i33hd:
1085 // callback INT33_Handler
1086 // iret
1087
1088
1089 // Callback for ps2 irq
1090 call_int74=CALLBACK_Allocate();
1091 CALLBACK_Setup(call_int74,&INT74_Handler,CB_IRQ12,"int 74");
1092 // pseudocode for CB_IRQ12:
1093 // push ds
1094 // push es
1095 // pushad
1096 // sti
1097 // callback INT74_Handler
1098 // doesn't return here, but rather to CB_IRQ12_RET
1099 // (ps2 callback/user callback inbetween if requested)
1100
1101 int74_ret_callback=CALLBACK_Allocate();
1102 CALLBACK_Setup(int74_ret_callback,&MOUSE_UserInt_CB_Handler,CB_IRQ12_RET,"int 74 ret");
1103 // pseudocode for CB_IRQ12_RET:
1104 // callback MOUSE_UserInt_CB_Handler
1105 // cli
1106 // mov al, 0x20
1107 // out 0xa0, al
1108 // out 0x20, al
1109 // popad
1110 // pop es
1111 // pop ds
1112 // iret
1113
1114 Bit8u hwvec=(MOUSE_IRQ>7)?(0x70+MOUSE_IRQ-8):(0x8+MOUSE_IRQ);
1115 RealSetVec(hwvec,CALLBACK_RealPointer(call_int74));
1116
1117 // Callback for ps2 user callback handling
1118 useps2callback = false; ps2callbackinit = false;
1119 call_ps2=CALLBACK_Allocate();
1120 CALLBACK_Setup(call_ps2,&PS2_Handler,CB_RETF,"ps2 bios callback");
1121 ps2_callback=CALLBACK_RealPointer(call_ps2);
1122
1123 memset(&mouse,0,sizeof(mouse));
1124 mouse.hidden = 1; //Hide mouse on startup
1125 mouse.timer_in_progress = false;
1126 mouse.mode = 0xFF; //Non existing mode
1127
1128 mouse.sub_mask=0;
1129 mouse.sub_seg=0x6362; // magic value
1130 mouse.sub_ofs=0;
1131
1132 Mouse_ResetHardware();
1133 Mouse_Reset();
1134 Mouse_SetSensitivity(50,50,50);
1135 }
1136