DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/mouse.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 /* TODO: If biosps2=true and aux=false, also allow an option (default disabled)
00020  *       where if set, we don't bother to fire IRQ 12 at all but simply call the
00021  *       device callback directly. */
00022 
00023 #include <string.h>
00024 #include <math.h>
00025 
00026 
00027 #include "dosbox.h"
00028 #include "callback.h"
00029 #include "mem.h"
00030 #include "regs.h"
00031 #include "cpu.h"
00032 #include "mouse.h"
00033 #include "pic.h"
00034 #include "inout.h"
00035 #include "int10.h"
00036 #include "bios.h"
00037 #include "dos_inc.h"
00038 #include "support.h"
00039 #include "setup.h"
00040 #include "control.h"
00041 
00042 #if defined(_MSC_VER)
00043 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00044 #endif
00045 
00046 /* ints/bios.cpp */
00047 void bios_enable_ps2();
00048 
00049 /* hardware/keyboard.cpp */
00050 void AUX_INT33_Takeover();
00051 int KEYBOARD_AUX_Active();
00052 void KEYBOARD_AUX_Event(float x,float y,Bitu buttons,int scrollwheel);
00053 
00054 bool en_int33=false;
00055 bool en_bios_ps2mouse=false;
00056 bool cell_granularity_disable=false;
00057 bool en_int33_hide_if_polling=false;
00058 bool en_int33_hide_if_intsub=false;
00059 
00060 double int33_last_poll = 0;
00061 
00062 void DisableINT33() {
00063     if (en_int33) {
00064         LOG(LOG_MISC,LOG_DEBUG)("Disabling INT 33 services");
00065 
00066         en_int33 = false;
00067         /* TODO: Also unregister INT 33h handler */
00068     }
00069 }
00070 
00071 void CALLBACK_DeAllocate(Bitu in);
00072 
00073 static Bitu int74_ret_callback = 0;
00074 static Bitu call_mouse_bd = 0;
00075 static Bitu call_int33 = 0;
00076 static Bitu call_int74 = 0;
00077 static Bitu call_ps2 = 0;
00078 
00079 void MOUSE_Unsetup_DOS(void) {
00080     if (call_mouse_bd != 0) {
00081         CALLBACK_DeAllocate(call_mouse_bd);
00082         call_mouse_bd = 0;
00083     }
00084     if (call_int33 != 0) {
00085         CALLBACK_DeAllocate(call_int33);
00086         call_int33 = 0;
00087     }
00088 }
00089 
00090 void MOUSE_Unsetup_BIOS(void) {
00091     if (int74_ret_callback != 0) {
00092         CALLBACK_DeAllocate(int74_ret_callback);
00093         int74_ret_callback = 0;
00094     }
00095     if (call_int74 != 0) {
00096         CALLBACK_DeAllocate(call_int74);
00097         call_int74 = 0;
00098     }
00099     if (call_ps2 != 0) {
00100         CALLBACK_DeAllocate(call_ps2);
00101         call_ps2 = 0;
00102     }
00103 }
00104 
00105 static Bit16u ps2cbseg,ps2cbofs;
00106 static bool useps2callback,ps2callbackinit;
00107 static RealPt ps2_callback;
00108 static Bit16s oldmouseX, oldmouseY;
00109 // forward
00110 void WriteMouseIntVector(void);
00111 
00112 // serial mouse emulation
00113 void on_mouse_event_for_serial(int delta_x,int delta_y,Bit8u buttonstate);
00114 
00115 struct button_event {
00116     Bit8u type;
00117     Bit8u buttons;
00118 };
00119 
00120 extern bool enable_slave_pic;
00121 extern uint8_t p7fd8_8255_mouse_int_enable;
00122 
00123 uint8_t MOUSE_IRQ = 12; // IBM PC/AT default
00124 
00125 #define QUEUE_SIZE 32
00126 #define MOUSE_BUTTONS 3
00127 #define POS_X (static_cast<Bit16s>(mouse.x) & mouse.gran_x)
00128 #define POS_Y (static_cast<Bit16s>(mouse.y) & mouse.gran_y)
00129 
00130 #define CURSORX 16
00131 #define CURSORY 16
00132 #define HIGHESTBIT (1<<(CURSORX-1))
00133 
00134 static Bit16u defaultTextAndMask = 0x77FF;
00135 static Bit16u defaultTextXorMask = 0x7700;
00136 
00137 static Bit16u defaultScreenMask[CURSORY] = {
00138         0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
00139         0x03FF, 0x01FF, 0x00FF, 0x007F,
00140         0x003F, 0x001F, 0x01FF, 0x00FF,
00141         0x30FF, 0xF87F, 0xF87F, 0xFCFF
00142 };
00143 
00144 static Bit16u defaultCursorMask[CURSORY] = {
00145         0x0000, 0x4000, 0x6000, 0x7000,
00146         0x7800, 0x7C00, 0x7E00, 0x7F00,
00147         0x7F80, 0x7C00, 0x6C00, 0x4600,
00148         0x0600, 0x0300, 0x0300, 0x0000
00149 };
00150 
00151 static Bit16u userdefScreenMask[CURSORY];
00152 static Bit16u userdefCursorMask[CURSORY];
00153 
00154 static struct {
00155     Bit8u buttons;
00156     Bit16u times_pressed[MOUSE_BUTTONS];
00157     Bit16u times_released[MOUSE_BUTTONS];
00158     Bit16u last_released_x[MOUSE_BUTTONS];
00159     Bit16u last_released_y[MOUSE_BUTTONS];
00160     Bit16u last_pressed_x[MOUSE_BUTTONS];
00161     Bit16u last_pressed_y[MOUSE_BUTTONS];
00162     Bit16u hidden;
00163     float add_x,add_y;
00164     Bit16s min_x,max_x,min_y,max_y;
00165     Bit16s max_screen_x,max_screen_y;
00166     float mickey_x,mickey_y;
00167     float x,y;
00168     float ps2x,ps2y;
00169     button_event event_queue[QUEUE_SIZE];
00170     Bit8u events;//Increase if QUEUE_SIZE >255 (currently 32)
00171     Bit16u sub_seg,sub_ofs;
00172     Bit16u sub_mask;
00173 
00174     bool    background;
00175     Bit16s  backposx, backposy;
00176     Bit8u   backData[CURSORX*CURSORY];
00177     Bit16u* screenMask;
00178     Bit16u* cursorMask;
00179     Bit16s  clipx,clipy;
00180     Bit16s  hotx,hoty;
00181     Bit16u  textAndMask, textXorMask;
00182 
00183     float   mickeysPerPixel_x;
00184     float   mickeysPerPixel_y;
00185     float   pixelPerMickey_x;
00186     float   pixelPerMickey_y;
00187     Bit16u  senv_x_val;
00188     Bit16u  senv_y_val;
00189     Bit16u  dspeed_val;
00190     float   senv_x;
00191     float   senv_y;
00192     Bit16u  updateRegion_x[2];
00193     Bit16u  updateRegion_y[2];
00194     Bit16u  doubleSpeedThreshold;
00195     Bit16u  language;
00196     Bit16u  cursorType;
00197     Bit16u  oldhidden;
00198     Bit8u  page;
00199     bool enabled;
00200     bool inhibit_draw;
00201     bool timer_in_progress;
00202     bool first_range_setx;
00203     bool first_range_sety;
00204     bool in_UIR;
00205     Bit8u mode;
00206     Bit16s gran_x,gran_y;
00207     int scrollwheel;
00208 } mouse;
00209 
00210 bool Mouse_SetPS2State(bool use) {
00211     if (use && (!ps2callbackinit)) {
00212         useps2callback = false;
00213 
00214         if (MOUSE_IRQ != 0)
00215             PIC_SetIRQMask(MOUSE_IRQ,true);
00216 
00217         return false;
00218     }
00219     useps2callback = use;
00220     Mouse_AutoLock(useps2callback);
00221 
00222     if (MOUSE_IRQ != 0)
00223         PIC_SetIRQMask(MOUSE_IRQ,!useps2callback);
00224 
00225     return true;
00226 }
00227 
00228 void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs) {
00229     if ((pseg==0) && (pofs==0)) {
00230         ps2callbackinit = false;
00231         Mouse_AutoLock(false);
00232     } else {
00233         ps2callbackinit = true;
00234         ps2cbseg = pseg;
00235         ps2cbofs = pofs;
00236     }
00237     Mouse_AutoLock(ps2callbackinit);
00238 }
00239 
00240 /* set to true in case of shitty INT 15h device callbacks that fail to preserve CPU registers */
00241 bool ps2_callback_save_regs = false;
00242 
00243 void DoPS2Callback(Bit16u data, Bit16s mouseX, Bit16s mouseY) {
00244     if (useps2callback && ps2cbseg != 0 && ps2cbofs != 0) {
00245         Bit16u mdat = (data & 0x03) | 0x08;
00246         Bit16s xdiff = mouseX-oldmouseX;
00247         Bit16s ydiff = oldmouseY-mouseY;
00248         oldmouseX = mouseX;
00249         oldmouseY = mouseY;
00250         if ((xdiff>0xff) || (xdiff<-0xff)) mdat |= 0x40;        // x overflow
00251         if ((ydiff>0xff) || (ydiff<-0xff)) mdat |= 0x80;        // y overflow
00252         xdiff %= 256;
00253         ydiff %= 256;
00254         if (xdiff<0) {
00255             xdiff = (0x100+xdiff);
00256             mdat |= 0x10;
00257         }
00258         if (ydiff<0) {
00259             ydiff = (0x100+ydiff);
00260             mdat |= 0x20;
00261         }
00262         if (ps2_callback_save_regs) {
00263             CPU_Push16(reg_ax);CPU_Push16(reg_cx);CPU_Push16(reg_dx);CPU_Push16(reg_bx);
00264             CPU_Push16(reg_bp);CPU_Push16(reg_si);CPU_Push16(reg_di);
00265             CPU_Push16(SegValue(ds)); CPU_Push16(SegValue(es));
00266         }
00267         CPU_Push16((Bit16u)mdat); 
00268         CPU_Push16((Bit16u)(xdiff % 256)); 
00269         CPU_Push16((Bit16u)(ydiff % 256)); 
00270         CPU_Push16((Bit16u)0); 
00271         CPU_Push16(RealSeg(ps2_callback));
00272         CPU_Push16(RealOff(ps2_callback));
00273         SegSet16(cs, ps2cbseg);
00274         reg_ip = ps2cbofs;
00275     }
00276 }
00277 
00278 Bitu PS2_Handler(void) {
00279     CPU_Pop16();CPU_Pop16();CPU_Pop16();CPU_Pop16();// remove the 4 words
00280     if (ps2_callback_save_regs) {
00281         SegSet16(es,CPU_Pop16()); SegSet16(ds,CPU_Pop16());
00282         reg_di=CPU_Pop16();reg_si=CPU_Pop16();reg_bp=CPU_Pop16();
00283         reg_bx=CPU_Pop16();reg_dx=CPU_Pop16();reg_cx=CPU_Pop16();reg_ax=CPU_Pop16();
00284     }
00285     return CBRET_NONE;
00286 }
00287 
00288 
00289 #define X_MICKEY 8
00290 #define Y_MICKEY 8
00291 
00292 #define MOUSE_HAS_MOVED 1
00293 #define MOUSE_LEFT_PRESSED 2
00294 #define MOUSE_LEFT_RELEASED 4
00295 #define MOUSE_RIGHT_PRESSED 8
00296 #define MOUSE_RIGHT_RELEASED 16
00297 #define MOUSE_MIDDLE_PRESSED 32
00298 #define MOUSE_MIDDLE_RELEASED 64
00299 #define MOUSE_DUMMY 128
00300 #define MOUSE_DELAY 5.0
00301 
00302 void MOUSE_Limit_Events(Bitu /*val*/) {
00303     mouse.timer_in_progress = false;
00304 
00305     if (IS_PC98_ARCH) {
00306         if (mouse.events>0) {
00307             mouse.events--;
00308         }
00309     }
00310 
00311     if (mouse.events) {
00312         mouse.timer_in_progress = true;
00313         PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
00314 
00315         if (MOUSE_IRQ != 0) {
00316             if (!IS_PC98_ARCH)
00317                 PIC_ActivateIRQ(MOUSE_IRQ);
00318         }
00319     }
00320 }
00321 
00322 INLINE void Mouse_AddEvent(Bit8u type) {
00323     if (mouse.events<QUEUE_SIZE) {
00324         if (mouse.events>0) {
00325             /* Skip duplicate events */
00326             if (type==MOUSE_HAS_MOVED) return;
00327             /* Always put the newest element in the front as that the events are 
00328              * handled backwards (prevents doubleclicks while moving)
00329              */
00330             for(Bitu i = mouse.events ; i ; i--)
00331                 mouse.event_queue[i] = mouse.event_queue[i-1];
00332         }
00333         mouse.event_queue[0].type=type;
00334         mouse.event_queue[0].buttons=mouse.buttons;
00335         mouse.events++;
00336     }
00337     if (!mouse.timer_in_progress) {
00338         mouse.timer_in_progress = true;
00339         PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
00340 
00341         if (MOUSE_IRQ != 0) {
00342             if (!IS_PC98_ARCH)
00343                 PIC_ActivateIRQ(MOUSE_IRQ);
00344         }
00345     }
00346 }
00347 
00348 void MOUSE_DummyEvent(void) {
00349     Mouse_AddEvent(MOUSE_DUMMY);
00350 }
00351 
00352 // ***************************************************************************
00353 // Mouse cursor - text mode
00354 // ***************************************************************************
00355 /* Write and read directly to the screen. Do no use int_setcursorpos (LOTUS123) */
00356 extern void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit16u chr,Bit8u attr,bool useattr);
00357 extern void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result);
00358 
00359 void RestoreCursorBackgroundText() {
00360     if (mouse.hidden || mouse.inhibit_draw) return;
00361 
00362     if (mouse.background) {
00363         WriteChar((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE),mouse.backData[0],mouse.backData[1],true);
00364         mouse.background = false;
00365     }
00366 }
00367 
00368 void DrawCursorText() { 
00369     // Restore Background
00370     RestoreCursorBackgroundText();
00371 
00372 
00373     // Save Background
00374     mouse.backposx      = POS_X>>3;
00375     mouse.backposy      = POS_Y>>3;
00376     if (mouse.mode < 2) mouse.backposx >>= 1; 
00377 
00378     //use current page (CV program)
00379     Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00380     Bit16u result;
00381 
00382     ReadCharAttr((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,page,&result);
00383     mouse.backData[0]   = (Bit8u)(result & 0xFF);
00384     mouse.backData[1]   = (Bit8u)(result>>8);
00385     mouse.background    = true;
00386     // Write Cursor
00387     result = (result & mouse.textAndMask) ^ mouse.textXorMask;
00388     WriteChar((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true);
00389 }
00390 
00391 // ***************************************************************************
00392 // Mouse cursor - graphic mode
00393 // ***************************************************************************
00394 
00395 static Bit8u gfxReg3CE[9];
00396 static Bit8u index3C4,gfxReg3C5;
00397 void SaveVgaRegisters() {
00398     if (IS_VGA_ARCH) {
00399         for (Bit8u i=0; i<9; i++) {
00400             IO_Write    (0x3CE,i);
00401             gfxReg3CE[i] = IO_Read(0x3CF);
00402         }
00403         /* Setup some default values in GFX regs that should work */
00404         IO_Write(0x3CE,3); IO_Write(0x3Cf,0);               //disable rotate and operation
00405         IO_Write(0x3CE,5); IO_Write(0x3Cf,gfxReg3CE[5]&0xf0);   //Force read/write mode 0
00406 
00407         //Set Map to all planes. Celtic Tales
00408         index3C4 = IO_Read(0x3c4);  IO_Write(0x3C4,2);
00409         gfxReg3C5 = IO_Read(0x3C5); IO_Write(0x3C5,0xF);
00410     } else if (machine==MCH_EGA) {
00411         //Set Map to all planes.
00412         IO_Write(0x3C4,2);
00413         IO_Write(0x3C5,0xF);
00414     }
00415 }
00416 
00417 void RestoreVgaRegisters() {
00418     if (IS_VGA_ARCH) {
00419         for (Bit8u i=0; i<9; i++) {
00420             IO_Write(0x3CE,i);
00421             IO_Write(0x3CF,gfxReg3CE[i]);
00422         }
00423 
00424         IO_Write(0x3C4,2);
00425         IO_Write(0x3C5,gfxReg3C5);
00426         IO_Write(0x3C4,index3C4);
00427     }
00428 }
00429 
00430 void ClipCursorArea(Bit16s& x1, Bit16s& x2, Bit16s& y1, Bit16s& y2,
00431                     Bit16u& addx1, Bit16u& addx2, Bit16u& addy) {
00432     addx1 = addx2 = addy = 0;
00433     // Clip up
00434     if (y1<0) {
00435         addy += (-y1);
00436         y1 = 0;
00437     }
00438     // Clip down
00439     if (y2>mouse.clipy) {
00440         y2 = mouse.clipy;       
00441     };
00442     // Clip left
00443     if (x1<0) {
00444         addx1 += (-x1);
00445         x1 = 0;
00446     };
00447     // Clip right
00448     if (x2>mouse.clipx) {
00449         addx2 = x2 - mouse.clipx;
00450         x2 = mouse.clipx;
00451     };
00452 }
00453 
00454 void RestoreCursorBackground() {
00455     if (mouse.hidden || mouse.inhibit_draw) return;
00456 
00457     SaveVgaRegisters();
00458     if (mouse.background) {
00459         // Restore background
00460         Bit16s x,y;
00461         Bit16u addx1,addx2,addy;
00462         Bit16u dataPos  = 0;
00463         Bit16s x1       = mouse.backposx;
00464         Bit16s y1       = mouse.backposy;
00465         Bit16s x2       = x1 + CURSORX - 1;
00466         Bit16s y2       = y1 + CURSORY - 1; 
00467 
00468         ClipCursorArea(x1, x2, y1, y2, addx1, addx2, addy);
00469 
00470         dataPos = addy * CURSORX;
00471         for (y=y1; y<=y2; y++) {
00472             dataPos += addx1;
00473             for (x=x1; x<=x2; x++) {
00474                 INT10_PutPixel((Bit16u)x,(Bit16u)y,mouse.page,mouse.backData[dataPos++]);
00475             };
00476             dataPos += addx2;
00477         };
00478         mouse.background = false;
00479     };
00480     RestoreVgaRegisters();
00481 }
00482 
00483 void DrawCursor() {
00484     if (mouse.hidden || mouse.inhibit_draw) return;
00485     // In Textmode ?
00486     if (CurMode->type==M_TEXT) {
00487         DrawCursorText();
00488         return;
00489     }
00490 
00491     // Check video page. Seems to be ignored for text mode. 
00492     // hence the text mode handled above this
00493     if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)!=mouse.page) return;
00494 // Check if cursor in update region
00495 /*  if ((POS_X >= mouse.updateRegion_x[0]) && (POS_X <= mouse.updateRegion_x[1]) &&
00496         (POS_Y >= mouse.updateRegion_y[0]) && (POS_Y <= mouse.updateRegion_y[1])) {
00497         if (CurMode->type==M_TEXT16)
00498             RestoreCursorBackgroundText();
00499         else
00500             RestoreCursorBackground();
00501         mouse.shown--;
00502         return;
00503     }
00504    */ /*Not sure yet what to do update region should be set to ??? */
00505          
00506     // Get Clipping ranges
00507 
00508 
00509     mouse.clipx = (Bit16s)((Bits)CurMode->swidth-1);    /* Get from bios ? */
00510     mouse.clipy = (Bit16s)((Bits)CurMode->sheight-1);
00511 
00512     /* might be vidmode == 0x13?2:1 */
00513     Bit16s xratio = 640;
00514     if (CurMode->swidth>0) xratio/=(Bit16u)CurMode->swidth;
00515     if (xratio==0) xratio = 1;
00516     
00517     RestoreCursorBackground();
00518 
00519     SaveVgaRegisters();
00520 
00521     // Save Background
00522     Bit16s x,y;
00523     Bit16u addx1,addx2,addy;
00524     Bit16u dataPos  = 0;
00525     Bit16s x1       = POS_X / xratio - mouse.hotx;
00526     Bit16s y1       = POS_Y - mouse.hoty;
00527     Bit16s x2       = x1 + CURSORX - 1;
00528     Bit16s y2       = y1 + CURSORY - 1; 
00529 
00530     ClipCursorArea(x1,x2,y1,y2, addx1, addx2, addy);
00531 
00532     dataPos = addy * CURSORX;
00533     for (y=y1; y<=y2; y++) {
00534         dataPos += addx1;
00535         for (x=x1; x<=x2; x++) {
00536             INT10_GetPixel((Bit16u)x,(Bit16u)y,mouse.page,&mouse.backData[dataPos++]);
00537         };
00538         dataPos += addx2;
00539     };
00540     mouse.background= true;
00541     mouse.backposx  = POS_X / xratio - mouse.hotx;
00542     mouse.backposy  = POS_Y - mouse.hoty;
00543 
00544     // Draw Mousecursor
00545     dataPos = addy * CURSORX;
00546     for (y=y1; y<=y2; y++) {
00547         Bit16u scMask = mouse.screenMask[addy+y-y1];
00548         Bit16u cuMask = mouse.cursorMask[addy+y-y1];
00549         if (addx1>0) { scMask<<=addx1; cuMask<<=addx1; dataPos += addx1; };
00550         for (x=x1; x<=x2; x++) {
00551             Bit8u pixel = 0;
00552             // ScreenMask
00553             if (scMask & HIGHESTBIT) pixel = mouse.backData[dataPos];
00554             scMask<<=1;
00555             // CursorMask
00556             if (cuMask & HIGHESTBIT) pixel = pixel ^ 0x0F;
00557             cuMask<<=1;
00558             // Set Pixel
00559             INT10_PutPixel((Bit16u)x,(Bit16u)y,mouse.page,pixel);
00560             dataPos++;
00561         };
00562         dataPos += addx2;
00563     };
00564     RestoreVgaRegisters();
00565 }
00566 
00567 void pc98_mouse_movement_apply(int x,int y);
00568 
00569 #if !defined(C_SDL2)
00570 bool GFX_IsFullscreen(void);
00571 #else
00572 static inline bool GFX_IsFullscreen(void) {
00573     return false;
00574 }
00575 #endif
00576 
00577 extern int  user_cursor_x,  user_cursor_y;
00578 extern int  user_cursor_sw, user_cursor_sh;
00579 extern bool user_cursor_locked;
00580 
00581 /* FIXME: Re-test this code */
00582 void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) {
00583     extern bool Mouse_Vertical;
00584     float dx = xrel * mouse.pixelPerMickey_x;
00585     float dy = (Mouse_Vertical?-yrel:yrel) * mouse.pixelPerMickey_y;
00586 
00587     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00588         KEYBOARD_AUX_Event(xrel,yrel,mouse.buttons,mouse.scrollwheel);
00589         mouse.scrollwheel = 0;
00590         return;
00591     }
00592 
00593     if((fabs(xrel) > 1.0) || (mouse.senv_x < 1.0)) dx *= mouse.senv_x;
00594     if((fabs(yrel) > 1.0) || (mouse.senv_y < 1.0)) dy *= mouse.senv_y;
00595     if (useps2callback) dy *= 2;    
00596 
00597     if (user_cursor_locked) {
00598         /* either device reports relative motion ONLY, and therefore requires that the user
00599          * has captured the mouse */
00600 
00601         /* serial mouse */
00602         on_mouse_event_for_serial((int)(dx),(int)(dy*2),mouse.buttons);
00603 
00604         /* PC-98 mouse */
00605         if (IS_PC98_ARCH) pc98_mouse_movement_apply(xrel,yrel);
00606     }
00607 
00608     if (user_cursor_locked) {
00609         mouse.mickey_x += (dx * mouse.mickeysPerPixel_x);
00610         mouse.mickey_y += (dy * mouse.mickeysPerPixel_y);
00611         if (mouse.mickey_x >= 32768.0) mouse.mickey_x -= 65536.0;
00612         else if (mouse.mickey_x <= -32769.0) mouse.mickey_x += 65536.0;
00613         if (mouse.mickey_y >= 32768.0) mouse.mickey_y -= 65536.0;
00614         else if (mouse.mickey_y <= -32769.0) mouse.mickey_y += 65536.0;
00615     }
00616 
00617     if (emulate) {
00618         mouse.x += dx;
00619         mouse.y += dy;
00620     } else if (CurMode != NULL) {
00621         if (CurMode->type == M_TEXT) {
00622             mouse.x = x*CurMode->swidth;
00623             mouse.y = y*CurMode->sheight * 8 / CurMode->cheight;
00624         /* NTS: DeluxePaint II enhanced sets a large range (5112x3832) for VGA mode 0x12 640x480 16-color */
00625         } else {
00626             if ((mouse.max_x > 0) && (mouse.max_y > 0)) {
00627                 mouse.x = x*mouse.max_x;
00628                 mouse.y = y*mouse.max_y;
00629             } else {
00630                 mouse.x += xrel;
00631                 mouse.y += yrel;
00632             }
00633         }
00634     }
00635 
00636     /* ignore constraints if using PS2 mouse callback in the bios */
00637 
00638     if (mouse.x > mouse.max_x) mouse.x = mouse.max_x;
00639     if (mouse.x < mouse.min_x) mouse.x = mouse.min_x;
00640     if (mouse.y > mouse.max_y) mouse.y = mouse.max_y;
00641     if (mouse.y < mouse.min_y) mouse.y = mouse.min_y;
00642 
00643     /*make mouse emulated, eventually*/
00644     extern MOUSE_EMULATION user_cursor_emulation;
00645     bool emu;
00646     switch (user_cursor_emulation)
00647     {
00648     case MOUSE_EMULATION_ALWAYS:
00649         emu = true;
00650         break;
00651     case MOUSE_EMULATION_INTEGRATION:
00652         emu = !user_cursor_locked && !GFX_IsFullscreen();
00653         break;
00654     case MOUSE_EMULATION_LOCKED:
00655         emu = user_cursor_locked && !GFX_IsFullscreen();
00656         break;
00657     case MOUSE_EMULATION_NEVER:
00658     default:
00659         emu = false;
00660     }
00661     if (!emu)
00662     {
00663         auto x1 = (double)user_cursor_x / static_cast<double>(user_cursor_sw - 1);
00664         auto y1 = (double)user_cursor_y / static_cast<double>(user_cursor_sh - 1);
00665         mouse.x       = x1 * mouse.max_screen_x;
00666         mouse.y       = y1 * mouse.max_screen_y;
00667 
00668         if (mouse.x < mouse.min_x)
00669             mouse.x = mouse.min_x;
00670         if (mouse.y < mouse.min_y)
00671             mouse.y = mouse.min_y;
00672         if (mouse.x > mouse.max_x)
00673             mouse.x = mouse.max_x;
00674         if (mouse.y > mouse.max_y)
00675             mouse.y = mouse.max_y;
00676     }
00677 
00678     if (user_cursor_locked) {
00679         /* send relative PS/2 mouse motion only if the cursor is captured */
00680         mouse.ps2x += xrel;
00681         mouse.ps2y += yrel;
00682         if (mouse.ps2x >= 32768.0)       mouse.ps2x -= 65536.0;
00683         else if (mouse.ps2x <= -32769.0) mouse.ps2x += 65536.0;
00684         if (mouse.ps2y >= 32768.0)       mouse.ps2y -= 65536.0;
00685         else if (mouse.ps2y <= -32769.0) mouse.ps2y += 65536.0;
00686     }
00687 
00688     Mouse_AddEvent(MOUSE_HAS_MOVED);
00689 }
00690 
00691 uint8_t Mouse_GetButtonState(void) {
00692     return mouse.buttons;
00693 }
00694 
00695 void Mouse_ButtonPressed(Bit8u button) {
00696     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00697         switch (button) {
00698             case 0:
00699                 mouse.buttons|=1;
00700                 break;
00701             case 1:
00702                 mouse.buttons|=2;
00703                 break;
00704             case 2:
00705                 mouse.buttons|=4;
00706                 break;
00707             default:
00708                 return;
00709         }
00710 
00711         KEYBOARD_AUX_Event(0,0,mouse.buttons,mouse.scrollwheel);
00712         mouse.scrollwheel = 0;
00713         return;
00714     }
00715 
00716     if (button > 2)
00717         return;
00718 
00719     switch (button) {
00720 #if (MOUSE_BUTTONS >= 1)
00721     case 0:
00722         mouse.buttons|=1;
00723         Mouse_AddEvent(MOUSE_LEFT_PRESSED);
00724         break;
00725 #endif
00726 #if (MOUSE_BUTTONS >= 2)
00727     case 1:
00728         mouse.buttons|=2;
00729         Mouse_AddEvent(MOUSE_RIGHT_PRESSED);
00730         break;
00731 #endif
00732 #if (MOUSE_BUTTONS >= 3)
00733     case 2:
00734         mouse.buttons|=4;
00735         Mouse_AddEvent(MOUSE_MIDDLE_PRESSED);
00736         break;
00737 #endif
00738     default:
00739         return;
00740     }
00741     mouse.times_pressed[button]++;
00742     mouse.last_pressed_x[button]=(Bit16u)POS_X;
00743     mouse.last_pressed_y[button]=(Bit16u)POS_Y;
00744 
00745     /* serial mouse, if connected, also wants to know about it */
00746     on_mouse_event_for_serial(0,0,mouse.buttons);
00747 }
00748 
00749 void Mouse_ButtonReleased(Bit8u button) {
00750     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00751         switch (button) {
00752             case 0:
00753                 mouse.buttons&=~1;
00754                 break;
00755             case 1:
00756                 mouse.buttons&=~2;
00757                 break;
00758             case 2:
00759                 mouse.buttons&=~4;
00760                 break;
00761             case (100-1):   /* scrollwheel up */
00762                 mouse.scrollwheel -= 8;
00763                 break;
00764             case (100+1):   /* scrollwheel down */
00765                 mouse.scrollwheel += 8;
00766                 break;
00767             default:
00768                 return;
00769         }
00770 
00771         KEYBOARD_AUX_Event(0,0,mouse.buttons,mouse.scrollwheel);
00772         mouse.scrollwheel = 0;
00773         return;
00774     }
00775 
00776     if (button > 2)
00777         return;
00778 
00779     switch (button) {
00780 #if (MOUSE_BUTTONS >= 1)
00781     case 0:
00782         mouse.buttons&=~1;
00783         Mouse_AddEvent(MOUSE_LEFT_RELEASED);
00784         break;
00785 #endif
00786 #if (MOUSE_BUTTONS >= 2)
00787     case 1:
00788         mouse.buttons&=~2;
00789         Mouse_AddEvent(MOUSE_RIGHT_RELEASED);
00790         break;
00791 #endif
00792 #if (MOUSE_BUTTONS >= 3)
00793     case 2:
00794         mouse.buttons&=~4;
00795         Mouse_AddEvent(MOUSE_MIDDLE_RELEASED);
00796         break;
00797 #endif
00798     default:
00799         return;
00800     }
00801     mouse.times_released[button]++; 
00802     mouse.last_released_x[button]=(Bit16u)POS_X;
00803     mouse.last_released_y[button]=(Bit16u)POS_Y;
00804 
00805     /* serial mouse, if connected, also wants to know about it */
00806     on_mouse_event_for_serial(0,0,mouse.buttons);
00807 }
00808 
00809 static void Mouse_SetMickeyPixelRate(Bit16s px, Bit16s py){
00810     if ((px!=0) && (py!=0)) {
00811         mouse.mickeysPerPixel_x  = (float)px/X_MICKEY;
00812         mouse.mickeysPerPixel_y  = (float)py/Y_MICKEY;
00813         mouse.pixelPerMickey_x   = X_MICKEY/(float)px;
00814         mouse.pixelPerMickey_y   = Y_MICKEY/(float)py;  
00815     }
00816 }
00817 
00818 static void Mouse_SetSensitivity(Bit16u px, Bit16u py, Bit16u dspeed){
00819     if(px>100) px=100;
00820     if(py>100) py=100;
00821     if(dspeed>100) dspeed=100;
00822     // save values
00823     mouse.senv_x_val=px;
00824     mouse.senv_y_val=py;
00825     mouse.dspeed_val=dspeed;
00826     if ((px!=0) && (py!=0)) {
00827         px--;  //Inspired by cutemouse 
00828         py--;  //Although their cursor update routine is far more complex then ours
00829         mouse.senv_x=(static_cast<float>(px)*px)/3600.0f +1.0f/3.0f;
00830         mouse.senv_y=(static_cast<float>(py)*py)/3600.0f +1.0f/3.0f;
00831      }
00832 }
00833 
00834 
00835 static void Mouse_ResetHardware(void){
00836     if (MOUSE_IRQ != 0)
00837         PIC_SetIRQMask(MOUSE_IRQ,false);
00838 
00839     if (IS_PC98_ARCH)
00840         IO_WriteB(0x7FDD,IO_ReadB(0x7FDD) & (~0x10)); // remove interrupt inhibit
00841 }
00842 
00843 //Does way to much. Many things should be moved to mouse reset one day
00844 void Mouse_NewVideoMode(void) {
00845     mouse.inhibit_draw = false;
00846     /* Get the correct resolution from the current video mode */
00847     Bit8u mode = mem_readb(BIOS_VIDEO_MODE);
00848     if(mode == mouse.mode) {LOG(LOG_MOUSE,LOG_NORMAL)("New video is the same as the old"); /*return;*/}
00849     mouse.first_range_setx = false;
00850     mouse.first_range_sety = false;
00851     mouse.gran_x = (Bit16s)0xffff;
00852     mouse.gran_y = (Bit16s)0xffff;
00853     mouse.min_x = 0;
00854     mouse.max_x = 639;
00855     mouse.min_y = 0;
00856     mouse.max_y = 479;
00857 
00858     if (machine == MCH_HERC) {
00859         // DeluxePaint II again...
00860         mouse.first_range_setx = true;
00861         mouse.first_range_sety = true;
00862     }
00863 
00864     switch (mode) {
00865     case 0x00:
00866     case 0x01:
00867     case 0x02:
00868     case 0x03:
00869     case 0x07: {
00870         mouse.gran_x = (mode<2)?0xfff0:0xfff8;
00871         mouse.gran_y = (Bit16s)0xfff8;
00872         if (IS_PC98_ARCH) {
00873             mouse.max_y = 400 - 1;
00874         }
00875         else {
00876             Bitu rows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS);
00877             if ((rows == 0) || (rows > 250)) rows = 25 - 1;
00878             mouse.max_y = 8*(rows+1) - 1;
00879         }
00880         break;
00881     }
00882     case 0x04:
00883     case 0x05:
00884     case 0x06:
00885     case 0x08:
00886     case 0x09:
00887     case 0x0a:
00888     case 0x0d:
00889     case 0x0e:
00890     case 0x13:
00891         if (mode == 0x0d || mode == 0x13) mouse.gran_x = (Bit16s)0xfffe;
00892         mouse.max_y = 199;
00893         // some games redefine the mouse range for this mode
00894         mouse.first_range_setx = true;
00895         mouse.first_range_sety = true;
00896         break;
00897     case 0x0f:
00898     case 0x10:
00899         mouse.max_y = 349;
00900         // Deluxepaint II enhanced will redefine the range for 640x480 mode
00901         mouse.first_range_setx = true;
00902         mouse.first_range_sety = true;
00903         break;
00904     case 0x11:
00905     case 0x12:
00906         mouse.max_y = 479;
00907         // Deluxepaint II enhanced will redefine the range for 640x480 mode
00908         mouse.first_range_setx = true;
00909         mouse.first_range_sety = true;
00910         break;
00911     default:
00912         LOG(LOG_MOUSE,LOG_ERROR)("Unhandled videomode %X on reset",mode);
00913         mouse.inhibit_draw = true;
00914         if (CurMode != NULL) {
00915             mouse.first_range_setx = true;
00916             mouse.first_range_sety = true;
00917             mouse.max_x = CurMode->swidth - 1;
00918             mouse.max_y = CurMode->sheight - 1;
00919         }
00920         else {
00921             mouse.max_x = 639;
00922             mouse.max_y = 479;
00923         }
00924         break;
00925     }
00926     mouse.mode = mode;
00927     mouse.hidden = 1;
00928 
00929     if (cell_granularity_disable) {
00930         mouse.gran_x = (Bit16s)0xffff;
00931         mouse.gran_y = (Bit16s)0xffff;
00932     }
00933 
00934     mouse.events = 0;
00935     mouse.timer_in_progress = false;
00936     PIC_RemoveEvents(MOUSE_Limit_Events);
00937 
00938     mouse.hotx       = 0;
00939     mouse.hoty       = 0;
00940     mouse.background = false;
00941     mouse.screenMask = defaultScreenMask;
00942     mouse.cursorMask = defaultCursorMask;
00943     mouse.textAndMask= defaultTextAndMask;
00944     mouse.textXorMask= defaultTextXorMask;
00945     mouse.language   = 0;
00946     mouse.page               = 0;
00947     mouse.doubleSpeedThreshold = 64;
00948     mouse.updateRegion_x[0] = 1;
00949     mouse.updateRegion_y[0] = 1;
00950     mouse.updateRegion_x[1] = 1;
00951     mouse.updateRegion_y[1] = 1;
00952     mouse.cursorType = 0;
00953     mouse.enabled=true;
00954     mouse.oldhidden=1;
00955 
00956     mouse.max_screen_x = mouse.max_x;
00957     mouse.max_screen_y = mouse.max_y;
00958 }
00959 
00960 //Much too empty, Mouse_NewVideoMode contains stuff that should be in here
00961 static void Mouse_Reset(void) {
00962     /* Remove drawn mouse Legends of Valor */
00963     if (CurMode->type!=M_TEXT) RestoreCursorBackground();
00964     else RestoreCursorBackgroundText();
00965     mouse.hidden = 1;
00966 
00967     Mouse_NewVideoMode();
00968     Mouse_SetMickeyPixelRate(8,16);
00969 
00970     mouse.mickey_x = 0;
00971     mouse.mickey_y = 0;
00972 
00973     // Dont set max coordinates here. it is done by SetResolution!
00974     mouse.x = static_cast<float>((mouse.max_x + 1)/ 2);
00975     mouse.y = static_cast<float>((mouse.max_y + 1)/ 2);
00976     mouse.sub_mask = 0;
00977     mouse.in_UIR = false;
00978 }
00979 
00980 static Bitu INT33_Handler(void) {
00981 //  LOG(LOG_MOUSE,LOG_NORMAL)("MOUSE: %04X %X %X %d %d",reg_ax,reg_bx,reg_cx,POS_X,POS_Y);
00982     INT10_SetCurMode();
00983     switch (reg_ax) {
00984     case 0x00:  /* Reset Driver and Read Status */
00985         Mouse_ResetHardware(); /* fallthrough */
00986     case 0x21:  /* Software Reset */
00987         extern bool Mouse_Drv;
00988         if (Mouse_Drv) {
00989             reg_ax=0xffff;
00990             reg_bx=MOUSE_BUTTONS;
00991             Mouse_Reset();
00992             Mouse_AutoLock(true);
00993             AUX_INT33_Takeover();
00994             LOG(LOG_KEYBOARD,LOG_NORMAL)("INT 33h reset");
00995         }
00996         break;
00997     case 0x01:  /* Show Mouse */
00998         if(mouse.hidden) mouse.hidden--;
00999         Mouse_AutoLock(true);
01000         DrawCursor();
01001         break;
01002     case 0x02:  /* Hide Mouse */
01003         {
01004             if (CurMode->type!=M_TEXT) RestoreCursorBackground();
01005             else RestoreCursorBackgroundText();
01006             mouse.hidden++;
01007         }
01008         break;
01009     case 0x03:  /* Return position and Button Status */
01010         reg_bx=mouse.buttons;
01011         reg_cx=(Bit16u)POS_X;
01012         reg_dx=(Bit16u)POS_Y;
01013         mouse.first_range_setx = false;
01014         mouse.first_range_sety = false;
01015         if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01016         break;
01017     case 0x04:  /* Position Mouse */
01018         /* If position isn't different from current position
01019          * don't change it then. (as position is rounded so numbers get
01020          * lost when the rounded number is set) (arena/simulation Wolf) */
01021         if ((Bit16s)reg_cx >= mouse.max_x) mouse.x = static_cast<float>(mouse.max_x);
01022         else if (mouse.min_x >= (Bit16s)reg_cx) mouse.x = static_cast<float>(mouse.min_x); 
01023         else if ((Bit16s)reg_cx != POS_X) mouse.x = static_cast<float>(reg_cx);
01024 
01025         if ((Bit16s)reg_dx >= mouse.max_y) mouse.y = static_cast<float>(mouse.max_y);
01026         else if (mouse.min_y >= (Bit16s)reg_dx) mouse.y = static_cast<float>(mouse.min_y); 
01027         else if ((Bit16s)reg_dx != POS_Y) mouse.y = static_cast<float>(reg_dx);
01028         DrawCursor();
01029         if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01030         break;
01031     case 0x05:  /* Return Button Press Data */
01032         {
01033             Bit16u but=reg_bx;
01034             reg_ax=mouse.buttons;
01035             if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
01036             reg_cx=mouse.last_pressed_x[but];
01037             reg_dx=mouse.last_pressed_y[but];
01038             reg_bx=mouse.times_pressed[but];
01039             mouse.times_pressed[but]=0;
01040             if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01041             break;
01042         }
01043     case 0x06:  /* Return Button Release Data */
01044         {
01045             Bit16u but=reg_bx;
01046             reg_ax=mouse.buttons;
01047             if (but>=MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
01048             reg_cx=mouse.last_released_x[but];
01049             reg_dx=mouse.last_released_y[but];
01050             reg_bx=mouse.times_released[but];
01051             mouse.times_released[but]=0;
01052             if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01053             break;
01054         }
01055     case 0x07:  /* Define horizontal cursor range */
01056         {   //lemmings set 1-640 and wants that. iron seeds set 0-640 but doesn't like 640
01057             //Iron seed works if newvideo mode with mode 13 sets 0-639
01058             //Larry 6 actually wants newvideo mode with mode 13 to set it to 0-319
01059             Bit16s max,min;
01060             if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
01061             else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
01062             mouse.min_x=min;
01063             mouse.max_x=max;
01064             /* Battlechess wants this */
01065             if(mouse.x > mouse.max_x) mouse.x = mouse.max_x;
01066             if(mouse.x < mouse.min_x) mouse.x = mouse.min_x;
01067             /* Or alternatively this: 
01068             mouse.x = (mouse.max_x - mouse.min_x + 1)/2;*/
01069             LOG(LOG_MOUSE,LOG_NORMAL)("Define Hortizontal range min:%d max:%d",min,max);
01070 
01071             /* NTS: The mouse in VESA BIOS modes would ideally start with the x and y ranges
01072              *      that fit the screen, but I'm not so sure mouse drivers even pay attention
01073              *      to VESA BIOS modes so it's not certain what comes out. However some
01074              *      demoscene productions like "Aqua" will set their own mouse range and draw
01075              *      their own cursor. The menu in "Aqua" will set up 640x480 256-color mode
01076              *      and then set a mouse range of x=0-1279 and y=0-479. Using the FIRST range
01077              *      set after mode set is the only way to make sure mouse pointer integration
01078              *      tracks the guest pointer properly. */
01079             if (mouse.first_range_setx) {
01080                 if (mouse.min_x == 0 && mouse.max_x > 0) {
01081                     // most games redefine the range so they can use a saner range matching the screen
01082                     mouse.max_screen_x = mouse.max_x;
01083                     LOG(LOG_MOUSE,LOG_NORMAL)("Define Hortizontal range min:%d max:%d defines the bounds of the screen",min,max);
01084                 }
01085 
01086                 mouse.first_range_setx = false;
01087             }
01088         }
01089         break;
01090     case 0x08:  /* Define vertical cursor range */
01091         {   // not sure what to take instead of the CurMode (see case 0x07 as well)
01092             // especially the cases where sheight= 400 and we set it with the mouse_reset to 200
01093             //disabled it at the moment. Seems to break syndicate who want 400 in mode 13
01094             Bit16s max,min;
01095             if ((Bit16s)reg_cx<(Bit16s)reg_dx) { min=(Bit16s)reg_cx;max=(Bit16s)reg_dx;}
01096             else { min=(Bit16s)reg_dx;max=(Bit16s)reg_cx;}
01097             mouse.min_y=min;
01098             mouse.max_y=max;
01099             /* Battlechess wants this */
01100             if(mouse.y > mouse.max_y) mouse.y = mouse.max_y;
01101             if(mouse.y < mouse.min_y) mouse.y = mouse.min_y;
01102             /* Or alternatively this: 
01103             mouse.y = (mouse.max_y - mouse.min_y + 1)/2;*/
01104             LOG(LOG_MOUSE,LOG_NORMAL)("Define Vertical range min:%d max:%d",min,max);
01105 
01106             /* NTS: The mouse in VESA BIOS modes would ideally start with the x and y ranges
01107              *      that fit the screen, but I'm not so sure mouse drivers even pay attention
01108              *      to VESA BIOS modes so it's not certain what comes out. However some
01109              *      demoscene productions like "Aqua" will set their own mouse range and draw
01110              *      their own cursor. The menu in "Aqua" will set up 640x480 256-color mode
01111              *      and then set a mouse range of x=0-1279 and y=0-479. Using the FIRST range
01112              *      set after mode set is the only way to make sure mouse pointer integration
01113              *      tracks the guest pointer properly. */
01114             if (mouse.first_range_sety) {
01115                 if (mouse.min_y == 0 && mouse.max_y > 0) {
01116                     // most games redefine the range so they can use a saner range matching the screen
01117                     mouse.max_screen_y = mouse.max_y;
01118                     LOG(LOG_MOUSE,LOG_NORMAL)("Define Vertical range min:%d max:%d defines the bounds of the screen",min,max);
01119                 }
01120 
01121                 mouse.first_range_sety = false;
01122             }
01123         }
01124         break;
01125     case 0x09:  /* Define GFX Cursor */
01126         {
01127             PhysPt src = SegPhys(es)+reg_dx;
01128             MEM_BlockRead(src          ,userdefScreenMask,CURSORY*2);
01129             MEM_BlockRead(src+CURSORY*2,userdefCursorMask,CURSORY*2);
01130             mouse.screenMask = userdefScreenMask;
01131             mouse.cursorMask = userdefCursorMask;
01132             mouse.hotx       = (Bit16s)reg_bx;
01133             mouse.hoty       = (Bit16s)reg_cx;
01134             mouse.cursorType = 2;
01135             DrawCursor();
01136         }
01137         break;
01138     case 0x0a:  /* Define Text Cursor */
01139         mouse.cursorType = reg_bx;
01140         mouse.textAndMask = reg_cx;
01141         mouse.textXorMask = reg_dx;
01142         break;
01143     case 0x0b:  /* Read Motion Data */
01144     {
01145             bool MOUSE_IsLocked();
01146             const auto locked = MOUSE_IsLocked();
01147             reg_cx = (Bit16u)static_cast<Bit16s>(locked ? mouse.mickey_x : 0);
01148             reg_dx = (Bit16u)static_cast<Bit16s>(locked ? mouse.mickey_y : 0);
01149             mouse.mickey_x = 0;
01150             mouse.mickey_y = 0;
01151         break;
01152     }
01153     case 0x0c:  /* Define interrupt subroutine parameters */
01154         mouse.sub_mask=reg_cx;
01155         mouse.sub_seg=SegValue(es);
01156         mouse.sub_ofs=reg_dx;
01157         Mouse_AutoLock(true); //Some games don't seem to reset the mouse before using
01158         break;
01159     case 0x0f:  /* Define mickey/pixel rate */
01160         Mouse_SetMickeyPixelRate((Bit16s)reg_cx,(Bit16s)reg_dx);
01161         break;
01162     case 0x10:      /* Define screen region for updating */
01163         mouse.updateRegion_x[0]=reg_cx;
01164         mouse.updateRegion_y[0]=reg_dx;
01165         mouse.updateRegion_x[1]=reg_si;
01166         mouse.updateRegion_y[1]=reg_di;
01167         break;
01168     case 0x11:      /* Get number of buttons */
01169         reg_ax=0xffff;
01170         reg_bx=MOUSE_BUTTONS;
01171         break;
01172     case 0x13:      /* Set double-speed threshold */
01173         mouse.doubleSpeedThreshold=(reg_bx ? reg_bx : 64);
01174         break;
01175     case 0x14: /* Exchange event-handler */ 
01176         {   
01177             Bit16u oldSeg = mouse.sub_seg;
01178             Bit16u oldOfs = mouse.sub_ofs;
01179             Bit16u oldMask= mouse.sub_mask;
01180             // Set new values
01181             mouse.sub_mask= reg_cx;
01182             mouse.sub_seg = SegValue(es);
01183             mouse.sub_ofs = reg_dx;
01184             // Return old values
01185             reg_cx = (Bit16u)oldMask;
01186             reg_dx = (Bit16u)oldOfs;
01187             SegSet16(es,oldSeg);
01188         }
01189         break;      
01190     case 0x15: /* Get Driver storage space requirements */
01191         reg_bx = sizeof(mouse);
01192         break;
01193     case 0x16: /* Save driver state */
01194         {
01195             LOG(LOG_MOUSE,LOG_WARN)("Saving driver state...");
01196             PhysPt dest = SegPhys(es)+reg_dx;
01197             MEM_BlockWrite(dest, &mouse, sizeof(mouse));
01198         }
01199         break;
01200     case 0x17: /* load driver state */
01201         {
01202             LOG(LOG_MOUSE,LOG_WARN)("Loading driver state...");
01203             PhysPt src = SegPhys(es)+reg_dx;
01204             MEM_BlockRead(src, &mouse, sizeof(mouse));
01205         }
01206         break;
01207     case 0x1a:  /* Set mouse sensitivity */
01208         // ToDo : double mouse speed value
01209         Mouse_SetSensitivity(reg_bx,reg_cx,reg_dx);
01210 
01211         LOG(LOG_MOUSE,LOG_WARN)("Set sensitivity used with %d %d (%d)",reg_bx,reg_cx,reg_dx);
01212         break;
01213     case 0x1b:  /* Get mouse sensitivity */
01214         reg_bx = mouse.senv_x_val;
01215         reg_cx = mouse.senv_y_val;
01216         reg_dx = mouse.dspeed_val;
01217 
01218         LOG(LOG_MOUSE,LOG_WARN)("Get sensitivity %d %d",reg_bx,reg_cx);
01219         break;
01220     case 0x1c:  /* Set interrupt rate */
01221         /* Can't really set a rate this is host determined */
01222         break;
01223     case 0x1d:      /* Set display page number */
01224         mouse.page=reg_bl;
01225         break;
01226     case 0x1e:      /* Get display page number */
01227         reg_bx=mouse.page;
01228         break;
01229     case 0x1f:  /* Disable Mousedriver */
01230         /* ES:BX old mouse driver Zero at the moment TODO */ 
01231         reg_bx=0;
01232         SegSet16(es,0);    
01233         mouse.enabled=false; /* Just for reporting not doing a thing with it */
01234         mouse.oldhidden=mouse.hidden;
01235         mouse.hidden=1;
01236         break;
01237     case 0x20:  /* Enable Mousedriver */
01238         mouse.enabled=true;
01239         mouse.hidden=mouse.oldhidden;
01240         break;
01241     case 0x22:      /* Set language for messages */
01242             /*
01243              *                        Values for mouse driver language:
01244              * 
01245              *                        00h     English
01246              *                        01h     French
01247              *                        02h     Dutch
01248              *                        03h     German
01249              *                        04h     Swedish
01250              *                        05h     Finnish
01251              *                        06h     Spanish
01252              *                        07h     Portugese
01253              *                        08h     Italian
01254              *                
01255              */
01256         mouse.language=reg_bx;
01257         break;
01258     case 0x23:      /* Get language for messages */
01259         reg_bx=mouse.language;
01260         break;
01261     case 0x24:  /* Get Software version and mouse type */
01262         reg_bx=0x805;   //Version 8.05 woohoo 
01263         reg_ch=0x04;    /* PS/2 type */
01264         reg_cl=0;       /* PS/2 (unused) */
01265         break;
01266     case 0x26: /* Get Maximum virtual coordinates */
01267         reg_bx=(mouse.enabled ? 0x0000 : 0xffff);
01268         reg_cx=(Bit16u)mouse.max_x;
01269         reg_dx=(Bit16u)mouse.max_y;
01270         break;
01271     case 0x2a:  /* Get cursor hot spot */
01272         reg_al=(Bit8u)-mouse.hidden;    // Microsoft uses a negative byte counter for cursor visibility
01273         reg_bx=(Bit16u)mouse.hotx;
01274         reg_cx=(Bit16u)mouse.hoty;
01275         reg_dx=0x04;    // PS/2 mouse type
01276         break;
01277     case 0x31: /* Get Current Minimum/Maximum virtual coordinates */
01278         reg_ax=(Bit16u)mouse.min_x;
01279         reg_bx=(Bit16u)mouse.min_y;
01280         reg_cx=(Bit16u)mouse.max_x;
01281         reg_dx=(Bit16u)mouse.max_y;
01282         break;
01283     default:
01284         LOG(LOG_MOUSE,LOG_ERROR)("Mouse Function %04X not implemented!",reg_ax);
01285         break;
01286     }
01287     return CBRET_NONE;
01288 }
01289 
01290 static Bitu MOUSE_BD_Handler(void) {
01291     // the stack contains offsets to register values
01292     Bit16u raxpt=real_readw(SegValue(ss),reg_sp+0x0a);
01293     Bit16u rbxpt=real_readw(SegValue(ss),reg_sp+0x08);
01294     Bit16u rcxpt=real_readw(SegValue(ss),reg_sp+0x06);
01295     Bit16u rdxpt=real_readw(SegValue(ss),reg_sp+0x04);
01296 
01297     // read out the actual values, registers ARE overwritten
01298     Bit16u rax=real_readw(SegValue(ds),raxpt);
01299     reg_ax=rax;
01300     reg_bx=real_readw(SegValue(ds),rbxpt);
01301     reg_cx=real_readw(SegValue(ds),rcxpt);
01302     reg_dx=real_readw(SegValue(ds),rdxpt);
01303 //  LOG_MSG("MOUSE BD: %04X %X %X %X %d %d",reg_ax,reg_bx,reg_cx,reg_dx,POS_X,POS_Y);
01304     
01305     // some functions are treated in a special way (additional registers)
01306     switch (rax) {
01307         case 0x09:  /* Define GFX Cursor */
01308         case 0x16:  /* Save driver state */
01309         case 0x17:  /* load driver state */
01310             SegSet16(es,SegValue(ds));
01311             break;
01312         case 0x0c:  /* Define interrupt subroutine parameters */
01313         case 0x14:  /* Exchange event-handler */ 
01314             if (reg_bx!=0) SegSet16(es,reg_bx);
01315             else SegSet16(es,SegValue(ds));
01316             break;
01317         case 0x10:  /* Define screen region for updating */
01318             reg_cx=real_readw(SegValue(ds),rdxpt);
01319             reg_dx=real_readw(SegValue(ds),rdxpt+2);
01320             reg_si=real_readw(SegValue(ds),rdxpt+4);
01321             reg_di=real_readw(SegValue(ds),rdxpt+6);
01322             break;
01323         default:
01324             break;
01325     }
01326 
01327     INT33_Handler();
01328 
01329     // save back the registers, too
01330     real_writew(SegValue(ds),raxpt,reg_ax);
01331     real_writew(SegValue(ds),rbxpt,reg_bx);
01332     real_writew(SegValue(ds),rcxpt,reg_cx);
01333     real_writew(SegValue(ds),rdxpt,reg_dx);
01334     switch (rax) {
01335         case 0x1f:  /* Disable Mousedriver */
01336             real_writew(SegValue(ds),rbxpt,SegValue(es));
01337             break;
01338         case 0x14: /* Exchange event-handler */ 
01339             real_writew(SegValue(ds),rcxpt,SegValue(es));
01340             break;
01341         default:
01342             break;
01343     }
01344 
01345     reg_ax=rax;
01346     return CBRET_NONE;
01347 }
01348 
01349 static Bitu INT74_Handler(void) {
01350     if (mouse.events>0) {
01351         mouse.events--;
01352 
01353         /* INT 33h emulation: HERE within the IRQ 12 handler is the appropriate place to
01354          * redraw the cursor. OSes like Windows 3.1 expect real-mode code to do it in
01355          * response to IRQ 12, not "out of the blue" from the SDL event handler like
01356          * the original DOSBox code did it. Doing this allows the INT 33h emulation
01357          * to draw the cursor while not causing Windows 3.1 to crash or behave
01358          * erratically. */
01359         if (en_int33) DrawCursor();
01360 
01361         /* Check for an active Interrupt Handler that will get called */
01362         if (mouse.sub_mask & mouse.event_queue[mouse.events].type) {
01363             reg_ax=mouse.event_queue[mouse.events].type;
01364             reg_bx=mouse.event_queue[mouse.events].buttons;
01365             reg_cx=(Bit16u)POS_X;
01366             reg_dx=(Bit16u)POS_Y;
01367             reg_si=(Bit16u)static_cast<Bit16s>(mouse.mickey_x);
01368             reg_di=(Bit16u)static_cast<Bit16s>(mouse.mickey_y);
01369             CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01370             CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
01371             SegSet16(cs, mouse.sub_seg);
01372             reg_ip = mouse.sub_ofs;
01373             if(mouse.in_UIR) LOG(LOG_MOUSE,LOG_ERROR)("Already in UIR!");
01374             mouse.in_UIR = true;
01375         } else if (useps2callback) {
01376             CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01377             CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
01378             DoPS2Callback(mouse.event_queue[mouse.events].buttons, static_cast<Bit16s>(mouse.ps2x), static_cast<Bit16s>(mouse.ps2y));
01379         } else {
01380             SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01381             reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
01382         }
01383     } else {
01384         SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01385         reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
01386     }
01387     return CBRET_NONE;
01388 }
01389 
01390 Bitu MOUSE_UserInt_CB_Handler(void) {
01391     mouse.in_UIR = false;
01392     if (mouse.events) {
01393         if (!mouse.timer_in_progress) {
01394             mouse.timer_in_progress = true;
01395             PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
01396         }
01397     }
01398     return CBRET_NONE;
01399 }
01400 
01401 bool MouseTypeNone();
01402 
01403 void MOUSE_OnReset(Section *sec) {
01404     (void)sec;//UNUSED
01405     if (IS_PC98_ARCH)
01406         MOUSE_IRQ = 13; // PC-98 standard
01407     else if (!enable_slave_pic)
01408         MOUSE_IRQ = 0;
01409     else
01410         MOUSE_IRQ = 12; // IBM PC/AT standard
01411 
01412     if (MOUSE_IRQ != 0)
01413         PIC_SetIRQMask(MOUSE_IRQ,true);
01414 }
01415 
01416 void MOUSE_ShutDown(Section *sec) {
01417     (void)sec;//UNUSED
01418 }
01419 
01420 void BIOS_PS2MOUSE_ShutDown(Section *sec) {
01421     (void)sec;//UNUSED
01422 }
01423 
01424 void BIOS_PS2Mouse_Startup(Section *sec) {
01425     (void)sec;//UNUSED
01426     Section_prop *section=static_cast<Section_prop *>(control->GetSection("dos"));
01427 
01428     /* NTS: This assumes MOUSE_Init() is called after KEYBOARD_Init() */
01429     en_bios_ps2mouse = section->Get_bool("biosps2");
01430 
01431     if (!enable_slave_pic || machine == MCH_PCJR) return;
01432 
01433     if (!en_bios_ps2mouse) return;
01434 
01435     if (MouseTypeNone()) {
01436         LOG(LOG_KEYBOARD,LOG_WARN)("INT 15H PS/2 emulation NOT enabled. biosps2=1 but mouse type=none");
01437     }
01438     else {
01439         LOG(LOG_KEYBOARD,LOG_NORMAL)("INT 15H PS/2 emulation enabled");
01440         bios_enable_ps2();
01441     }
01442 
01443     ps2_callback_save_regs = section->Get_bool("int15 mouse callback does not preserve registers");
01444 
01445     // Callback for ps2 irq
01446     call_int74=CALLBACK_Allocate();
01447     CALLBACK_Setup(call_int74,&INT74_Handler,CB_IRQ12,"int 74");
01448     // pseudocode for CB_IRQ12:
01449     //  push ds
01450     //  push es
01451     //  pushad
01452     //  sti
01453     //  callback INT74_Handler
01454     //      doesn't return here, but rather to CB_IRQ12_RET
01455     //      (ps2 callback/user callback inbetween if requested)
01456 
01457     int74_ret_callback=CALLBACK_Allocate();
01458     CALLBACK_Setup(int74_ret_callback,&MOUSE_UserInt_CB_Handler,CB_IRQ12_RET,"int 74 ret");
01459     // pseudocode for CB_IRQ12_RET:
01460     //  callback MOUSE_UserInt_CB_Handler
01461     //  cli
01462     //  mov al, 0x20
01463     //  out 0xa0, al
01464     //  out 0x20, al
01465     //  popad
01466     //  pop es
01467     //  pop ds
01468     //  iret
01469 
01470     if (MOUSE_IRQ != 0) {
01471         Bit8u hwvec=(MOUSE_IRQ>7)?(0x70+MOUSE_IRQ-8):(0x8+MOUSE_IRQ);
01472         RealSetVec(hwvec,CALLBACK_RealPointer(call_int74));
01473     }
01474 
01475     // Callback for ps2 user callback handling
01476     useps2callback = false; ps2callbackinit = false;
01477     call_ps2=CALLBACK_Allocate();
01478     CALLBACK_Setup(call_ps2,&PS2_Handler,CB_RETF,"ps2 bios callback");
01479     ps2_callback=CALLBACK_RealPointer(call_ps2);
01480 }
01481 
01482 void MOUSE_Startup(Section *sec) {
01483     (void)sec;//UNUSED
01484     Section_prop *section=static_cast<Section_prop *>(control->GetSection("dos"));
01485     RealPt i33loc=0;
01486 
01487     /* TODO: Needs to check for mouse, and fail to do anything if neither PS/2 nor serial mouse emulation enabled */
01488 
01489     en_int33_hide_if_intsub=section->Get_bool("int33 hide host cursor if interrupt subroutine");
01490 
01491     en_int33_hide_if_polling=section->Get_bool("int33 hide host cursor when polling");
01492 
01493     en_int33=section->Get_bool("int33");
01494     if (!en_int33) {
01495         Mouse_Reset();
01496         Mouse_SetSensitivity(50,50,50);
01497         return;
01498     }
01499 
01500     cell_granularity_disable=section->Get_bool("int33 disable cell granularity");
01501 
01502     LOG(LOG_KEYBOARD,LOG_NORMAL)("INT 33H emulation enabled");
01503     if (en_int33_hide_if_polling)
01504         LOG(LOG_KEYBOARD,LOG_NORMAL)("INT 33H emulation will hide host cursor if polling");
01505 
01506     // Callback for mouse interrupt 0x33
01507     call_int33=CALLBACK_Allocate();
01508 
01509     // i33loc=RealMake(CB_SEG+1,(call_int33*CB_SIZE)-0x10);
01510     i33loc=RealMake(DOS_GetMemory(0x1,"i33loc")-1,0x10);
01511     CALLBACK_Setup(call_int33,&INT33_Handler,CB_MOUSE,Real2Phys(i33loc),"Mouse");
01512 
01513     // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0
01514     real_writed(0,0x33<<2,i33loc);
01515 
01516     call_mouse_bd=CALLBACK_Allocate();
01517     CALLBACK_Setup(call_mouse_bd,&MOUSE_BD_Handler,CB_RETF8,
01518         PhysMake(RealSeg(i33loc),RealOff(i33loc)+2),"MouseBD");
01519     // pseudocode for CB_MOUSE (including the special backdoor entry point):
01520     //  jump near i33hd
01521     //  callback MOUSE_BD_Handler
01522     //  retf 8
01523     //  label i33hd:
01524     //  callback INT33_Handler
01525     //  iret
01526 
01527     memset(&mouse,0,sizeof(mouse));
01528     mouse.hidden = 1; //Hide mouse on startup
01529     mouse.timer_in_progress = false;
01530     mouse.mode = 0xFF; //Non existing mode
01531     mouse.scrollwheel = 0;
01532 
01533     mouse.sub_mask=0;
01534     mouse.sub_seg=0x6362;   // magic value
01535     mouse.sub_ofs=0;
01536 
01537     oldmouseX = oldmouseY = 0;
01538     mouse.ps2x = mouse.ps2y = 0;
01539 
01540     Mouse_ResetHardware();
01541     Mouse_Reset();
01542     Mouse_SetSensitivity(50,50,50);
01543 }
01544 
01545 void MOUSE_Init() {
01546     LOG(LOG_MISC,LOG_DEBUG)("Initializing mouse interface emulation");
01547 
01548     // TODO: We need a DOSBox shutdown callback, and we need a shutdown callback for when the DOS kernel begins to unload and on system reset
01549     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(MOUSE_OnReset));
01550 }
01551 
01552 bool MOUSE_IsHidden()
01553 {
01554     return static_cast<bool>(mouse.hidden);
01555 }
01556 
01557 bool MOUSE_IsBeingPolled()
01558 {
01559     if (!en_int33_hide_if_polling)
01560         return false;
01561 
01562     return (PIC_FullIndex() < (int33_last_poll + 1000));
01563 }
01564 
01565 bool MOUSE_HasInterruptSub()
01566 {
01567     if (!en_int33_hide_if_intsub)
01568         return false;
01569 
01570     return (mouse.sub_mask != 0);
01571 }
01572