summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/i386/i386.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/i386/i386.cpp')
-rw-r--r--src/devices/cpu/i386/i386.cpp41
1 files changed, 41 insertions, 0 deletions
diff --git a/src/devices/cpu/i386/i386.cpp b/src/devices/cpu/i386/i386.cpp
index 7a7529aed40..9f6eaafc83c 100644
--- a/src/devices/cpu/i386/i386.cpp
+++ b/src/devices/cpu/i386/i386.cpp
@@ -55,6 +55,7 @@ i386_device::i386_device(const machine_config &mconfig, device_type type, const
, device_vtlb_interface(mconfig, *this, AS_PROGRAM)
, m_program_config("program", ENDIANNESS_LITTLE, program_data_width, program_addr_width, 0, 32, 12)
, m_io_config("io", ENDIANNESS_LITTLE, io_data_width, 16, 0)
+ , m_dr_breakpoints{nullptr, nullptr, nullptr, nullptr}
, m_smiact(*this)
, m_ferr_handler(*this)
{
@@ -2002,6 +2003,7 @@ void i386_device::i386_common_init()
save_item(NAME(m_CPL));
+ save_item(NAME(m_auto_clear_RF));
save_item(NAME(m_performed_intersegment_jump));
save_item(NAME(m_cr));
@@ -2042,6 +2044,10 @@ void i386_device::i386_common_init()
m_ferr_handler(0);
set_icountptr(m_cycles);
+ m_notifier = m_program->add_change_notifier([this](read_or_write mode)
+ {
+ dri_changed();
+ });
}
void i386_device::device_start()
@@ -2488,6 +2494,8 @@ void i386_device::device_reset()
m_CPL = 0;
+ m_auto_clear_RF = true;
+
CHANGE_PC(m_eip);
}
@@ -2742,6 +2750,36 @@ void i386_device::execute_run()
while( m_cycles > 0 )
{
i386_check_irq_line();
+
+ // The LE and GE bits of DR7 aren't currently implemented because they could potentially require cycle-accurate emulation.
+ if((m_dr[7] & 0xff) != 0) // If all of the breakpoints are disabled, skip checking for instruction breakpoint hitting entirely.
+ for(int i = 0; i < 4; i++)
+ {
+ bool dri_enabled = (m_dr[7] & (1 << ((i << 1) + 1))) || (m_dr[7] & (1 << (i << 1))); // Check both local AND global enable bits for this breakpoint.
+ if(dri_enabled && !m_RF)
+ {
+ int breakpoint_type = (m_dr[7] >> (i << 2)) & 3;
+ int breakpoint_length = (m_dr[7] >> ((i << 2) + 2)) & 3;
+ if(breakpoint_type == 0)
+ {
+ uint32_t phys_addr = 0;
+ uint32_t error;
+ phys_addr = (m_cr[0] & (1 << 31)) ? translate_address(m_CPL, TRANSLATE_FETCH, &m_dr[i], &error) : m_dr[i];
+ if(breakpoint_length != 0) // Not one byte in length? logerror it, I have no idea how this works on real processors.
+ {
+ logerror("i386: Breakpoint length not 1 byte on an instruction breakpoint\n");
+ }
+ if(m_pc == phys_addr)
+ {
+ // The processor never automatically clears bits in DR6. It only sets them.
+ m_dr[6] |= 1 << i;
+ i386_trap(1,0,0);
+ break;
+ }
+ }
+ }
+ }
+
m_operand_size = m_sreg[CS].d;
m_xmm_operand_size = 0;
m_address_size = m_sreg[CS].d;
@@ -2772,6 +2810,7 @@ void i386_device::execute_run()
{
m_prev_eip = m_eip;
m_ext = 1;
+ m_dr[6] |= (1 << 14); //Set BS bit of DR6.
i386_trap(1,0,0);
}
if(m_lock && (m_opcode != 0xf0))
@@ -2782,6 +2821,8 @@ void i386_device::execute_run()
m_ext = 1;
i386_trap_with_error(e&0xffffffff,0,0,e>>32);
}
+ if(m_RF && m_auto_clear_RF) m_RF = 0;
+ if(!m_auto_clear_RF) m_auto_clear_RF = true;
}
m_tsc += (cycles - m_cycles);
}