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