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