mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-14 02:17:36 +08:00
CPU normal core: Add segment limit check exception handling to MOVSB, MOVSW, MOVSD, and make it throw a C++ exception while restoring CPU state properly. This should fix any glitches the previous hackery caused and help resolve crashes. It should also resolve any rendering errors in Windows 3.1 if using the S3 86C928 driver, which uses segment limit exceptions to fake a linear framebuffer from hardware that can only bank switch
This commit is contained in:
@@ -519,4 +519,13 @@ public:
|
||||
Bitu faultcode;
|
||||
};
|
||||
|
||||
class GuestGenFaultException : public std::exception {
|
||||
public:
|
||||
virtual const char *what() const throw() {
|
||||
return "Guest general protection fault exception";
|
||||
}
|
||||
GuestGenFaultException() {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -178,6 +178,88 @@ void DoString(STRING_OP type) {
|
||||
|
||||
case R_MOVSB:
|
||||
do {
|
||||
if (do_seg_limits) {
|
||||
if (Segs.expanddown[core.base_val_ds]) {
|
||||
if (si_index <= SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x <= %x (E) DS:SI",(unsigned int)si_index,(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((si_index+1U-1UL) > SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x DS:SI",(unsigned int)si_index,(unsigned int)1U,
|
||||
(unsigned int)(si_index+1U-1U),(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
|
||||
if (Segs.expanddown[es]) {
|
||||
if (di_index <= SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x <= %x (E) ES:DI",(unsigned int)di_index,(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((di_index+1U-1UL) > SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x ES:DI",(unsigned int)di_index,(unsigned int)1U,
|
||||
(unsigned int)(di_index+1U-1U),(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SaveMb(di_base+di_index,LoadMb(si_base+si_index));
|
||||
di_index=(di_index+(Bitu)add_index) & add_mask;
|
||||
si_index=(si_index+(Bitu)add_index) & add_mask;
|
||||
@@ -188,6 +270,88 @@ void DoString(STRING_OP type) {
|
||||
case R_MOVSW:
|
||||
add_index<<=1;
|
||||
do {
|
||||
if (do_seg_limits) {
|
||||
if (Segs.expanddown[core.base_val_ds]) {
|
||||
if (si_index <= SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x <= %x (E) DS:SI",(unsigned int)si_index,(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((si_index+2U-1UL) > SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x DS:SI",(unsigned int)si_index,(unsigned int)2U,
|
||||
(unsigned int)(si_index+2U-1U),(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
|
||||
if (Segs.expanddown[es]) {
|
||||
if (di_index <= SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x <= %x (E) ES:DI",(unsigned int)di_index,(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((di_index+2U-1UL) > SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x ES:DI",(unsigned int)di_index,(unsigned int)2U,
|
||||
(unsigned int)(di_index+2U-1U),(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SaveMw(di_base+di_index,LoadMw(si_base+si_index));
|
||||
di_index=(di_index+(Bitu)add_index) & add_mask;
|
||||
si_index=(si_index+(Bitu)add_index) & add_mask;
|
||||
@@ -209,21 +373,83 @@ void DoString(STRING_OP type) {
|
||||
* the screen and (depending on the implementation) may go as far as scribbling on the
|
||||
* VGA BIOS at C0000h and into the UMB and DOSBox private data area! */
|
||||
if (do_seg_limits) {
|
||||
if (Segs.expanddown[core.base_val_ds]) {
|
||||
if (si_index <= SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x <= %x (E) DS:SI",(unsigned int)si_index,(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((si_index+4U-1UL) > SegLimit(core.base_val_ds)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x DS:SI",(unsigned int)si_index,(unsigned int)4U,
|
||||
(unsigned int)(si_index+4U-1U),(unsigned int)SegLimit(core.base_val_ds));
|
||||
LOG_MSG("Segment limit violation");
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
|
||||
if (Segs.expanddown[es]) {
|
||||
if (di_index <= SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x <= %x (E)",(unsigned int)di_index,(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Limit check %x <= %x (E) ES:DI",(unsigned int)di_index,(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
CPU_Exception(EXCEPTION_GP,0);
|
||||
break;
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((di_index+4U-1UL) > SegLimit(es)) {
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x",(unsigned int)di_index,(unsigned int)4U,
|
||||
LOG_MSG("Limit check %x+%x-1 = %x > %x ES:DI",(unsigned int)di_index,(unsigned int)4U,
|
||||
(unsigned int)(di_index+4U-1U),(unsigned int)SegLimit(es));
|
||||
LOG_MSG("Segment limit violation");
|
||||
CPU_Exception(EXCEPTION_GP,0);
|
||||
break;
|
||||
|
||||
/* Clean up after certain amount of instructions */
|
||||
reg_esi&=(~add_mask);
|
||||
reg_esi|=(si_index & add_mask);
|
||||
reg_edi&=(~add_mask);
|
||||
reg_edi|=(di_index & add_mask);
|
||||
if (TEST_PREFIX_REP) {
|
||||
count+=count_left;
|
||||
reg_ecx&=(~add_mask);
|
||||
reg_ecx|=(count & add_mask);
|
||||
}
|
||||
|
||||
throw GuestGenFaultException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -401,18 +401,27 @@ static Bitu Normal_Loop(void) {
|
||||
dosbox_allow_nonrecursive_page_fault = false;
|
||||
CPU_Exception(EXCEPTION_PF, pf.faultcode);
|
||||
dosbox_allow_nonrecursive_page_fault = saved_allow;
|
||||
}
|
||||
catch (int x) {
|
||||
dosbox_allow_nonrecursive_page_fault = saved_allow;
|
||||
if (x == 4/*CMOS shutdown*/) {
|
||||
ret = 0;
|
||||
}
|
||||
catch (const GuestGenFaultException& gpf) {
|
||||
Bitu FillFlags(void);
|
||||
|
||||
ret = 0;
|
||||
FillFlags();
|
||||
dosbox_allow_nonrecursive_page_fault = false;
|
||||
CPU_Exception(EXCEPTION_GP, 0);
|
||||
dosbox_allow_nonrecursive_page_fault = saved_allow;
|
||||
}
|
||||
catch (int x) {
|
||||
dosbox_allow_nonrecursive_page_fault = saved_allow;
|
||||
if (x == 4/*CMOS shutdown*/) {
|
||||
ret = 0;
|
||||
// LOG_MSG("CMOS shutdown reset acknowledged");
|
||||
}
|
||||
else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void increaseticks() { //Make it return ticksRemain and set it in the function above to remove the global variable.
|
||||
|
Reference in New Issue
Block a user