--- hatari/src/uae-cpu/newcpu.c 2019/04/09 08:53:26 1.1.1.19 +++ hatari/src/uae-cpu/newcpu.c 2019/04/09 08:54:38 1.1.1.20 @@ -110,12 +110,24 @@ /* 'while' condition (PendingInterruptCount <= 0 is true less often than STOP==0) */ /* 2011/04/29 [NP] In Exception(), check the new PC is not on odd address ; raise an address error */ /* exception if it's the case. */ +/* 2012/09/01 [NP] Add a special case to correct the stacked PC when a bus error happens during */ +/* a movem (fix the game Blood Money). */ +/* 2013/03/16 [NP] In refill_prefetch(), reload only one new word in regs.prefetch if low word is */ +/* still valid : low word goes to high word and we reload only low word */ +/* (fix EOR/ADD self modified code in the protection for the game Damocles). */ /* 2013/04/11 [NP] In Exception(), call MFP_ProcessIACK after 12 cycles to update the MFP's vector */ /* number used for the exception (see mfp.c). */ /* 2013/05/03 [NP] In Exception(), handle IACK for HBL and VBL interrupts too, allowing pending bit*/ /* to be set twice during an active video interrupt (correct fix for Super Monaco */ /* GP, Super Hang On, Monster Business, European Demo's Intro, BBC Menu 52). */ - +/* 2014/02/22 [NP] In Exception(), call valid_address() before reading the opcode at BusErrorPC, */ +/* else this will cause an unwanted "double bus error" ("Union Demo" loader). */ +/* 2014/02/22 [NP] In refill_prefetch(), use get_word() instead of do_get_mem_word() to generate */ +/* a bus error when trying to read from an invalid region. */ +/* 2014/03/18 [NP] In Exception(), add a specific case to restore the dest part of a "move" after */ +/* it was overwritten during a bus error (fix the game Dragon Flight). */ +/* 2014/04/06 [NP] In Exception(), add a special case for last_addr_for_exception_3 stored in the */ +/* stack after a bus error (fix the game Batman The Movie). */ const char NewCpu_fileid[] = "Hatari newcpu.c : " __DATE__ " " __TIME__; @@ -143,6 +155,12 @@ const char NewCpu_fileid[] = "Hatari new #include "debugcpu.h" #include "68kDisass.h" +#ifdef HAVE_CAPSIMAGE +#if CAPSIMAGE_VERSION == 5 +#include +#endif +#endif + //#define DEBUG_PREFETCH struct flag_struct regflags; @@ -862,12 +880,14 @@ void Exception(int nr, uaecptr oldpc, in nr = MFP_ProcessIACK ( nr ); CPU_IACK = false; } - else if ( 1 && ( ExceptionSource == M68000_EXC_SRC_AUTOVEC ) && ( ( nr == 26 ) || ( nr == 28 ) ) ) + else if ( ( ExceptionSource == M68000_EXC_SRC_AUTOVEC ) && ( ( nr == 26 ) || ( nr == 28 ) ) ) { M68000_AddCycles ( CPU_IACK_CYCLES_VIDEO ); CPU_IACK = true; while ( ( PendingInterruptCount <= 0 ) && ( PendingInterruptFunction ) ) CALL_VAR(PendingInterruptFunction); + if ( MFP_UpdateNeeded == true ) + MFP_UpdateIRQ ( 0 ); /* update MFP's state if some internal timers related to MFP expired */ pendingInterrupts &= ~( 1 << ( nr - 24 ) ); /* clear HBL or VBL pending bit */ CPU_IACK = false; } @@ -875,10 +895,10 @@ void Exception(int nr, uaecptr oldpc, in if (ExceptionSource == M68000_EXC_SRC_CPU) { - if (bVdiAesIntercept && nr == 0x22) + if (nr == 0x22) { /* Intercept VDI & AES exceptions (Trap #2) */ - if(VDI_AES_Entry()) + if(bVdiAesIntercept && VDI_AES_Entry()) { /* Set 'PC' to address of 'VDI_OPCODE' illegal instruction. * This will call OpCode_VDI() after completion of Trap call! @@ -888,20 +908,15 @@ void Exception(int nr, uaecptr oldpc, in currpc = CART_VDI_OPCODE_ADDR; } } - - if (bBiosIntercept) + else if (nr == 0x2d) { - /* Intercept BIOS or XBIOS trap (Trap #13 or #14) */ - if (nr == 0x2d) - { - /* Intercept BIOS calls */ - if (Bios()) return; - } - else if (nr == 0x2e) - { - /* Intercept XBIOS calls */ - if (XBios()) return; - } + /* Intercept BIOS (Trap #13) calls */ + if (Bios()) return; + } + else if (nr == 0x2e) + { + /* Intercept XBIOS (Trap #14) calls */ + if (XBios()) return; } } @@ -970,6 +985,7 @@ void Exception(int nr, uaecptr oldpc, in /* 68000 bus/address errors: */ if (currprefs.cpu_level==0 && (nr==2 || nr==3) && ExceptionSource == M68000_EXC_SRC_CPU) { uae_u16 specialstatus = 1; + uae_u16 BusError_opcode; /* Special status word emulation isn't perfect yet... :-( */ if (regs.sr & 0x2000) @@ -981,29 +997,55 @@ void Exception(int nr, uaecptr oldpc, in put_long (m68k_areg(regs, 7)+2, last_fault_for_exception_3); put_word (m68k_areg(regs, 7)+6, last_op_for_exception_3); put_long (m68k_areg(regs, 7)+10, last_addr_for_exception_3); - if (bExceptionDebugging) { + if (ExceptionDebugMask & EXCEPT_ADDRESS) { fprintf(stderr,"Address Error at address $%x, PC=$%x\n",last_fault_for_exception_3,currpc); DebugUI(REASON_CPU_EXCEPTION); } } else { /* Bus error */ - specialstatus |= ( get_word(BusErrorPC) & (~0x1f) ); /* [NP] unused bits of special status are those of the last opcode ! */ + /* Get the opcode that caused the bus error, to adapt the stack frame in some cases */ + /* (we must call get_word() only on valid region, else this will cause a double bus error) */ + if ( valid_address ( BusErrorPC , 2 ) ) + BusError_opcode = get_word(BusErrorPC); + else + BusError_opcode = 0; + + specialstatus |= ( BusError_opcode & (~0x1f) ); /* [NP] unused bits of special status are those of the last opcode ! */ if (bBusErrorReadWrite) specialstatus |= 0x10; put_word (m68k_areg(regs, 7), specialstatus); put_long (m68k_areg(regs, 7)+2, BusErrorAddress); - put_word (m68k_areg(regs, 7)+6, get_word(BusErrorPC)); /* Opcode */ + put_word (m68k_areg(regs, 7)+6, BusError_opcode); /* Opcode */ /* [NP] PC stored in the stack frame is not necessarily pointing to the next instruction ! */ /* FIXME : we should have a proper model for this, in the meantime we handle specific cases */ - if ( get_word(BusErrorPC) == 0x21f8 ) /* move.l $0.w,$24.w (Transbeauce 2 loader) */ - put_long (m68k_areg(regs, 7)+10, currpc-2); /* correct PC is 2 bytes less than usual value */ + if ( BusError_opcode == 0x21f8 ) /* move.l $0.w,$24.w (Transbeauce 2 loader) */ + put_long (m68k_areg(regs, 7)+10, currpc-2); /* correct PC is 2 bytes less than usual value */ + + else if ( ( BusErrorPC == 0xccc ) && ( BusError_opcode == 0x48d6 ) ) /* 48d6 3f00 movem.l a0-a5,(a6) (Blood Money) */ + put_long (m68k_areg(regs, 7)+10, currpc+2); /* correct PC is 2 bytes more than usual value */ + + else if ( ( BusErrorPC == 0x1fece ) && ( BusError_opcode == 0x33d4 ) ) /* 1fece : 33d4 0001 fdca move.w (a4),$1fdca (Batman The Movie) */ + put_long (m68k_areg(regs, 7)+10, currpc-4); /* correct PC is 4 bytes less than usual value */ + + /* [NP] In case of a move with a bus error on the read part, uae cpu is writing to the dest part */ + /* then process the bus error ; on a real CPU, the bus error occurs after the read and before the */ + /* write, so the dest part doesn't change. For now, we restore the dest part on some specific cases */ + /* FIXME : the bus error should be processed just after the read, not at the end of the instruction */ + else if ( ( BusErrorPC == 0x62a ) && ( BusError_opcode == 0x3079 ) ) /* 3079 4ef9 0000 move.l $4ef90000,a0 (Dragon Flight) */ + m68k_areg(regs, 0) = 8; /* A0 should not be changed to "0" but keep its value "8" */ + + else if ( get_long(BusErrorPC) == 0x13f88e21 ) /* 13f8 8e21 move.b $ffff8e21.w,$xxxxx (Tymewarp) */ + put_byte ( get_long(BusErrorPC+4) , 0x00 ); /* dest content should not be changed to "ff" but keep its value "00" */ + + fprintf(stderr,"Bus Error at address $%x, PC=$%lx %x %x\n", BusErrorAddress, (long)currpc, BusErrorPC , BusError_opcode); + /* Check for double bus errors: */ if (regs.spcflags & SPCFLAG_BUSERROR) { fprintf(stderr, "Detected double bus error at address $%x, PC=$%lx => CPU halted!\n", BusErrorAddress, (long)currpc); unset_special(SPCFLAG_BUSERROR); - if (bExceptionDebugging) + if (ExceptionDebugMask & EXCEPT_BUS) DebugUI(REASON_CPU_EXCEPTION); else DlgAlert_Notice("Detected double bus error => CPU halted!\nEmulation needs to be reset.\n"); @@ -1011,7 +1053,7 @@ void Exception(int nr, uaecptr oldpc, in m68k_setstopped(true); return; } - if (bExceptionDebugging && BusErrorAddress!=0xff8a00) { + if ((ExceptionDebugMask & EXCEPT_BUS) && BusErrorAddress!=0xff8a00) { fprintf(stderr,"Bus Error at address $%x, PC=$%lx\n", BusErrorAddress, (long)currpc); DebugUI(REASON_CPU_EXCEPTION); } @@ -1019,8 +1061,8 @@ void Exception(int nr, uaecptr oldpc, in } /* Set PC and flags */ - if (bExceptionDebugging && get_long (regs.vbr + 4*nr) == 0) { - write_log("Uninitialized exception handler #%i!\n", nr); + if ((ExceptionDebugMask & EXCEPT_NOHANDLER) && (regs.vbr + 4*nr) == 0) { + fprintf(stderr,"Uninitialized exception handler #%i!\n", nr); DebugUI(REASON_CPU_EXCEPTION); } newpc = get_long (regs.vbr + 4*nr); @@ -1028,8 +1070,11 @@ void Exception(int nr, uaecptr oldpc, in { if ( nr==2 || nr==3 ) /* address error during bus/address error -> stop emulation */ { - fprintf(stderr,"Address Error during exception 2/3, aborting new PC=$%x\n",newpc); - DebugUI(REASON_CPU_EXCEPTION); + fprintf(stderr,"Address Error during exception 2/3, aborting new PC=$%x\n",newpc); + if (ExceptionDebugMask & (EXCEPT_BUS|EXCEPT_ADDRESS)) + DebugUI(REASON_CPU_EXCEPTION); + else + DlgAlert_Notice("Address Error during exception 2/3 => CPU halted!\nEmulation needs to be reset.\n"); } else { @@ -1038,6 +1083,9 @@ void Exception(int nr, uaecptr oldpc, in } return; } + /* handle debugger invocation for rest of exceptions */ + if (ExceptionDebugMask && nr > 3 && nr < 9) + DebugUI_Exceptions(nr, currpc); m68k_setpc (get_long (regs.vbr + 4*nr)); fill_prefetch_0 (); @@ -1161,7 +1209,7 @@ int m68k_move2c (int regno, uae_u32 *reg case 0x800: regs.usp = *regp; break; case 0x801: regs.vbr = *regp; break; - case 0x802: caar = *regp & 0xfc; break; + case 0x802: caar = *regp; break; case 0x803: regs.msp = *regp; if (regs.m == 1) m68k_areg(regs, 7) = regs.msp; break; case 0x804: regs.isp = *regp; if (regs.m == 0) m68k_areg(regs, 7) = regs.isp; break; case 0x805: mmusr = *regp; break; @@ -1682,19 +1730,20 @@ static void m68k_run_1 (void) { #ifdef DEBUG_PREFETCH uae_u8 saved_bytes[20]; - uae_u16 *oldpcp; + uae_u8 *oldpcp; #endif for (;;) { int cycles; +//fprintf (stderr, "ir in %x %x\n",do_get_mem_long(®s.prefetch) , regs.prefetch_pc); uae_u32 opcode = get_iword_prefetch (0); #ifdef DEBUG_PREFETCH - if (get_ilong (0) != do_get_mem_long (®s.prefetch)) { - fprintf (stderr, "Prefetch differs from memory.\n"); - debugging = 1; - return; - } +// if (get_ilong (0) != do_get_mem_long (®s.prefetch)) { +// fprintf (stderr, "Prefetch differs from memory.\n"); +// debugging = 1; +// return; +// } oldpcp = regs.pc_p; memcpy (saved_bytes, regs.pc_p, 20); #endif @@ -1726,13 +1775,18 @@ static void m68k_run_1 (void) if (bDspEnabled) Cycles_SetCounter(CYCLES_COUNTER_CPU, 0); /* to measure the total number of cycles spent in the cpu */ + /* Uncomment following lines to call capslib's debugger after each instruction */ + //if ( CAPSGetDebugRequest() ) + // DebugUI(REASON_CPU_BREAKPOINT); + cycles = (*cpufunctbl[opcode])(opcode); +//fprintf (stderr, "ir out %x %x\n",do_get_mem_long(®s.prefetch) , regs.prefetch_pc); #ifdef DEBUG_PREFETCH if (memcmp (saved_bytes, oldpcp, 20) != 0) { fprintf (stderr, "Self-modifying code detected %x.\n" , m68k_getpc() ); - set_special (SPCFLAG_BRK); - debugging = 1; +// set_special (SPCFLAG_BRK); +// debugging = 1; } #endif