DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/mouse.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 /* TODO: If biosps2=true and aux=false, also allow an option (default disabled)
00020  *       where if set, we don't bother to fire IRQ 12 at all but simply call the
00021  *       device callback directly. */
00022 
00023 #include <string.h>
00024 #include <math.h>
00025 
00026 
00027 #include "dosbox.h"
00028 #include "callback.h"
00029 #include "mem.h"
00030 #include "regs.h"
00031 #include "cpu.h"
00032 #include "mouse.h"
00033 #include "pic.h"
00034 #include "inout.h"
00035 #include "int10.h"
00036 #include "bios.h"
00037 #include "dos_inc.h"
00038 #include "support.h"
00039 #include "setup.h"
00040 #include "control.h"
00041 
00042 #if defined(_MSC_VER)
00043 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00044 #endif
00045 
00046 /* ints/bios.cpp */
00047 void bios_enable_ps2();
00048 
00049 /* hardware/keyboard.cpp */
00050 void AUX_INT33_Takeover();
00051 int KEYBOARD_AUX_Active();
00052 void KEYBOARD_AUX_Event(float x,float y,Bitu buttons,int scrollwheel);
00053 
00054 bool en_int33=false;
00055 bool en_bios_ps2mouse=false;
00056 bool cell_granularity_disable=false;
00057 bool en_int33_hide_if_polling=false;
00058 bool en_int33_hide_if_intsub=false;
00059 bool en_int33_pc98_show_graphics=true; // NEC MOUSE.COM behavior
00060 
00061 double int33_last_poll = 0;
00062 
00063 void DisableINT33() {
00064     if (en_int33) {
00065         LOG(LOG_MOUSE, LOG_DEBUG)("Disabling INT 33 services");
00066 
00067         en_int33 = false;
00068         /* TODO: Also unregister INT 33h handler */
00069     }
00070 }
00071 
00072 void CALLBACK_DeAllocate(Bitu in);
00073 
00074 static Bitu int74_ret_callback = 0;
00075 static Bitu call_mouse_bd = 0;
00076 static Bitu call_int33 = 0;
00077 static Bitu call_int74 = 0;
00078 static Bitu call_ps2, call_uir = 0;
00079 
00080 void MOUSE_Unsetup_DOS(void) {
00081     if (call_mouse_bd != 0) {
00082         CALLBACK_DeAllocate(call_mouse_bd);
00083         call_mouse_bd = 0;
00084     }
00085     if (call_int33 != 0) {
00086         CALLBACK_DeAllocate(call_int33);
00087         call_int33 = 0;
00088     }
00089 }
00090 
00091 void MOUSE_Unsetup_BIOS(void) {
00092     if (int74_ret_callback != 0) {
00093         CALLBACK_DeAllocate(int74_ret_callback);
00094         int74_ret_callback = 0;
00095     }
00096     if (call_int74 != 0) {
00097         CALLBACK_DeAllocate(call_int74);
00098         call_int74 = 0;
00099     }
00100     if (call_ps2 != 0) {
00101         CALLBACK_DeAllocate(call_ps2);
00102         call_ps2 = 0;
00103     }
00104 }
00105 
00106 static Bit16u ps2cbseg,ps2cbofs;
00107 static bool useps2callback,ps2callbackinit;
00108 static RealPt ps2_callback,uir_callback;
00109 static Bit16s oldmouseX, oldmouseY;
00110 
00111 // serial mouse emulation
00112 void on_mouse_event_for_serial(int delta_x,int delta_y,Bit8u buttonstate);
00113 
00114 struct button_event {
00115     Bit8u type;
00116     Bit8u buttons;
00117 };
00118 
00119 extern bool enable_slave_pic;
00120 extern uint8_t p7fd8_8255_mouse_int_enable;
00121 
00122 uint8_t MOUSE_IRQ = 12; // IBM PC/AT default
00123 
00124 #define QUEUE_SIZE 32
00125 #define MOUSE_BUTTONS 3
00126 #define POS_X (static_cast<Bit16s>(mouse.x) & mouse.gran_x)
00127 #define POS_Y (static_cast<Bit16s>(mouse.y) & mouse.gran_y)
00128 
00129 #define CURSORX 16
00130 #define CURSORY 16
00131 #define HIGHESTBIT (1<<(CURSORX-1))
00132 
00133 static Bit16u defaultTextAndMask = 0x77FF;
00134 static Bit16u defaultTextXorMask = 0x7700;
00135 
00136 static Bit16u defaultScreenMask[CURSORY] = {
00137         0x3FFF, 0x1FFF, 0x0FFF, 0x07FF,
00138         0x03FF, 0x01FF, 0x00FF, 0x007F,
00139         0x003F, 0x001F, 0x01FF, 0x00FF,
00140         0x30FF, 0xF87F, 0xF87F, 0xFCFF
00141 };
00142 
00143 static Bit16u defaultCursorMask[CURSORY] = {
00144         0x0000, 0x4000, 0x6000, 0x7000,
00145         0x7800, 0x7C00, 0x7E00, 0x7F00,
00146         0x7F80, 0x7C00, 0x6C00, 0x4600,
00147         0x0600, 0x0300, 0x0300, 0x0000
00148 };
00149 
00150 static Bit16u userdefScreenMask[CURSORY];
00151 static Bit16u userdefCursorMask[CURSORY];
00152 
00153 static struct {
00154     Bit8u buttons;
00155     Bit16u times_pressed[MOUSE_BUTTONS];
00156     Bit16u times_released[MOUSE_BUTTONS];
00157     Bit16u last_released_x[MOUSE_BUTTONS];
00158     Bit16u last_released_y[MOUSE_BUTTONS];
00159     Bit16u last_pressed_x[MOUSE_BUTTONS];
00160     Bit16u last_pressed_y[MOUSE_BUTTONS];
00161     pic_tickindex_t hidden_at;
00162     Bit16u hidden;
00163     float add_x,add_y;
00164     Bit16s min_x,max_x,min_y,max_y;
00165     Bit16s max_screen_x,max_screen_y;
00166     float mickey_x,mickey_y;
00167     float x,y;
00168     float ps2x,ps2y;
00169     button_event event_queue[QUEUE_SIZE];
00170     Bit8u events;//Increase if QUEUE_SIZE >255 (currently 32)
00171     Bit16u sub_seg,sub_ofs;
00172     Bit16u sub_mask;
00173 
00174     bool    background;
00175     Bit16s  backposx, backposy;
00176     Bit8u   backData[CURSORX*CURSORY];
00177     Bit16u* screenMask;
00178     Bit16u* cursorMask;
00179     Bit16s  clipx,clipy;
00180     Bit16s  hotx,hoty;
00181     Bit16u  textAndMask, textXorMask;
00182 
00183     float   mickeysPerPixel_x;
00184     float   mickeysPerPixel_y;
00185     float   pixelPerMickey_x;
00186     float   pixelPerMickey_y;
00187     Bit16u  senv_x_val;
00188     Bit16u  senv_y_val;
00189     Bit16u  dspeed_val;
00190     float   senv_x;
00191     float   senv_y;
00192     Bit16s  updateRegion_x[2];
00193     Bit16s  updateRegion_y[2];
00194     Bit16u  doubleSpeedThreshold;
00195     Bit16u  language;
00196     Bit16u  cursorType;
00197     Bit16u  oldhidden;
00198     Bit8u  page;
00199     bool enabled;
00200     bool inhibit_draw;
00201     bool timer_in_progress;
00202     bool first_range_setx;
00203     bool first_range_sety;
00204     bool in_UIR;
00205     Bit8u mode;
00206     Bit16s gran_x,gran_y;
00207     int scrollwheel;
00208 } mouse;
00209 
00210 bool Mouse_SetPS2State(bool use) {
00211     if (use && (!ps2callbackinit)) {
00212         useps2callback = false;
00213 
00214         if (MOUSE_IRQ != 0)
00215             PIC_SetIRQMask(MOUSE_IRQ,true);
00216 
00217         return false;
00218     }
00219     useps2callback = use;
00220     Mouse_AutoLock(useps2callback);
00221 
00222     if (MOUSE_IRQ != 0)
00223         PIC_SetIRQMask(MOUSE_IRQ,!useps2callback);
00224 
00225     return true;
00226 }
00227 
00228 void Mouse_ChangePS2Callback(Bit16u pseg, Bit16u pofs) {
00229     if ((pseg==0) && (pofs==0)) {
00230         ps2callbackinit = false;
00231         Mouse_AutoLock(false);
00232     } else {
00233         ps2callbackinit = true;
00234         ps2cbseg = pseg;
00235         ps2cbofs = pofs;
00236     }
00237     Mouse_AutoLock(ps2callbackinit);
00238 }
00239 
00240 /* set to true in case of shitty INT 15h device callbacks that fail to preserve CPU registers */
00241 bool ps2_callback_save_regs = false;
00242 
00243 void DoPS2Callback(Bit16u data, Bit16s mouseX, Bit16s mouseY) {
00244     if (useps2callback && ps2cbseg != 0 && ps2cbofs != 0) {
00245         Bit16u mdat = (data & 0x03) | 0x08;
00246         Bit16s xdiff = mouseX-oldmouseX;
00247         Bit16s ydiff = oldmouseY-mouseY;
00248         oldmouseX = mouseX;
00249         oldmouseY = mouseY;
00250         if ((xdiff>0xff) || (xdiff<-0xff)) mdat |= 0x40;        // x overflow
00251         if ((ydiff>0xff) || (ydiff<-0xff)) mdat |= 0x80;        // y overflow
00252         xdiff %= 256;
00253         ydiff %= 256;
00254         if (xdiff<0) {
00255             xdiff = (0x100+xdiff);
00256             mdat |= 0x10;
00257         }
00258         if (ydiff<0) {
00259             ydiff = (0x100+ydiff);
00260             mdat |= 0x20;
00261         }
00262         if (ps2_callback_save_regs) {
00263             CPU_Push16(reg_ax);CPU_Push16(reg_cx);CPU_Push16(reg_dx);CPU_Push16(reg_bx);
00264             CPU_Push16(reg_bp);CPU_Push16(reg_si);CPU_Push16(reg_di);
00265             CPU_Push16(SegValue(ds)); CPU_Push16(SegValue(es));
00266         }
00267         CPU_Push16((Bit16u)mdat); 
00268         CPU_Push16((Bit16u)(xdiff % 256)); 
00269         CPU_Push16((Bit16u)(ydiff % 256)); 
00270         CPU_Push16((Bit16u)0); 
00271         CPU_Push16(RealSeg(ps2_callback));
00272         CPU_Push16(RealOff(ps2_callback));
00273         SegSet16(cs, ps2cbseg);
00274         reg_ip = ps2cbofs;
00275     }
00276 }
00277 
00278 Bitu PS2_Handler(void) {
00279     CPU_Pop16();CPU_Pop16();CPU_Pop16();CPU_Pop16();// remove the 4 words
00280     if (ps2_callback_save_regs) {
00281         SegSet16(es,CPU_Pop16()); SegSet16(ds,CPU_Pop16());
00282         reg_di=CPU_Pop16();reg_si=CPU_Pop16();reg_bp=CPU_Pop16();
00283         reg_bx=CPU_Pop16();reg_dx=CPU_Pop16();reg_cx=CPU_Pop16();reg_ax=CPU_Pop16();
00284     }
00285     return CBRET_NONE;
00286 }
00287 
00288 
00289 #define X_MICKEY 8
00290 #define Y_MICKEY 8
00291 
00292 #define MOUSE_HAS_MOVED 1
00293 #define MOUSE_LEFT_PRESSED 2
00294 #define MOUSE_LEFT_RELEASED 4
00295 #define MOUSE_RIGHT_PRESSED 8
00296 #define MOUSE_RIGHT_RELEASED 16
00297 #define MOUSE_MIDDLE_PRESSED 32
00298 #define MOUSE_MIDDLE_RELEASED 64
00299 #define MOUSE_DUMMY 128
00300 #define MOUSE_DELAY 5.0
00301 
00302 void MOUSE_Limit_Events(Bitu /*val*/) {
00303     mouse.timer_in_progress = false;
00304 
00305     if (IS_PC98_ARCH) {
00306         if (mouse.events>0) {
00307             mouse.events--;
00308         }
00309     }
00310 
00311     if (mouse.events) {
00312         mouse.timer_in_progress = true;
00313         PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
00314 
00315         if (MOUSE_IRQ != 0) {
00316             if (!IS_PC98_ARCH)
00317                 PIC_ActivateIRQ(MOUSE_IRQ);
00318         }
00319     }
00320 }
00321 
00322 INLINE void Mouse_AddEvent(Bit8u type) {
00323     if (mouse.events<QUEUE_SIZE) {
00324         if (mouse.events>0) {
00325             /* Skip duplicate events */
00326             if (type==MOUSE_HAS_MOVED) return;
00327             /* Always put the newest element in the front as that the events are 
00328              * handled backwards (prevents doubleclicks while moving)
00329              */
00330             for(Bitu i = mouse.events ; i ; i--)
00331                 mouse.event_queue[i] = mouse.event_queue[i-1];
00332         }
00333         mouse.event_queue[0].type=type;
00334         mouse.event_queue[0].buttons=mouse.buttons;
00335         mouse.events++;
00336     }
00337     if (!mouse.timer_in_progress) {
00338         mouse.timer_in_progress = true;
00339         PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
00340 
00341         if (MOUSE_IRQ != 0) {
00342             if (!IS_PC98_ARCH)
00343                 PIC_ActivateIRQ(MOUSE_IRQ);
00344         }
00345     }
00346 }
00347 
00348 void MOUSE_DummyEvent(void) {
00349     Mouse_AddEvent(MOUSE_DUMMY);
00350 }
00351 
00352 // ***************************************************************************
00353 // Mouse cursor - text mode
00354 // ***************************************************************************
00355 /* Write and read directly to the screen. Do no use int_setcursorpos (LOTUS123) */
00356 extern void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit16u chr,Bit8u attr,bool useattr);
00357 extern void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result);
00358 
00359 void RestoreCursorBackgroundText() {
00360     if (mouse.hidden || mouse.inhibit_draw) return;
00361 
00362     if (mouse.background) {
00363         WriteChar((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE),mouse.backData[0],mouse.backData[1],true);
00364         mouse.background = false;
00365     }
00366 }
00367 
00368 void DrawCursorText() { 
00369     // Restore Background
00370     RestoreCursorBackgroundText();
00371 
00372     // Check if cursor in update region
00373     if ((POS_Y <= mouse.updateRegion_y[1]) && (POS_Y >= mouse.updateRegion_y[0]) &&
00374         (POS_X <= mouse.updateRegion_x[1]) && (POS_X >= mouse.updateRegion_x[0])) {
00375         return;
00376     }
00377 
00378     // Save Background
00379     mouse.backposx      = POS_X>>3;
00380     mouse.backposy      = POS_Y>>3;
00381     if (mouse.mode < 2) mouse.backposx >>= 1; 
00382 
00383     //use current page (CV program)
00384     Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00385 
00386     if (mouse.cursorType == 0) {
00387         Bit16u result;
00388         ReadCharAttr((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,page,&result);
00389         mouse.backData[0]       = (Bit8u)(result & 0xFF);
00390         mouse.backData[1]       = (Bit8u)(result>>8);
00391         mouse.background        = true;
00392         // Write Cursor
00393         result = (result & mouse.textAndMask) ^ mouse.textXorMask;
00394         WriteChar((Bit16u)mouse.backposx,(Bit16u)mouse.backposy,page,(Bit8u)(result&0xFF),(Bit8u)(result>>8),true);
00395     } else {
00396         Bit16u address=page * real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00397         address += (mouse.backposy * real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS) + mouse.backposx) * 2;
00398         address /= 2;
00399         Bit16u cr = real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
00400         IO_Write(cr     , 0xe);
00401         IO_Write((Bitu)cr + 1u, (address >> 8) & 0xff);
00402         IO_Write(cr     , 0xf);
00403         IO_Write((Bitu)cr + 1u, address & 0xff);
00404     }
00405 }
00406 
00407 // ***************************************************************************
00408 // Mouse cursor - graphic mode
00409 // ***************************************************************************
00410 
00411 static Bit8u gfxReg3CE[9];
00412 static Bit8u index3C4,gfxReg3C5;
00413 void SaveVgaRegisters() {
00414     if (IS_VGA_ARCH) {
00415         for (Bit8u i=0; i<9; i++) {
00416             IO_Write    (0x3CE,i);
00417             gfxReg3CE[i] = IO_Read(0x3CF);
00418         }
00419         /* Setup some default values in GFX regs that should work */
00420         IO_Write(0x3CE,3); IO_Write(0x3Cf,0);               //disable rotate and operation
00421         IO_Write(0x3CE,5); IO_Write(0x3Cf,gfxReg3CE[5]&0xf0);   //Force read/write mode 0
00422 
00423         //Set Map to all planes. Celtic Tales
00424         index3C4 = IO_Read(0x3c4);  IO_Write(0x3C4,2);
00425         gfxReg3C5 = IO_Read(0x3C5); IO_Write(0x3C5,0xF);
00426     } else if (machine==MCH_EGA) {
00427         //Set Map to all planes.
00428         IO_Write(0x3C4,2);
00429         IO_Write(0x3C5,0xF);
00430     }
00431 }
00432 
00433 void RestoreVgaRegisters() {
00434     if (IS_VGA_ARCH) {
00435         for (Bit8u i=0; i<9; i++) {
00436             IO_Write(0x3CE,i);
00437             IO_Write(0x3CF,gfxReg3CE[i]);
00438         }
00439 
00440         IO_Write(0x3C4,2);
00441         IO_Write(0x3C5,gfxReg3C5);
00442         IO_Write(0x3C4,index3C4);
00443     }
00444 }
00445 
00446 void ClipCursorArea(Bit16s& x1, Bit16s& x2, Bit16s& y1, Bit16s& y2,
00447                     Bit16u& addx1, Bit16u& addx2, Bit16u& addy) {
00448     addx1 = addx2 = addy = 0;
00449     // Clip up
00450     if (y1<0) {
00451         addy += (-y1);
00452         y1 = 0;
00453     }
00454     // Clip down
00455     if (y2>mouse.clipy) {
00456         y2 = mouse.clipy;       
00457     }
00458     // Clip left
00459     if (x1<0) {
00460         addx1 += (-x1);
00461         x1 = 0;
00462     }
00463     // Clip right
00464     if (x2>mouse.clipx) {
00465         addx2 = x2 - mouse.clipx;
00466         x2 = mouse.clipx;
00467     }
00468 }
00469 
00470 void RestoreCursorBackground() {
00471     if (mouse.hidden || mouse.inhibit_draw) return;
00472 
00473     SaveVgaRegisters();
00474     if (mouse.background) {
00475         // Restore background
00476         Bit16s x,y;
00477         Bit16u addx1,addx2,addy;
00478         Bit16u dataPos  = 0;
00479         Bit16s x1       = mouse.backposx;
00480         Bit16s y1       = mouse.backposy;
00481         Bit16s x2       = x1 + CURSORX - 1;
00482         Bit16s y2       = y1 + CURSORY - 1; 
00483 
00484         ClipCursorArea(x1, x2, y1, y2, addx1, addx2, addy);
00485 
00486         dataPos = addy * CURSORX;
00487         for (y=y1; y<=y2; y++) {
00488             dataPos += addx1;
00489             for (x=x1; x<=x2; x++) {
00490                 INT10_PutPixel((Bit16u)x,(Bit16u)y,mouse.page,mouse.backData[dataPos++]);
00491             }
00492             dataPos += addx2;
00493         }
00494         mouse.background = false;
00495     }
00496     RestoreVgaRegisters();
00497 }
00498 
00499 void DrawCursor() {
00500     if (mouse.hidden || mouse.inhibit_draw) return;
00501     INT10_SetCurMode();
00502     // In Textmode ?
00503     if (CurMode->type==M_TEXT) {
00504         DrawCursorText();
00505         return;
00506     }
00507 
00508     // Check video page. Seems to be ignored for text mode. 
00509     // hence the text mode handled above this
00510     if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)!=mouse.page) return;
00511 // Check if cursor in update region
00512 /*  if ((POS_X >= mouse.updateRegion_x[0]) && (POS_X <= mouse.updateRegion_x[1]) &&
00513         (POS_Y >= mouse.updateRegion_y[0]) && (POS_Y <= mouse.updateRegion_y[1])) {
00514         if (CurMode->type==M_TEXT16)
00515             RestoreCursorBackgroundText();
00516         else
00517             RestoreCursorBackground();
00518         mouse.shown--;
00519         return;
00520     }
00521    */ /*Not sure yet what to do update region should be set to ??? */
00522          
00523     // Get Clipping ranges
00524 
00525 
00526     mouse.clipx = (Bit16s)((Bits)CurMode->swidth-1);    /* Get from bios ? */
00527     mouse.clipy = (Bit16s)((Bits)CurMode->sheight-1);
00528 
00529     /* might be vidmode == 0x13?2:1 */
00530     Bit16s xratio = 640;
00531     if (CurMode->swidth>0) xratio/=(Bit16u)CurMode->swidth;
00532     if (xratio==0) xratio = 1;
00533     
00534     RestoreCursorBackground();
00535 
00536     SaveVgaRegisters();
00537 
00538     // Save Background
00539     Bit16s x,y;
00540     Bit16u addx1,addx2,addy;
00541     Bit16u dataPos  = 0;
00542     Bit16s x1       = POS_X / xratio - mouse.hotx;
00543     Bit16s y1       = POS_Y - mouse.hoty;
00544     Bit16s x2       = x1 + CURSORX - 1;
00545     Bit16s y2       = y1 + CURSORY - 1; 
00546 
00547     ClipCursorArea(x1,x2,y1,y2, addx1, addx2, addy);
00548 
00549     dataPos = addy * CURSORX;
00550     for (y=y1; y<=y2; y++) {
00551         dataPos += addx1;
00552         for (x=x1; x<=x2; x++) {
00553             INT10_GetPixel((Bit16u)x,(Bit16u)y,mouse.page,&mouse.backData[dataPos++]);
00554         }
00555         dataPos += addx2;
00556     }
00557     mouse.background= true;
00558     mouse.backposx  = POS_X / xratio - mouse.hotx;
00559     mouse.backposy  = POS_Y - mouse.hoty;
00560 
00561     // Draw Mousecursor
00562     dataPos = addy * CURSORX;
00563     for (y=y1; y<=y2; y++) {
00564         Bit16u scMask = mouse.screenMask[addy+y-y1];
00565         Bit16u cuMask = mouse.cursorMask[addy+y-y1];
00566         if (addx1>0) { scMask<<=addx1; cuMask<<=addx1; dataPos += addx1; }
00567         for (x=x1; x<=x2; x++) {
00568             Bit8u pixel = 0;
00569             // ScreenMask
00570             if (scMask & HIGHESTBIT) pixel = mouse.backData[dataPos];
00571             scMask<<=1;
00572             // CursorMask
00573             if (cuMask & HIGHESTBIT) pixel = pixel ^ 0x0F;
00574             cuMask<<=1;
00575             // Set Pixel
00576             INT10_PutPixel((Bit16u)x,(Bit16u)y,mouse.page,pixel);
00577             dataPos++;
00578         }
00579         dataPos += addx2;
00580     }
00581     RestoreVgaRegisters();
00582 }
00583 
00584 void pc98_mouse_movement_apply(int x,int y);
00585 
00586 #if !defined(C_SDL2)
00587 bool GFX_IsFullscreen(void);
00588 #else
00589 static inline bool GFX_IsFullscreen(void) {
00590     return false;
00591 }
00592 #endif
00593 
00594 extern int  user_cursor_x,  user_cursor_y;
00595 extern int  user_cursor_sw, user_cursor_sh;
00596 extern bool user_cursor_locked;
00597 
00598 /* FIXME: Re-test this code */
00599 void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate) {
00600     extern bool Mouse_Vertical;
00601     float dx = xrel * mouse.pixelPerMickey_x;
00602     float dy = (Mouse_Vertical?-yrel:yrel) * mouse.pixelPerMickey_y;
00603 
00604     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00605         KEYBOARD_AUX_Event(xrel,yrel,mouse.buttons,mouse.scrollwheel);
00606         mouse.scrollwheel = 0;
00607         return;
00608     }
00609 
00610     if((fabs(xrel) > 1.0) || (mouse.senv_x < 1.0)) dx *= mouse.senv_x;
00611     if((fabs(yrel) > 1.0) || (mouse.senv_y < 1.0)) dy *= mouse.senv_y;
00612     if (useps2callback) dy *= 2;    
00613 
00614     if (user_cursor_locked) {
00615         /* either device reports relative motion ONLY, and therefore requires that the user
00616          * has captured the mouse */
00617 
00618         /* serial mouse */
00619         on_mouse_event_for_serial((int)(dx),(int)(dy*2),mouse.buttons);
00620 
00621         /* PC-98 mouse */
00622         if (IS_PC98_ARCH) pc98_mouse_movement_apply(xrel,yrel);
00623 
00624         mouse.mickey_x += (dx * mouse.mickeysPerPixel_x);
00625         mouse.mickey_y += (dy * mouse.mickeysPerPixel_y);
00626         if (mouse.mickey_x >= 32768.0) mouse.mickey_x -= 65536.0;
00627         else if (mouse.mickey_x <= -32769.0) mouse.mickey_x += 65536.0;
00628         if (mouse.mickey_y >= 32768.0) mouse.mickey_y -= 65536.0;
00629         else if (mouse.mickey_y <= -32769.0) mouse.mickey_y += 65536.0;
00630     }
00631 
00632     if (emulate) {
00633         mouse.x += dx;
00634         mouse.y += dy;
00635     } else if (CurMode != NULL) {
00636         if (CurMode->type == M_TEXT) {
00637             mouse.x = x*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8;
00638             mouse.y = y*(real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1)*8;
00639         /* NTS: DeluxePaint II enhanced sets a large range (5112x3832) for VGA mode 0x12 640x480 16-color */
00640         } else {
00641             if ((mouse.max_x > 0) && (mouse.max_y > 0)) {
00642                 mouse.x = x*mouse.max_x;
00643                 mouse.y = y*mouse.max_y;
00644             } else {
00645                 mouse.x += xrel;
00646                 mouse.y += yrel;
00647             }
00648         }
00649     }
00650 
00651     /* ignore constraints if using PS2 mouse callback in the bios */
00652 
00653     if (mouse.x > mouse.max_x) mouse.x = mouse.max_x;
00654     if (mouse.x < mouse.min_x) mouse.x = mouse.min_x;
00655     if (mouse.y > mouse.max_y) mouse.y = mouse.max_y;
00656     if (mouse.y < mouse.min_y) mouse.y = mouse.min_y;
00657 
00658     /*make mouse emulated, eventually*/
00659     extern MOUSE_EMULATION user_cursor_emulation;
00660     bool emu;
00661     switch (user_cursor_emulation)
00662     {
00663     case MOUSE_EMULATION_ALWAYS:
00664         emu = true;
00665         break;
00666     case MOUSE_EMULATION_INTEGRATION:
00667         emu = !user_cursor_locked && !GFX_IsFullscreen();
00668         break;
00669     case MOUSE_EMULATION_LOCKED:
00670         emu = user_cursor_locked && !GFX_IsFullscreen();
00671         break;
00672     case MOUSE_EMULATION_NEVER:
00673     default:
00674         emu = false;
00675     }
00676     if (!emu)
00677     {
00678         auto x1 = (double)user_cursor_x / ((double)user_cursor_sw - 1);
00679         auto y1 = (double)user_cursor_y / ((double)user_cursor_sh - 1);
00680         mouse.x       = x1 * mouse.max_screen_x;
00681         mouse.y       = y1 * mouse.max_screen_y;
00682 
00683         if (mouse.x < mouse.min_x)
00684             mouse.x = mouse.min_x;
00685         if (mouse.y < mouse.min_y)
00686             mouse.y = mouse.min_y;
00687         if (mouse.x > mouse.max_x)
00688             mouse.x = mouse.max_x;
00689         if (mouse.y > mouse.max_y)
00690             mouse.y = mouse.max_y;
00691     }
00692 
00693     if (user_cursor_locked) {
00694         /* send relative PS/2 mouse motion only if the cursor is captured */
00695         mouse.ps2x += xrel;
00696         mouse.ps2y += yrel;
00697         if (mouse.ps2x >= 32768.0)       mouse.ps2x -= 65536.0;
00698         else if (mouse.ps2x <= -32769.0) mouse.ps2x += 65536.0;
00699         if (mouse.ps2y >= 32768.0)       mouse.ps2y -= 65536.0;
00700         else if (mouse.ps2y <= -32769.0) mouse.ps2y += 65536.0;
00701     }
00702 
00703     Mouse_AddEvent(MOUSE_HAS_MOVED);
00704 }
00705 
00706 uint8_t Mouse_GetButtonState(void) {
00707     return mouse.buttons;
00708 }
00709 
00710 #if defined(WIN32)
00711 char text[5000];
00712 const char* Mouse_GetSelected(int x1, int y1, int x2, int y2, int w, int h, Bit16u *textlen) {
00713         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00714         Bit16u c=0, r=0;
00715         if (IS_PC98_ARCH) {
00716                 c=80;
00717                 r=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
00718         } else {
00719                 c=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00720                 r=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
00721         }
00722         int c1=c*x1/w, r1=r*y1/h, c2=c*x2/w, r2=r*y2/h, t;
00723         if (c1>c2) {
00724                 t=c1;
00725                 c1=c2;
00726                 c2=t;
00727         }
00728         if (r1>r2) {
00729                 t=r1;
00730                 r1=r2;
00731                 r2=t;
00732         }
00733         Bit16u result=0, len=0;
00734         text[0]=0;
00735         for (int i=r1; i<=r2; i++) {
00736                 for (int j=c1; j<=c2; j++) {
00737                         if (IS_PC98_ARCH) {
00738                                 Bit16u address=((i*80)+j)*2;
00739                                 PhysPt where = CurMode->pstart+address;
00740                                 result=mem_readw(where);
00741                                 if ((result & 0xFF00u) != 0u && (result & 0xFCu) != 0x08u && result==mem_readw(where+2) && ++j<c) {
00742                                         result&=0x7F7F;
00743                                         Bit8u j1=(result%0x100)+0x20, j2=result/0x100;
00744                                         if (j1>32&&j1<127&&j2>32&&j2<127) {
00745                                                 text[len++]=(j1+1)/2+(j1<95?112:176);
00746                                                 text[len++]=j2+(j1%2?31+(j2/96):126);
00747                                         }
00748                                 } else
00749                                         text[len++]=result;
00750                         } else {
00751                                 ReadCharAttr(j,i,page,&result);
00752                                 text[len++]=result;
00753                         }
00754                 }
00755                 while (len>0&&text[len-1]==32) text[--len]=0;
00756                 if (i<r2) {
00757                         text[len++]='\r';
00758                         text[len++]='\n';
00759                 }
00760         }
00761         text[len]=0;
00762         *textlen=len;
00763         return text;
00764 }
00765 
00766 void Mouse_Select(int x1, int y1, int x2, int y2, int w, int h) {
00767         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00768         Bit16u c=0, r=0;
00769         if (IS_PC98_ARCH) {
00770                 c=80;
00771                 r=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
00772         } else {
00773                 c=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00774                 r=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
00775         }
00776         int c1=c*x1/w, r1=r*y1/h, c2=c*x2/w, r2=r*y2/h, t;
00777         if (c1>c2) {
00778                 t=c1;
00779                 c1=c2;
00780                 c2=t;
00781         }
00782         if (r1>r2) {
00783                 t=r1;
00784                 r1=r2;
00785                 r2=t;
00786         }
00787         for (int i=r1; i<=r2; i++)
00788                 for (int j=c1; j<=c2; j++) {
00789                         if (IS_PC98_ARCH) {
00790                                 Bit16u address=((i*80)+j)*2;
00791                                 PhysPt where = CurMode->pstart+address;
00792                 mem_writeb(where+0x2000,mem_readb(where+0x2000)^16);
00793                         } else
00794                                 real_writeb(0xb800,(i*c+j)*2+1,real_readb(0xb800,(i*c+j)*2+1)^119);
00795                 }
00796 }
00797 
00798 void Restore_Text(int x1, int y1, int x2, int y2, int w, int h) {
00799         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00800         Bit16u c=0, r=0;
00801         if (IS_PC98_ARCH) {
00802                 c=80;
00803                 r=real_readb(0x60,0x113) & 0x01 ? 25 : 20;
00804         } else {
00805                 c=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00806                 r=(Bit16u)real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
00807         }
00808         int c1=c*x1/w, r1=r*y1/h, c2=c*x2/w, r2=r*y2/h, t;
00809         if (c1>c2) {
00810                 t=c1;
00811                 c1=c2;
00812                 c2=t;
00813         }
00814         if (r1>r2) {
00815                 t=r1;
00816                 r1=r2;
00817                 r2=t;
00818         }
00819         for (int i=r1; i<=r2; i++)
00820                 for (int j=c1; j<=c2; j++) {
00821                         if (IS_PC98_ARCH) {
00822                                 Bit16u address=((i*80)+j)*2;
00823                                 PhysPt where = CurMode->pstart+address;
00824                 mem_writeb(where+0x2000,mem_readb(where+0x2000)^16);
00825                         } else
00826                                 real_writeb(0xb800,(i*c+j)*2+1,real_readb(0xb800,(i*c+j)*2+1)^119);
00827                 }
00828 }
00829 #endif
00830 
00831 void Mouse_ButtonPressed(Bit8u button) {
00832     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00833         switch (button) {
00834             case 0:
00835                 mouse.buttons|=1;
00836                 break;
00837             case 1:
00838                 mouse.buttons|=2;
00839                 break;
00840             case 2:
00841                 mouse.buttons|=4;
00842                 break;
00843             default:
00844                 return;
00845         }
00846 
00847         KEYBOARD_AUX_Event(0,0,mouse.buttons,mouse.scrollwheel);
00848         mouse.scrollwheel = 0;
00849         return;
00850     }
00851 
00852     if (button > 2)
00853         return;
00854 
00855     switch (button) {
00856 #if (MOUSE_BUTTONS >= 1)
00857     case 0:
00858         if (mouse.buttons&1) return;
00859         mouse.buttons|=1;
00860         Mouse_AddEvent(MOUSE_LEFT_PRESSED);
00861         break;
00862 #endif
00863 #if (MOUSE_BUTTONS >= 2)
00864     case 1:
00865         if (mouse.buttons&2) return;
00866         mouse.buttons|=2;
00867         Mouse_AddEvent(MOUSE_RIGHT_PRESSED);
00868         break;
00869 #endif
00870 #if (MOUSE_BUTTONS >= 3)
00871     case 2:
00872         if (mouse.buttons&4) return;
00873         mouse.buttons|=4;
00874         Mouse_AddEvent(MOUSE_MIDDLE_PRESSED);
00875         break;
00876 #endif
00877     default:
00878         return;
00879     }
00880     mouse.times_pressed[button]++;
00881     mouse.last_pressed_x[button]=(Bit16u)POS_X;
00882     mouse.last_pressed_y[button]=(Bit16u)POS_Y;
00883 
00884     /* serial mouse, if connected, also wants to know about it */
00885     on_mouse_event_for_serial(0,0,mouse.buttons);
00886 }
00887 
00888 void Mouse_ButtonReleased(Bit8u button) {
00889     if (!IS_PC98_ARCH && KEYBOARD_AUX_Active()) {
00890         switch (button) {
00891             case 0:
00892                 mouse.buttons&=~1;
00893                 break;
00894             case 1:
00895                 mouse.buttons&=~2;
00896                 break;
00897             case 2:
00898                 mouse.buttons&=~4;
00899                 break;
00900             case (100-1):   /* scrollwheel up */
00901                 mouse.scrollwheel -= 8;
00902                 break;
00903             case (100+1):   /* scrollwheel down */
00904                 mouse.scrollwheel += 8;
00905                 break;
00906             default:
00907                 return;
00908         }
00909 
00910         KEYBOARD_AUX_Event(0,0,mouse.buttons,mouse.scrollwheel);
00911         mouse.scrollwheel = 0;
00912         return;
00913     }
00914 
00915     if (button > 2)
00916         return;
00917 
00918     switch (button) {
00919 #if (MOUSE_BUTTONS >= 1)
00920     case 0:
00921         if (!(mouse.buttons&1)) return;
00922         mouse.buttons&=~1;
00923         Mouse_AddEvent(MOUSE_LEFT_RELEASED);
00924         break;
00925 #endif
00926 #if (MOUSE_BUTTONS >= 2)
00927     case 1:
00928         if (!(mouse.buttons&2)) return;
00929         mouse.buttons&=~2;
00930         Mouse_AddEvent(MOUSE_RIGHT_RELEASED);
00931         break;
00932 #endif
00933 #if (MOUSE_BUTTONS >= 3)
00934     case 2:
00935         if (!(mouse.buttons&4)) return;
00936         mouse.buttons&=~4;
00937         Mouse_AddEvent(MOUSE_MIDDLE_RELEASED);
00938         break;
00939 #endif
00940     default:
00941         return;
00942     }
00943     mouse.times_released[button]++; 
00944     mouse.last_released_x[button]=(Bit16u)POS_X;
00945     mouse.last_released_y[button]=(Bit16u)POS_Y;
00946 
00947     /* serial mouse, if connected, also wants to know about it */
00948     on_mouse_event_for_serial(0,0,mouse.buttons);
00949 }
00950 
00951 static void Mouse_SetMickeyPixelRate(Bit16s px, Bit16s py){
00952     if ((px!=0) && (py!=0)) {
00953         mouse.mickeysPerPixel_x  = (float)px/X_MICKEY;
00954         mouse.mickeysPerPixel_y  = (float)py/Y_MICKEY;
00955         mouse.pixelPerMickey_x   = X_MICKEY/(float)px;
00956         mouse.pixelPerMickey_y   = Y_MICKEY/(float)py;  
00957     }
00958 }
00959 
00960 static void Mouse_SetSensitivity(Bit16u px, Bit16u py, Bit16u dspeed){
00961     if(px>100) px=100;
00962     if(py>100) py=100;
00963     if(dspeed>100) dspeed=100;
00964     // save values
00965     mouse.senv_x_val=px;
00966     mouse.senv_y_val=py;
00967     mouse.dspeed_val=dspeed;
00968     if ((px!=0) && (py!=0)) {
00969         px--;  //Inspired by cutemouse 
00970         py--;  //Although their cursor update routine is far more complex then ours
00971         mouse.senv_x=(static_cast<float>(px)*px)/3600.0f +1.0f/3.0f;
00972         mouse.senv_y=(static_cast<float>(py)*py)/3600.0f +1.0f/3.0f;
00973      }
00974 }
00975 
00976 
00977 static void Mouse_ResetHardware(void){
00978     if (MOUSE_IRQ != 0)
00979         PIC_SetIRQMask(MOUSE_IRQ,false);
00980 
00981     if (IS_PC98_ARCH) {
00982         IO_WriteB(0x7FDD,IO_ReadB(0x7FDD) & (~0x10)); // remove interrupt inhibit
00983 
00984         // NEC MOUSE.COM behavior: Driver startup and INT 33h AX=0 automatically show the graphics layer.
00985         // Some games by "Orange House" depend on this behavior, without which the graphics are invisible.
00986         if (en_int33_pc98_show_graphics) {
00987             reg_eax = 0x40u << 8u; // AH=40h show graphics layer
00988             CALLBACK_RunRealInt(0x18);
00989         }
00990     }
00991 }
00992 
00993 void Mouse_BeforeNewVideoMode(bool setmode) {
00994     (void)setmode;//unused
00995 
00996     if (CurMode->type!=M_TEXT) RestoreCursorBackground();
00997     else RestoreCursorBackgroundText();
00998     if (!mouse.hidden) {
00999         mouse.hidden = 1;
01000         mouse.hidden_at = PIC_FullIndex();
01001     }
01002     mouse.oldhidden = 1;
01003     mouse.background = false;
01004 }
01005 
01006 //Does way to much. Many things should be moved to mouse reset one day
01007 void Mouse_AfterNewVideoMode(bool setmode) {
01008     mouse.inhibit_draw = false;
01009     /* Get the correct resolution from the current video mode */
01010     Bit8u mode = mem_readb(BIOS_VIDEO_MODE);
01011     if (setmode && mode == mouse.mode) LOG(LOG_MOUSE,LOG_NORMAL)("New video mode is the same as the old");
01012     mouse.first_range_setx = false;
01013     mouse.first_range_sety = false;
01014     mouse.gran_x = (Bit16s)0xffff;
01015     mouse.gran_y = (Bit16s)0xffff;
01016 
01017     /* If new video mode is SVGA and this is NOT a mouse driver reset, then do not reset min/max.
01018      * This is needed for "down-by-the-laituri-peli" (some sort of Finnish band tour simulation?)
01019      * which sets the min/max range and THEN sets up 640x480 256-color mode through the VESA BIOS.
01020      * Without this fix, the cursor is constrained to the upper left hand quadrant of the screen. */
01021     if (!setmode || mode <= 0x13/*non-SVGA*/) {
01022         mouse.min_x = 0;
01023         mouse.max_x = 639;
01024         mouse.min_y = 0;
01025         mouse.max_y = 479;
01026     }
01027 
01028     if (machine == MCH_HERC) {
01029         // DeluxePaint II again...
01030         mouse.first_range_setx = true;
01031         mouse.first_range_sety = true;
01032     }
01033 
01034     switch (mode) {
01035     case 0x00:
01036     case 0x01:
01037     case 0x02:
01038     case 0x03:
01039     case 0x07: {
01040         mouse.gran_x = (mode<2)?0xfff0:0xfff8;
01041         mouse.gran_y = (Bit16s)0xfff8;
01042         if (IS_PC98_ARCH) {
01043             mouse.max_y = 400 - 1;
01044         }
01045         else {
01046             Bitu rows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS);
01047             if ((rows == 0) || (rows > 250)) rows = 25 - 1;
01048             mouse.max_y = 8*(rows+1) - 1;
01049         }
01050         break;
01051     }
01052     case 0x04:
01053     case 0x05:
01054     case 0x06:
01055     case 0x08:
01056     case 0x09:
01057     case 0x0a:
01058     case 0x0d:
01059     case 0x0e:
01060     case 0x13:
01061         if (mode == 0x0d || mode == 0x13) mouse.gran_x = (Bit16s)0xfffe;
01062         mouse.max_y = 199;
01063         // some games redefine the mouse range for this mode
01064         mouse.first_range_setx = true;
01065         mouse.first_range_sety = true;
01066         break;
01067     case 0x0f:
01068     case 0x10:
01069         mouse.max_y = 349;
01070         // Deluxepaint II enhanced will redefine the range for 640x480 mode
01071         mouse.first_range_setx = true;
01072         mouse.first_range_sety = true;
01073         break;
01074     case 0x11:
01075     case 0x12:
01076         mouse.max_y = 479;
01077         // Deluxepaint II enhanced will redefine the range for 640x480 mode
01078         mouse.first_range_setx = true;
01079         mouse.first_range_sety = true;
01080         break;
01081     default:
01082         LOG(LOG_MOUSE,LOG_ERROR)("Unhandled videomode %X on reset",mode);
01083         mouse.inhibit_draw = true;
01084         /* If new video mode is SVGA and this is NOT a mouse driver reset, then do not reset min/max.
01085          * This is needed for "down-by-the-laituri-peli" (some sort of Finnish band tour simulation?)
01086          * which sets the min/max range and THEN sets up 640x480 256-color mode through the VESA BIOS.
01087          * Without this fix, the cursor is constrained to the upper left hand quadrant of the screen. */
01088         if (!setmode) {
01089             if (CurMode != NULL) {
01090                 mouse.first_range_setx = true;
01091                 mouse.first_range_sety = true;
01092                 mouse.max_x = CurMode->swidth - 1;
01093                 mouse.max_y = CurMode->sheight - 1;
01094             }
01095             else {
01096                 mouse.max_x = 639;
01097                 mouse.max_y = 479;
01098             }
01099         }
01100         break;
01101     }
01102     mouse.mode = mode;
01103 
01104     if (cell_granularity_disable) {
01105         mouse.gran_x = (Bit16s)0xffff;
01106         mouse.gran_y = (Bit16s)0xffff;
01107     }
01108 
01109     mouse.events = 0;
01110     mouse.timer_in_progress = false;
01111     PIC_RemoveEvents(MOUSE_Limit_Events);
01112 
01113     mouse.hotx       = 0;
01114     mouse.hoty       = 0;
01115     mouse.background = false;
01116     mouse.screenMask = defaultScreenMask;
01117     mouse.cursorMask = defaultCursorMask;
01118     mouse.textAndMask= defaultTextAndMask;
01119     mouse.textXorMask= defaultTextXorMask;
01120     mouse.language   = 0;
01121     mouse.page               = 0;
01122     mouse.doubleSpeedThreshold = 64;
01123     mouse.updateRegion_y[1] = -1; //offscreen
01124     mouse.cursorType = 0;
01125     mouse.enabled=true;
01126 
01127     mouse.max_screen_x = mouse.max_x;
01128     mouse.max_screen_y = mouse.max_y;
01129 }
01130 
01131 //Much too empty, Mouse_NewVideoMode contains stuff that should be in here
01132 static void Mouse_Reset(void) {
01133     Mouse_BeforeNewVideoMode(false);
01134     Mouse_AfterNewVideoMode(false);
01135     Mouse_SetMickeyPixelRate(8,16);
01136 
01137     mouse.mickey_x = 0;
01138     mouse.mickey_y = 0;
01139 
01140     mouse.buttons = 0;
01141 
01142     for (Bit16u but=0; but<MOUSE_BUTTONS; but++) {
01143         mouse.times_pressed[but] = 0;
01144         mouse.times_released[but] = 0;
01145         mouse.last_pressed_x[but] = 0;
01146         mouse.last_pressed_y[but] = 0;
01147         mouse.last_released_x[but] = 0;
01148         mouse.last_released_y[but] = 0;
01149     }
01150 
01151     // Dont set max coordinates here. it is done by SetResolution!
01152     mouse.x = static_cast<float>((mouse.max_x + 1)/ 2);
01153     mouse.y = static_cast<float>((mouse.max_y + 1)/ 2);
01154     mouse.sub_mask = 0;
01155     mouse.in_UIR = false;
01156 }
01157 
01158 static Bitu INT33_Handler(void) {
01159 //  LOG(LOG_MOUSE,LOG_NORMAL)("MOUSE: %04X %X %X %d %d",reg_ax,reg_bx,reg_cx,POS_X,POS_Y);
01160     switch (reg_ax) {
01161     case 0x00:  /* Reset Driver and Read Status */
01162         Mouse_ResetHardware();
01163         goto software_reset;
01164     case 0x01:  /* Show Mouse */
01165         if (mouse.hidden) mouse.hidden--;
01166         mouse.updateRegion_y[1] = -1; //offscreen
01167         Mouse_AutoLock(true);
01168         DrawCursor();
01169         break;
01170     case 0x02:  /* Hide Mouse */
01171         {
01172             if (CurMode->type != M_TEXT) RestoreCursorBackground();
01173             else RestoreCursorBackgroundText();
01174             if (mouse.hidden == 0) mouse.hidden_at = PIC_FullIndex();
01175             mouse.hidden++;
01176         }
01177         break;
01178     case 0x03:  /* Return position and Button Status */
01179         reg_bx = mouse.buttons;
01180         reg_cx = (Bit16u)POS_X;
01181         reg_dx = (Bit16u)POS_Y;
01182         mouse.first_range_setx = false;
01183         mouse.first_range_sety = false;
01184         if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01185         break;
01186     case 0x04:  /* Position Mouse */
01187         /* If position isn't different from current position
01188          * don't change it then. (as position is rounded so numbers get
01189          * lost when the rounded number is set) (arena/simulation Wolf) */
01190         if ((Bit16s)reg_cx >= mouse.max_x) mouse.x = static_cast<float>(mouse.max_x);
01191         else if (mouse.min_x >= (Bit16s)reg_cx) mouse.x = static_cast<float>(mouse.min_x);
01192         else if ((Bit16s)reg_cx != POS_X) mouse.x = static_cast<float>(reg_cx);
01193 
01194         if ((Bit16s)reg_dx >= mouse.max_y) mouse.y = static_cast<float>(mouse.max_y);
01195         else if (mouse.min_y >= (Bit16s)reg_dx) mouse.y = static_cast<float>(mouse.min_y);
01196         else if ((Bit16s)reg_dx != POS_Y) mouse.y = static_cast<float>(reg_dx);
01197         DrawCursor();
01198         if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01199         break;
01200     case 0x05:  /* Return Button Press Data */
01201         {
01202             Bit16u but = reg_bx;
01203             reg_ax = mouse.buttons;
01204             if (but >= MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
01205             reg_cx = mouse.last_pressed_x[but];
01206             reg_dx = mouse.last_pressed_y[but];
01207             reg_bx = mouse.times_pressed[but];
01208             mouse.times_pressed[but] = 0;
01209             if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01210             break;
01211         }
01212     case 0x06:  /* Return Button Release Data */
01213         {
01214             Bit16u but = reg_bx;
01215             reg_ax = mouse.buttons;
01216             if (but >= MOUSE_BUTTONS) but = MOUSE_BUTTONS - 1;
01217             reg_cx = mouse.last_released_x[but];
01218             reg_dx = mouse.last_released_y[but];
01219             reg_bx = mouse.times_released[but];
01220             mouse.times_released[but] = 0;
01221             if (en_int33_hide_if_polling) int33_last_poll = PIC_FullIndex();
01222             break;
01223         }
01224     case 0x07:  /* Define horizontal cursor range */
01225         {
01226             //Lemmings sets 1-640 and wants that. Ironseed sets 0-640 but doesn't like 640
01227             //Ironseed works if newvideo mode with mode 13 sets 0-639
01228             //Larry 6 actually wants newvideo mode with mode 13 to set it to 0-319
01229             Bit16s max, min;
01230             if ((Bit16s)reg_cx < (Bit16s)reg_dx) { min = (Bit16s)reg_cx; max = (Bit16s)reg_dx; }
01231             else { min = (Bit16s)reg_dx; max = (Bit16s)reg_cx; }
01232             mouse.min_x = min;
01233             mouse.max_x = max;
01234             /* Battle Chess wants this */
01235             if (mouse.x > mouse.max_x) mouse.x = mouse.max_x;
01236             if (mouse.x < mouse.min_x) mouse.x = mouse.min_x;
01237             /* Or alternatively this:
01238             mouse.x = (mouse.max_x - mouse.min_x + 1)/2;*/
01239             LOG(LOG_MOUSE, LOG_NORMAL)("Define Horizontal range min:%d max:%d", min, max);
01240 
01241             /* NTS: The mouse in VESA BIOS modes would ideally start with the x and y ranges
01242              *      that fit the screen, but I'm not so sure mouse drivers even pay attention
01243              *      to VESA BIOS modes so it's not certain what comes out. However some
01244              *      demoscene productions like "Aqua" will set their own mouse range and draw
01245              *      their own cursor. The menu in "Aqua" will set up 640x480 256-color mode
01246              *      and then set a mouse range of x=0-1279 and y=0-479. Using the FIRST range
01247              *      set after mode set is the only way to make sure mouse pointer integration
01248              *      tracks the guest pointer properly. */
01249             if (mouse.first_range_setx || mouse.buttons == 0) {
01250                 if (mouse.min_x == 0 && mouse.max_x > 0) {
01251                     // most games redefine the range so they can use a saner range matching the screen
01252                     Bit16s nval = mouse.max_x;
01253 
01254                     if (CurMode->type == M_TEXT) {
01255                         // Text is reported as if each row is 8 lines high (CGA compat) even if EGA 14-line
01256                         // or VGA 16-line, and 8 pixels wide even if EGA/VGA 9-pixels/char is enabled.
01257                         //
01258                         // Apply sanity rounding.
01259                         //
01260                         // FreeDOS EDIT: The max is set to just under 640x400, so that the cursor only has
01261                         //               room for ONE PIXEL in the last row and column.
01262                         if (nval >= ((Bit16s)(CurMode->twidth*8) - 32) && nval <= ((Bit16s)(CurMode->twidth*8) + 32))
01263                             nval = (Bit16s)CurMode->twidth*8;
01264                     }
01265                     else {
01266                         // Apply sanity rounding.
01267                         //
01268                         // Daggerfall: Sets max to 310 instead of 320, probably to prevent drawing the cursor
01269                         //             partially offscreen. */
01270                         if (nval >= ((Bit16s)CurMode->swidth - 32) && nval <= ((Bit16s)CurMode->swidth + 32))
01271                             nval = (Bit16s)CurMode->swidth;
01272                         else if (nval >= (((Bit16s)CurMode->swidth - 32) * 2) && nval <= (((Bit16s)CurMode->swidth + 32) * 2))
01273                             nval = (Bit16s)CurMode->swidth * 2;
01274                     }
01275 
01276                     if (mouse.max_screen_x != nval) {
01277                         mouse.max_screen_x = nval;
01278                         LOG(LOG_MOUSE, LOG_NORMAL)("Define Horizontal range min:%d max:%d defines the bounds of the screen", min, max);
01279                     }
01280                 }
01281                 mouse.first_range_setx = false;
01282             }
01283         }
01284         break;
01285     case 0x08:  /* Define vertical cursor range */
01286         {
01287             // Not sure what to take instead of the CurMode (see case 0x07 as well)
01288             // especially the cases where sheight= 400 and we set it with the mouse_reset to 200
01289             // disabled it at the moment. Seems to break Syndicate which wants 400 in mode 13
01290             Bit16s max, min;
01291             if ((Bit16s)reg_cx < (Bit16s)reg_dx) { min = (Bit16s)reg_cx; max = (Bit16s)reg_dx; }
01292             else { min = (Bit16s)reg_dx; max = (Bit16s)reg_cx; }
01293             mouse.min_y = min;
01294             mouse.max_y = max;
01295             /* Battle Chess wants this */
01296             if (mouse.y > mouse.max_y) mouse.y = mouse.max_y;
01297             if (mouse.y < mouse.min_y) mouse.y = mouse.min_y;
01298             /* Or alternatively this:
01299             mouse.y = (mouse.max_y - mouse.min_y + 1)/2;*/
01300             LOG(LOG_MOUSE, LOG_NORMAL)("Define Vertical range min:%d max:%d", min, max);
01301 
01302             /* NTS: The mouse in VESA BIOS modes would ideally start with the x and y ranges
01303              *      that fit the screen, but I'm not so sure mouse drivers even pay attention
01304              *      to VESA BIOS modes so it's not certain what comes out. However some
01305              *      demoscene productions like "Aqua" will set their own mouse range and draw
01306              *      their own cursor. The menu in "Aqua" will set up 640x480 256-color mode
01307              *      and then set a mouse range of x=0-1279 and y=0-479. Using the FIRST range
01308              *      set after mode set is the only way to make sure mouse pointer integration
01309              *      tracks the guest pointer properly. */
01310             if (mouse.first_range_sety || mouse.buttons == 0) {
01311                 if (mouse.min_y == 0 && mouse.max_y > 0) {
01312                     // most games redefine the range so they can use a saner range matching the screen
01313                     Bit16s nval = mouse.max_y;
01314 
01315                     if (CurMode->type == M_TEXT) {
01316                         // Text is reported as if each row is 8 lines high (CGA compat) even if EGA 14-line
01317                         // or VGA 16-line, and 8 pixels wide even if EGA/VGA 9-pixels/char is enabled.
01318                         //
01319                         // Apply sanity rounding.
01320                         //
01321                         // FreeDOS EDIT: The max is set to just under 640x400, so that the cursor only has
01322                         //               room for ONE PIXEL in the last row and column.
01323                         if (nval >= ((Bit16s)(CurMode->theight*8) - 32) && nval <= ((Bit16s)(CurMode->theight*8) + 32))
01324                             nval = (Bit16s)CurMode->theight*8;
01325                     }
01326                     else {
01327                         // Apply sanity rounding.
01328                         //
01329                         // Daggerfall: Sets max to 310 instead of 320, probably to prevent drawing the cursor
01330                         //             partially offscreen. */
01331                         if (nval >= ((Bit16s)CurMode->sheight - 32) && nval <= ((Bit16s)CurMode->sheight + 32))
01332                             nval = (Bit16s)CurMode->sheight;
01333                         else if (nval >= (((Bit16s)CurMode->sheight - 32) * 2) && nval <= (((Bit16s)CurMode->sheight + 32) * 2))
01334                             nval = (Bit16s)CurMode->sheight * 2;
01335                     }
01336 
01337                     if (mouse.max_screen_y != nval) {
01338                         mouse.max_screen_y = nval;
01339                         LOG(LOG_MOUSE, LOG_NORMAL)("Define Vertical range min:%d max:%d defines the bounds of the screen", min, max);
01340                     }
01341                 }
01342                 mouse.first_range_sety = false;
01343             }
01344         }
01345         break;
01346     case 0x09:  /* Define GFX Cursor */
01347         {
01348             PhysPt src = SegPhys(es) + reg_dx;
01349             MEM_BlockRead(src, userdefScreenMask, CURSORY * 2);
01350             MEM_BlockRead(src + CURSORY * 2, userdefCursorMask, CURSORY * 2);
01351             mouse.screenMask = userdefScreenMask;
01352             mouse.cursorMask = userdefCursorMask;
01353             mouse.hotx = (Bit16s)reg_bx;
01354             mouse.hoty = (Bit16s)reg_cx;
01355             mouse.cursorType = 2;
01356             DrawCursor();
01357             break;
01358         }
01359     case 0x0a:  /* Define Text Cursor */
01360         mouse.cursorType = (reg_bx ? 1 : 0);
01361         mouse.textAndMask = reg_cx;
01362         mouse.textXorMask = reg_dx;
01363         if (reg_bx) {
01364             INT10_SetCursorShape(reg_cl, reg_dl);
01365             LOG(LOG_MOUSE, LOG_NORMAL)("Hardware Text cursor selected");
01366         }
01367         DrawCursor();
01368         break;
01369     case 0x0b:  /* Read Motion Data */
01370         {
01371             extern bool MOUSE_IsLocked();
01372             const auto locked = MOUSE_IsLocked();
01373             reg_cx = (Bit16u)static_cast<Bit16s>(locked ? mouse.mickey_x : 0);
01374             reg_dx = (Bit16u)static_cast<Bit16s>(locked ? mouse.mickey_y : 0);
01375             mouse.mickey_x = 0;
01376             mouse.mickey_y = 0;
01377             break;
01378         }
01379     case 0x0c:  /* Define interrupt subroutine parameters */
01380         mouse.sub_mask = reg_cx;
01381         mouse.sub_seg = SegValue(es);
01382         mouse.sub_ofs = reg_dx;
01383         Mouse_AutoLock(true); //Some games don't seem to reset the mouse before using
01384         break;
01385     case 0x0d:  /* Mouse light pen emulation on */
01386         LOG(LOG_MOUSE, LOG_ERROR)("Mouse light pen emulation on not implemented");
01387         break;
01388     case 0x0e:  /* Mouse light pen emulation off */
01389         LOG(LOG_MOUSE, LOG_ERROR)("Mouse light pen emulation off not implemented");
01390         break;
01391     case 0x0f:  /* Define mickey/pixel rate */
01392         Mouse_SetMickeyPixelRate((Bit16s)reg_cx, (Bit16s)reg_dx);
01393         break;
01394     case 0x10:  /* Define screen region for updating */
01395         mouse.updateRegion_x[0] = (Bit16s)reg_cx;
01396         mouse.updateRegion_y[0] = (Bit16s)reg_dx;
01397         mouse.updateRegion_x[1] = (Bit16s)reg_si;
01398         mouse.updateRegion_y[1] = (Bit16s)reg_di;
01399         DrawCursor();
01400         break;
01401     case 0x11:      /* Get number of buttons */
01402         reg_ax = 0xffff;
01403         reg_bx = MOUSE_BUTTONS;
01404         break;
01405     case 0x13:      /* Set double-speed threshold */
01406         mouse.doubleSpeedThreshold = (reg_bx ? reg_bx : 64);
01407         break;
01408     case 0x14: /* Exchange event-handler */
01409         {
01410             Bit16u oldSeg = mouse.sub_seg;
01411             Bit16u oldOfs = mouse.sub_ofs;
01412             Bit16u oldMask = mouse.sub_mask;
01413             // Set new values
01414             mouse.sub_mask = reg_cx;
01415             mouse.sub_seg = SegValue(es);
01416             mouse.sub_ofs = reg_dx;
01417             // Return old values
01418             reg_cx = (Bit16u)oldMask;
01419             reg_dx = (Bit16u)oldOfs;
01420             SegSet16(es, oldSeg);
01421         }
01422         break;
01423     case 0x15: /* Get Driver storage space requirements */
01424         reg_bx = sizeof(mouse);
01425         break;
01426     case 0x16: /* Save driver state */
01427         {
01428             LOG(LOG_MOUSE, LOG_NORMAL)("Saving driver state...");
01429             PhysPt dest = SegPhys(es) + reg_dx;
01430             MEM_BlockWrite(dest, &mouse, sizeof(mouse));
01431         }
01432         break;
01433     case 0x17: /* load driver state */
01434         {
01435             LOG(LOG_MOUSE, LOG_NORMAL)("Loading driver state...");
01436             PhysPt src = SegPhys(es) + reg_dx;
01437             MEM_BlockRead(src, &mouse, sizeof(mouse));
01438             break;
01439         }
01440     case 0x18: /* Set alternate subroutine call mask and address */
01441         LOG(LOG_MOUSE, LOG_ERROR)("Set alternate subroutine call mask and address not implemented");
01442         break;
01443     case 0x19: /* Get user alternate interrupt address*/
01444         LOG(LOG_MOUSE, LOG_ERROR)("Get user alternate interrupt address not implemented");
01445         break;
01446     case 0x1a:  /* Set mouse sensitivity */
01447         // ToDo : double mouse speed value
01448         Mouse_SetSensitivity(reg_bx, reg_cx, reg_dx);
01449         LOG(LOG_MOUSE, LOG_NORMAL)("Set sensitivity used with %d %d (%d)", reg_bx, reg_cx, reg_dx);
01450         break;
01451     case 0x1b:  /* Get mouse sensitivity */
01452         reg_bx = mouse.senv_x_val;
01453         reg_cx = mouse.senv_y_val;
01454         reg_dx = mouse.dspeed_val;
01455 
01456         LOG(LOG_MOUSE, LOG_NORMAL)("Get sensitivity %d %d", reg_bx, reg_cx);
01457         break;
01458     case 0x1c:  /* Set interrupt rate */
01459         /* Can't really set a rate this is host determined */
01460         break;
01461     case 0x1d:      /* Set display page number */
01462         mouse.page = reg_bl;
01463         break;
01464     case 0x1e:      /* Get display page number */
01465         reg_bx = mouse.page;
01466         break;
01467     case 0x1f:  /* Disable Mousedriver */
01468         /* ES:BX old mouse driver Zero at the moment TODO */
01469         reg_bx = 0;
01470         SegSet16(es, 0);
01471         mouse.enabled = false; /* Just for reporting not doing a thing with it */
01472         mouse.oldhidden = mouse.hidden;
01473         if (!mouse.hidden) {
01474             mouse.hidden = 1;
01475             mouse.hidden_at = PIC_FullIndex();
01476         }
01477         break;
01478     case 0x20:  /* Enable Mousedriver */
01479         mouse.enabled = true;
01480         mouse.hidden = mouse.oldhidden;
01481         break;
01482     case 0x21:  /* Software Reset */
01483     software_reset:
01484         extern bool Mouse_Drv;
01485         if (Mouse_Drv) {
01486             reg_ax = 0xffff;
01487             reg_bx = MOUSE_BUTTONS;
01488             Mouse_Reset();
01489             Mouse_AutoLock(true);
01490             AUX_INT33_Takeover();
01491             LOG(LOG_MOUSE, LOG_NORMAL)("INT 33h reset");
01492         }
01493         break;
01494     case 0x22:      /* Set language for messages */
01495             /*
01496              *                        Values for mouse driver language:
01497              *
01498              *                        00h     English
01499              *                        01h     French
01500              *                        02h     Dutch
01501              *                        03h     German
01502              *                        04h     Swedish
01503              *                        05h     Finnish
01504              *                        06h     Spanish
01505              *                        07h     Portugese
01506              *                        08h     Italian
01507              *
01508              */
01509         mouse.language = reg_bx;
01510         break;
01511     case 0x23:      /* Get language for messages */
01512         reg_bx = mouse.language;
01513         break;
01514     case 0x24:  /* Get Software version and mouse type */
01515         reg_bx = 0x805;   //Version 8.05 woohoo 
01516         reg_ch = 0x04;    /* PS/2 type */
01517         reg_cl = 0;       /* PS/2 (unused) */
01518         break;
01519     case 0x26: /* Get Maximum virtual coordinates */
01520         reg_bx = (mouse.enabled ? 0x0000 : 0xffff);
01521         reg_cx = (Bit16u)mouse.max_x;
01522         reg_dx = (Bit16u)mouse.max_y;
01523         break;
01524     case 0x2a:  /* Get cursor hot spot */
01525         reg_al = (Bit8u)-mouse.hidden;    // Microsoft uses a negative byte counter for cursor visibility
01526         reg_bx = (Bit16u)mouse.hotx;
01527         reg_cx = (Bit16u)mouse.hoty;
01528         reg_dx = 0x04;    // PS/2 mouse type
01529         break;
01530     case 0x31: /* Get Current Minimum/Maximum virtual coordinates */
01531         reg_ax = (Bit16u)mouse.min_x;
01532         reg_bx = (Bit16u)mouse.min_y;
01533         reg_cx = (Bit16u)mouse.max_x;
01534         reg_dx = (Bit16u)mouse.max_y;
01535         break;
01536     case 0x53C1: /* Logitech CyberMan */
01537         LOG(LOG_MOUSE, LOG_NORMAL)("Mouse function 53C1 for Logitech CyberMan called. Ignored by regular mouse driver.");
01538         break;
01539     default:
01540         LOG(LOG_MOUSE, LOG_ERROR)("Mouse Function %04X not implemented!", reg_ax);
01541         break;
01542     }
01543     return CBRET_NONE;
01544 }
01545 
01546 static Bitu MOUSE_BD_Handler(void) {
01547     // the stack contains offsets to register values
01548     Bit16u raxpt=real_readw(SegValue(ss),reg_sp+0x0a);
01549     Bit16u rbxpt=real_readw(SegValue(ss),reg_sp+0x08);
01550     Bit16u rcxpt=real_readw(SegValue(ss),reg_sp+0x06);
01551     Bit16u rdxpt=real_readw(SegValue(ss),reg_sp+0x04);
01552 
01553     // read out the actual values, registers ARE overwritten
01554     Bit16u rax=real_readw(SegValue(ds),raxpt);
01555     reg_ax=rax;
01556     reg_bx=real_readw(SegValue(ds),rbxpt);
01557     reg_cx=real_readw(SegValue(ds),rcxpt);
01558     reg_dx=real_readw(SegValue(ds),rdxpt);
01559 //  LOG_MSG("MOUSE BD: %04X %X %X %X %d %d",reg_ax,reg_bx,reg_cx,reg_dx,POS_X,POS_Y);
01560     
01561     // some functions are treated in a special way (additional registers)
01562     switch (rax) {
01563         case 0x09:  /* Define GFX Cursor */
01564         case 0x16:  /* Save driver state */
01565         case 0x17:  /* load driver state */
01566             SegSet16(es,SegValue(ds));
01567             break;
01568         case 0x0c:  /* Define interrupt subroutine parameters */
01569         case 0x14:  /* Exchange event-handler */ 
01570             if (reg_bx!=0) SegSet16(es,reg_bx);
01571             else SegSet16(es,SegValue(ds));
01572             break;
01573         case 0x10:  /* Define screen region for updating */
01574             reg_cx=real_readw(SegValue(ds),rdxpt);
01575             reg_dx=real_readw(SegValue(ds),rdxpt+2);
01576             reg_si=real_readw(SegValue(ds),rdxpt+4);
01577             reg_di=real_readw(SegValue(ds),rdxpt+6);
01578             break;
01579         default:
01580             break;
01581     }
01582 
01583     INT33_Handler();
01584 
01585     // save back the registers, too
01586     real_writew(SegValue(ds),raxpt,reg_ax);
01587     real_writew(SegValue(ds),rbxpt,reg_bx);
01588     real_writew(SegValue(ds),rcxpt,reg_cx);
01589     real_writew(SegValue(ds),rdxpt,reg_dx);
01590     switch (rax) {
01591         case 0x1f:  /* Disable Mousedriver */
01592             real_writew(SegValue(ds),rbxpt,SegValue(es));
01593             break;
01594         case 0x14: /* Exchange event-handler */ 
01595             real_writew(SegValue(ds),rcxpt,SegValue(es));
01596             break;
01597         default:
01598             break;
01599     }
01600 
01601     reg_ax=rax;
01602     return CBRET_NONE;
01603 }
01604 
01605 static Bitu INT74_Handler(void) {
01606     if (mouse.events>0 && !mouse.in_UIR) {
01607         mouse.events--;
01608 
01609         /* INT 33h emulation: HERE within the IRQ 12 handler is the appropriate place to
01610          * redraw the cursor. OSes like Windows 3.1 expect real-mode code to do it in
01611          * response to IRQ 12, not "out of the blue" from the SDL event handler like
01612          * the original DOSBox code did it. Doing this allows the INT 33h emulation
01613          * to draw the cursor while not causing Windows 3.1 to crash or behave
01614          * erratically. */
01615         if (en_int33) DrawCursor();
01616 
01617         /* Check for an active Interrupt Handler that will get called */
01618         if (mouse.sub_mask & mouse.event_queue[mouse.events].type) {
01619             reg_ax=mouse.event_queue[mouse.events].type;
01620             reg_bx=mouse.event_queue[mouse.events].buttons;
01621             reg_cx=(Bit16u)POS_X;
01622             reg_dx=(Bit16u)POS_Y;
01623             reg_si=(Bit16u)static_cast<Bit16s>(mouse.mickey_x);
01624             reg_di=(Bit16u)static_cast<Bit16s>(mouse.mickey_y);
01625             CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01626             CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback))+7);
01627             CPU_Push16(RealSeg(uir_callback));
01628             CPU_Push16(RealOff(uir_callback));
01629             CPU_Push16(mouse.sub_seg);
01630             CPU_Push16(mouse.sub_ofs);
01631             mouse.in_UIR = true;
01632         } else if (useps2callback) {
01633             CPU_Push16(RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01634             CPU_Push16(RealOff(CALLBACK_RealPointer(int74_ret_callback)));
01635             DoPS2Callback(mouse.event_queue[mouse.events].buttons, static_cast<Bit16s>(mouse.ps2x), static_cast<Bit16s>(mouse.ps2y));
01636         } else {
01637             SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01638             reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
01639         }
01640     } else {
01641         SegSet16(cs, RealSeg(CALLBACK_RealPointer(int74_ret_callback)));
01642         reg_ip = RealOff(CALLBACK_RealPointer(int74_ret_callback));
01643     }
01644     return CBRET_NONE;
01645 }
01646 
01647 Bitu INT74_Ret_Handler(void) {
01648     if (mouse.events) {
01649         if (!mouse.timer_in_progress) {
01650             mouse.timer_in_progress = true;
01651             PIC_AddEvent(MOUSE_Limit_Events,MOUSE_DELAY);
01652         }
01653     }
01654     return CBRET_NONE;
01655 }
01656 
01657 Bitu UIR_Handler(void) {
01658     mouse.in_UIR = false;
01659     return CBRET_NONE;
01660 }
01661 
01662 bool MouseTypeNone();
01663 
01664 void MOUSE_OnReset(Section *sec) {
01665     (void)sec;//UNUSED
01666     if (IS_PC98_ARCH)
01667         MOUSE_IRQ = 13; // PC-98 standard
01668     else if (!enable_slave_pic)
01669         MOUSE_IRQ = 0;
01670     else
01671         MOUSE_IRQ = 12; // IBM PC/AT standard
01672 
01673     if (MOUSE_IRQ != 0)
01674         PIC_SetIRQMask(MOUSE_IRQ,true);
01675 }
01676 
01677 void MOUSE_ShutDown(Section *sec) {
01678     (void)sec;//UNUSED
01679 }
01680 
01681 void BIOS_PS2MOUSE_ShutDown(Section *sec) {
01682     (void)sec;//UNUSED
01683 }
01684 
01685 void BIOS_PS2Mouse_Startup(Section *sec) {
01686     (void)sec;//UNUSED
01687     Section_prop *section=static_cast<Section_prop *>(control->GetSection("dos"));
01688 
01689     /* NTS: This assumes MOUSE_Init() is called after KEYBOARD_Init() */
01690     en_bios_ps2mouse = section->Get_bool("biosps2");
01691 
01692     if (!enable_slave_pic || machine == MCH_PCJR) return;
01693 
01694     if (!en_bios_ps2mouse) return;
01695 
01696     if (MouseTypeNone()) {
01697         LOG(LOG_MOUSE, LOG_WARN)("INT 15H PS/2 emulation NOT enabled. biosps2=1 but mouse type=none");
01698     }
01699     else {
01700         LOG(LOG_MOUSE, LOG_NORMAL)("INT 15H PS/2 emulation enabled");
01701         bios_enable_ps2();
01702     }
01703 
01704     ps2_callback_save_regs = section->Get_bool("int15 mouse callback does not preserve registers");
01705 
01706     // Callback for ps2 irq
01707     call_int74=CALLBACK_Allocate();
01708     CALLBACK_Setup(call_int74,&INT74_Handler,CB_IRQ12,"int 74");
01709     // pseudocode for CB_IRQ12:
01710     //  sti
01711     //  push ds
01712     //  push es
01713     //  pushad
01714     //  sti
01715     //  callback INT74_Handler
01716     //      ps2 or user callback if requested
01717     //      otherwise jumps to CB_IRQ12_RET
01718     //  push ax
01719     //  mov al, 0x20
01720     //  out 0xa0, al
01721     //  out 0x20, al
01722     //  pop ax
01723     //  cld
01724     //  retf
01725 
01726     int74_ret_callback=CALLBACK_Allocate();
01727     CALLBACK_Setup(int74_ret_callback,&INT74_Ret_Handler,CB_IRQ12_RET,"int 74 ret");
01728     // pseudocode for CB_IRQ12_RET:
01729     //  cli
01730     //  mov al, 0x20
01731     //  out 0xa0, al
01732     //  out 0x20, al
01733     //  callback INT74_Ret_Handler
01734     //  popad
01735     //  pop es
01736     //  pop ds
01737     //  iret
01738 
01739     if (MOUSE_IRQ != 0) {
01740         Bit8u hwvec=(MOUSE_IRQ>7)?(0x70+MOUSE_IRQ-8):(0x8+MOUSE_IRQ);
01741         RealSetVec(hwvec,CALLBACK_RealPointer(call_int74));
01742     }
01743 
01744     // Callback for ps2 user callback handling
01745     useps2callback = false; ps2callbackinit = false;
01746     if (call_ps2 == 0)
01747         call_ps2 = CALLBACK_Allocate();
01748     CALLBACK_Setup(call_ps2,&PS2_Handler,CB_RETF,"ps2 bios callback");
01749     ps2_callback=CALLBACK_RealPointer(call_ps2);
01750 
01751     // Callback for mouse user routine return
01752     if (call_uir == 0)
01753         call_uir = CALLBACK_Allocate();
01754     CALLBACK_Setup(call_uir,&UIR_Handler,CB_RETF_CLI,"mouse uir ret");
01755     uir_callback=CALLBACK_RealPointer(call_uir);
01756 }
01757 
01758 void MOUSE_Startup(Section *sec) {
01759     (void)sec;//UNUSED
01760     Section_prop *section=static_cast<Section_prop *>(control->GetSection("dos"));
01761         Section_prop * pc98_section=static_cast<Section_prop *>(control->GetSection("pc98"));
01762     RealPt i33loc=0;
01763 
01764     /* TODO: Needs to check for mouse, and fail to do anything if neither PS/2 nor serial mouse emulation enabled */
01765 
01766     en_int33_hide_if_intsub=section->Get_bool("int33 hide host cursor if interrupt subroutine");
01767 
01768     en_int33_hide_if_polling=section->Get_bool("int33 hide host cursor when polling");
01769 
01770     en_int33_pc98_show_graphics=pc98_section->Get_bool("pc-98 show graphics layer on initialize");
01771 
01772     en_int33=section->Get_bool("int33");
01773     if (!en_int33) {
01774         Mouse_Reset();
01775         Mouse_SetSensitivity(50,50,50);
01776         return;
01777     }
01778 
01779     cell_granularity_disable=section->Get_bool("int33 disable cell granularity");
01780 
01781     LOG(LOG_MOUSE, LOG_NORMAL)("INT 33H emulation enabled");
01782     if (en_int33_hide_if_polling)
01783         LOG(LOG_MOUSE, LOG_NORMAL)("INT 33H emulation will hide host cursor if polling");
01784 
01785     // Callback for mouse interrupt 0x33
01786     call_int33=CALLBACK_Allocate();
01787 
01788     // i33loc=RealMake(CB_SEG+1,(call_int33*CB_SIZE)-0x10);
01789     i33loc=RealMake(DOS_GetMemory(0x1,"i33loc")-1,0x10);
01790     CALLBACK_Setup(call_int33,&INT33_Handler,CB_MOUSE,Real2Phys(i33loc),"Mouse");
01791 
01792     // Wasteland needs low(seg(int33))!=0 and low(ofs(int33))!=0
01793     real_writed(0,0x33<<2,i33loc);
01794 
01795     call_mouse_bd=CALLBACK_Allocate();
01796     CALLBACK_Setup(call_mouse_bd,&MOUSE_BD_Handler,CB_RETF8,
01797         PhysMake(RealSeg(i33loc),RealOff(i33loc)+2),"MouseBD");
01798     // pseudocode for CB_MOUSE (including the special backdoor entry point):
01799     //  jump near i33hd
01800     //  callback MOUSE_BD_Handler
01801     //  retf 8
01802     //  label i33hd:
01803     //  callback INT33_Handler
01804     //  iret
01805 
01806     memset(&mouse,0,sizeof(mouse));
01807 
01808     if (!mouse.hidden) {
01809         mouse.hidden = 1;
01810         mouse.hidden_at = PIC_FullIndex();
01811     }
01812 
01813     mouse.timer_in_progress = false;
01814     mouse.mode = 0xFF; //Non existing mode
01815     mouse.scrollwheel = 0;
01816 
01817     mouse.sub_mask=0;
01818     mouse.sub_seg=0x6362;   // magic value
01819     mouse.sub_ofs=0;
01820 
01821     oldmouseX = oldmouseY = 0;
01822     mouse.ps2x = mouse.ps2y = 0;
01823 
01824     Mouse_ResetHardware();
01825     Mouse_Reset();
01826     Mouse_SetSensitivity(50,50,50);
01827 }
01828 
01829 void MOUSE_Init() {
01830     LOG(LOG_MOUSE, LOG_DEBUG)("Initializing mouse interface emulation");
01831 
01832     // 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
01833     AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(MOUSE_OnReset));
01834 }
01835 
01836 bool MOUSE_IsHidden()
01837 {
01838     /* mouse is returned hidden IF hidden for more than 100ms.
01839      * rapidly hiding/showing should not signal hidden (FreeDOS EDIT.COM) */
01840     return static_cast<bool>(mouse.hidden) && (PIC_FullIndex() >= (mouse.hidden_at + 100));
01841 }
01842 
01843 bool MOUSE_IsBeingPolled()
01844 {
01845     if (!en_int33_hide_if_polling)
01846         return false;
01847 
01848     return (PIC_FullIndex() < (int33_last_poll + 1000));
01849 }
01850 
01851 bool MOUSE_HasInterruptSub()
01852 {
01853     if (!en_int33_hide_if_intsub)
01854         return false;
01855 
01856     return (mouse.sub_mask != 0);
01857 }
01858 
01859 //save state support
01860 void *MOUSE_Limit_Events_PIC_Event = (void*)((uintptr_t)MOUSE_Limit_Events);
01861 
01862 
01863 namespace
01864 {
01865 class SerializeMouse : public SerializeGlobalPOD
01866 {
01867 public:
01868         SerializeMouse() : SerializeGlobalPOD("Mouse")
01869         {}
01870 
01871 private:
01872         virtual void getBytes(std::ostream& stream)
01873         {
01874                 Bit8u screenMask_idx, cursorMask_idx;
01875 
01876 
01877                 if( mouse.screenMask == defaultScreenMask ) screenMask_idx = 0x00;
01878                 else if( mouse.screenMask == userdefScreenMask ) screenMask_idx = 0x01;
01879 
01880                 if( mouse.cursorMask == defaultCursorMask ) cursorMask_idx = 0x00;
01881                 else if( mouse.cursorMask == userdefCursorMask ) cursorMask_idx = 0x01;
01882 
01883                 //*******************************************
01884                 //*******************************************
01885                 //*******************************************
01886 
01887                 SerializeGlobalPOD::getBytes(stream);
01888 
01889 
01890                 // - pure data
01891                 WRITE_POD( &ps2cbseg, ps2cbseg );
01892                 WRITE_POD( &ps2cbofs, ps2cbofs );
01893                 WRITE_POD( &useps2callback, useps2callback );
01894                 WRITE_POD( &ps2callbackinit, ps2callbackinit );
01895                 
01896                 WRITE_POD( &userdefScreenMask, userdefScreenMask );
01897                 WRITE_POD( &userdefCursorMask, userdefCursorMask );
01898 
01899 
01900                 // - near-pure data
01901                 WRITE_POD( &mouse, mouse );
01902 
01903                 // - pure data
01904                 WRITE_POD( &gfxReg3CE, gfxReg3CE );
01905                 WRITE_POD( &index3C4, index3C4 );
01906                 WRITE_POD( &gfxReg3C5, gfxReg3C5 );
01907 
01908                 //*******************************************
01909                 //*******************************************
01910                 //*******************************************
01911 
01912                 // - reloc ptr
01913                 WRITE_POD( &screenMask_idx, screenMask_idx );
01914                 WRITE_POD( &cursorMask_idx, cursorMask_idx );
01915         }
01916 
01917         virtual void setBytes(std::istream& stream)
01918         {
01919                 Bit8u screenMask_idx, cursorMask_idx;
01920 
01921                 //*******************************************
01922                 //*******************************************
01923                 //*******************************************
01924 
01925                 SerializeGlobalPOD::setBytes(stream);
01926 
01927                 // - pure data
01928                 READ_POD( &ps2cbseg, ps2cbseg );
01929                 READ_POD( &ps2cbofs, ps2cbofs );
01930                 READ_POD( &useps2callback, useps2callback );
01931                 READ_POD( &ps2callbackinit, ps2callbackinit );
01932                 
01933                 READ_POD( &userdefScreenMask, userdefScreenMask );
01934                 READ_POD( &userdefCursorMask, userdefCursorMask );
01935 
01936 
01937                 // - near-pure data
01938                 READ_POD( &mouse, mouse );
01939 
01940 
01941                 // - pure data
01942                 READ_POD( &gfxReg3CE, gfxReg3CE );
01943                 READ_POD( &index3C4, index3C4 );
01944                 READ_POD( &gfxReg3C5, gfxReg3C5 );
01945 
01946                 //*******************************************
01947                 //*******************************************
01948                 //*******************************************
01949 
01950                 // - reloc ptr
01951                 READ_POD( &screenMask_idx, screenMask_idx );
01952                 READ_POD( &cursorMask_idx, cursorMask_idx );
01953 
01954 
01955                 if( screenMask_idx == 0x00 ) mouse.screenMask = defaultScreenMask;
01956                 else if( screenMask_idx == 0x01 ) mouse.screenMask = userdefScreenMask;
01957 
01958                 if( cursorMask_idx == 0x00 ) mouse.cursorMask = defaultCursorMask;
01959                 else if( cursorMask_idx == 0x01 ) mouse.cursorMask = userdefCursorMask;
01960 
01961                 //*******************************************
01962                 //*******************************************
01963                 //*******************************************
01964 
01965                 // reset
01966                 oldmouseX = static_cast<Bit16s>(mouse.x);
01967                 oldmouseY = static_cast<Bit16s>(mouse.y);
01968         }
01969 } dummy;
01970 }