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