summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author mooglyguy <therealmogminer@gmail.com>2017-10-23 22:28:17 +0200
committer mooglyguy <therealmogminer@gmail.com>2017-10-23 22:28:57 +0200
commit98b7bb584806b7f4b292d8ad320f9f1c9205ec82 (patch)
tree43c8f9e8126b57b336b06aa08cb92e019fdbd2b3
parent9ae574482039eb503bc57d7af89ed7c78936eafe (diff)
-arm7: Added rudimentary instruction prefetch buffer. Fixes GBA NES Classics games. [Ryan Holtz]
-rw-r--r--src/devices/cpu/arm7/arm7.cpp157
-rw-r--r--src/devices/cpu/arm7/arm7.h12
-rw-r--r--src/devices/cpu/arm7/arm7ops.cpp19
-rw-r--r--src/mame/drivers/gba.cpp56
4 files changed, 209 insertions, 35 deletions
diff --git a/src/devices/cpu/arm7/arm7.cpp b/src/devices/cpu/arm7/arm7.cpp
index 551f940f692..0fae144e945 100644
--- a/src/devices/cpu/arm7/arm7.cpp
+++ b/src/devices/cpu/arm7/arm7.cpp
@@ -73,6 +73,14 @@ arm7_cpu_device::arm7_cpu_device(const machine_config &mconfig, device_type type
arch = ARM9_COPRO_ID_ARCH_V4T;
m_copro_id = ARM9_COPRO_ID_MFR_ARM | arch | ARM9_COPRO_ID_PART_GENERICARM7;
+
+ // TODO[RH]: Default to 3-instruction prefetch for unknown ARM variants. Derived cores should set the appropriate value in their constructors.
+ m_insn_prefetch_depth = 3;
+
+ memset(m_insn_prefetch_buffer, 0, sizeof(uint32_t) * 3);
+ memset(m_insn_prefetch_address, 0, sizeof(uint32_t) * 3);
+ m_insn_prefetch_count = 0;
+ m_insn_prefetch_index = 0;
}
@@ -641,6 +649,73 @@ void arm7_cpu_device::device_reset()
m_r[eR15] += 4; \
m_icount +=2; /* Any unexecuted instruction only takes 1 cycle (page 193) */
+void arm7_cpu_device::update_insn_prefetch(uint32_t curr_pc)
+{
+ curr_pc &= ~3;
+ if (m_insn_prefetch_address[m_insn_prefetch_index] != curr_pc)
+ {
+ m_insn_prefetch_count = 0;
+ m_insn_prefetch_index = 0;
+ }
+
+ if (m_insn_prefetch_count == m_insn_prefetch_depth)
+ return;
+
+ const uint32_t to_fetch = m_insn_prefetch_depth - m_insn_prefetch_count;
+ const uint32_t start_index = (m_insn_prefetch_depth + (m_insn_prefetch_index - to_fetch)) % m_insn_prefetch_depth;
+ //printf("need to prefetch %d instructions starting at index %d\n", to_fetch, start_index);
+
+ uint32_t pc = curr_pc + m_insn_prefetch_count * 4;
+ for (uint32_t i = 0; i < to_fetch; i++)
+ {
+ uint32_t index = (i + start_index) % m_insn_prefetch_depth;
+ if ((m_control & COPRO_CTRL_MMU_EN) && !arm7_tlb_translate(pc, ARM7_TLB_ABORT_P | ARM7_TLB_READ))
+ {
+ break;
+ }
+ uint32_t op = m_direct->read_dword(pc);
+ //printf("ipb[%d] <- %08x(%08x)\n", index, op, pc);
+ m_insn_prefetch_buffer[index] = op;
+ m_insn_prefetch_address[index] = pc;
+ m_insn_prefetch_count++;
+ pc += 4;
+ }
+}
+
+uint16_t arm7_cpu_device::insn_fetch_thumb(uint32_t pc)
+{
+ if (pc & 2)
+ {
+ uint16_t insn = (uint16_t)(m_insn_prefetch_buffer[m_insn_prefetch_index] >> 16);
+ m_insn_prefetch_index = (m_insn_prefetch_index + 1) % m_insn_prefetch_count;
+ m_insn_prefetch_count--;
+ return insn;
+ }
+ return (uint16_t)m_insn_prefetch_buffer[m_insn_prefetch_index];
+}
+
+uint32_t arm7_cpu_device::insn_fetch_arm(uint32_t pc)
+{
+ //printf("ipb[%d] = %08x\n", m_insn_prefetch_index, m_insn_prefetch_buffer[m_insn_prefetch_index]);
+ uint32_t insn = m_insn_prefetch_buffer[m_insn_prefetch_index];
+ m_insn_prefetch_index = (m_insn_prefetch_index + 1) % m_insn_prefetch_count;
+ m_insn_prefetch_count--;
+ return insn;
+}
+
+int arm7_cpu_device::get_insn_prefetch_index(uint32_t address)
+{
+ address &= ~3;
+ for (uint32_t i = 0; i < m_insn_prefetch_depth; i++)
+ {
+ if (m_insn_prefetch_address[i] == address)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
void arm7_cpu_device::execute_run()
{
uint32_t insn;
@@ -649,6 +724,8 @@ void arm7_cpu_device::execute_run()
{
uint32_t pc = GET_PC;
+ update_insn_prefetch(pc);
+
debugger_instruction_hook(this, pc);
/* handle Thumb instructions if active */
@@ -669,7 +746,7 @@ void arm7_cpu_device::execute_run()
}
}
- insn = m_direct->read_word(raddr);
+ insn = insn_fetch_thumb(raddr);
(this->*thumb_handler[(insn & 0xffc0) >> 6])(pc, insn);
}
@@ -700,7 +777,7 @@ void arm7_cpu_device::execute_run()
}
#endif
- insn = m_direct->read_dword(raddr);
+ insn = insn_fetch_arm(raddr);
int op_offset = 0;
/* process condition codes for this instruction */
@@ -823,20 +900,82 @@ offs_t arm7_cpu_device::disasm_disassemble(std::ostream &stream, offs_t pc, cons
extern CPU_DISASSEMBLE( arm7arm_be );
extern CPU_DISASSEMBLE( arm7thumb_be );
- if (T_IS_SET(m_r[eCPSR]))
+ uint8_t fetched_op[4];
+ uint32_t op = 0;
+ int prefetch_index = get_insn_prefetch_index(pc);
+ if (prefetch_index < 0)
{
- if ( m_endian == ENDIANNESS_BIG )
- return CPU_DISASSEMBLE_NAME(arm7thumb_be)(this, stream, pc, oprom, opram, options);
+ memcpy(fetched_op, oprom, 4);
+ if (T_IS_SET(m_r[eCPSR]))
+ {
+ if ( m_endian == ENDIANNESS_BIG )
+ {
+ return CPU_DISASSEMBLE_NAME(arm7thumb_be)(this, stream, pc, fetched_op, opram, options);
+ }
+ else
+ {
+ return CPU_DISASSEMBLE_NAME(arm7thumb)(this, stream, pc, fetched_op, opram, options);
+ }
+ }
else
- return CPU_DISASSEMBLE_NAME(arm7thumb)(this, stream, pc, oprom, opram, options);
+ {
+ if ( m_endian == ENDIANNESS_BIG )
+ {
+ return CPU_DISASSEMBLE_NAME(arm7arm_be)(this, stream, pc, fetched_op, opram, options);
+ }
+ else
+ {
+ return CPU_DISASSEMBLE_NAME(arm7arm)(this, stream, pc, fetched_op, opram, options);
+ }
+ }
}
else
{
- if ( m_endian == ENDIANNESS_BIG )
- return CPU_DISASSEMBLE_NAME(arm7arm_be)(this, stream, pc, oprom, opram, options);
+ op = m_insn_prefetch_buffer[prefetch_index];
+ if (T_IS_SET(m_r[eCPSR]))
+ {
+ if (m_endian == ENDIANNESS_BIG)
+ {
+ if (pc & 1)
+ {
+ fetched_op[1] = op & 0xff;
+ fetched_op[0] = (op >> 8) & 0xff;
+ }
+ else
+ {
+ fetched_op[1] = op & 0xff;
+ fetched_op[0] = (op >> 8) & 0xff;
+ }
+ return CPU_DISASSEMBLE_NAME(arm7thumb_be)(this, stream, pc, fetched_op, opram, options);
+ }
+ else
+ {
+ fetched_op[0] = op & 0xff;
+ fetched_op[1] = (op >> 8) & 0xff;
+ return CPU_DISASSEMBLE_NAME(arm7thumb)(this, stream, pc, fetched_op, opram, options);
+ }
+ }
else
- return CPU_DISASSEMBLE_NAME(arm7arm)(this, stream, pc, oprom, opram, options);
+ {
+ if (m_endian == ENDIANNESS_BIG)
+ {
+ fetched_op[3] = op & 0xff;
+ fetched_op[2] = (op >> 8) & 0xff;
+ fetched_op[1] = (op >> 16) & 0xff;
+ fetched_op[0] = (op >> 24) & 0xff;
+ return CPU_DISASSEMBLE_NAME(arm7arm_be)(this, stream, pc, fetched_op, opram, options);
+ }
+ else
+ {
+ fetched_op[0] = op & 0xff;
+ fetched_op[1] = (op >> 8) & 0xff;
+ fetched_op[2] = (op >> 16) & 0xff;
+ fetched_op[3] = (op >> 24) & 0xff;
+ return CPU_DISASSEMBLE_NAME(arm7arm)(this, stream, pc, fetched_op, opram, options);
+ }
+ }
}
+ return 0;
}
diff --git a/src/devices/cpu/arm7/arm7.h b/src/devices/cpu/arm7/arm7.h
index 3f7f82fed8a..65ebd9ad35f 100644
--- a/src/devices/cpu/arm7/arm7.h
+++ b/src/devices/cpu/arm7/arm7.h
@@ -128,6 +128,18 @@ protected:
address_space_config m_program_config;
uint32_t m_r[/*NUM_REGS*/37];
+
+ void update_insn_prefetch(uint32_t curr_pc);
+ virtual uint16_t insn_fetch_thumb(uint32_t pc);
+ uint32_t insn_fetch_arm(uint32_t pc);
+ int get_insn_prefetch_index(uint32_t address);
+
+ uint32_t m_insn_prefetch_depth;
+ uint32_t m_insn_prefetch_count;
+ uint32_t m_insn_prefetch_index;
+ uint32_t m_insn_prefetch_buffer[3];
+ uint32_t m_insn_prefetch_address[3];
+
bool m_pendingIrq;
bool m_pendingFiq;
bool m_pendingAbtD;
diff --git a/src/devices/cpu/arm7/arm7ops.cpp b/src/devices/cpu/arm7/arm7ops.cpp
index ea10a36be4d..8e52635ecea 100644
--- a/src/devices/cpu/arm7/arm7ops.cpp
+++ b/src/devices/cpu/arm7/arm7ops.cpp
@@ -262,22 +262,21 @@ int arm7_cpu_device::storeInc(uint32_t pat, uint32_t rbv, int mode)
int arm7_cpu_device::storeDec(uint32_t pat, uint32_t rbv, int mode)
{
- int i, result = 0, cnt;
-
// pre-count the # of registers being stored
- for (i = 15; i >= 0; i--)
+ // TODO[RH]: This is just a popcnt. Consider eminline intrinsic.
+ int result = 0;
+ for (int i = 15; i >= 0; i--)
{
if ((pat >> i) & 1)
{
result++;
-
- // starting address
- rbv -= 4;
}
}
- cnt = 0;
- for (i = 0; i <= 15; i++)
+ // adjust starting address
+ rbv -= (result << 2);
+
+ for (int i = 0; i <= 15; i++)
{
if ((pat >> i) & 1)
{
@@ -285,8 +284,8 @@ int arm7_cpu_device::storeDec(uint32_t pat, uint32_t rbv, int mode)
if (i == 15) /* R15 is plus 12 from address of STM */
LOG(("%08x: StoreDec on R15\n", R15));
#endif
- WRITE32(rbv + (cnt * 4), GetModeRegister(mode, i));
- cnt++;
+ WRITE32(rbv, GetModeRegister(mode, i));
+ rbv += 4;
}
}
return result;
diff --git a/src/mame/drivers/gba.cpp b/src/mame/drivers/gba.cpp
index 51088aafaa4..1fbb615a811 100644
--- a/src/mame/drivers/gba.cpp
+++ b/src/mame/drivers/gba.cpp
@@ -970,24 +970,48 @@ WRITE32_MEMBER(gba_state::gba_io_w)
}
break;
case 0x00a0/4:
- m_fifo_a_in %= 17;
- m_fifo_a[m_fifo_a_in++] = (data)&0xff;
- m_fifo_a_in %= 17;
- m_fifo_a[m_fifo_a_in++] = (data>>8)&0xff;
- m_fifo_a_in %= 17;
- m_fifo_a[m_fifo_a_in++] = (data>>16)&0xff;
- m_fifo_a_in %= 17;
- m_fifo_a[m_fifo_a_in++] = (data>>24)&0xff;
+ if (ACCESSING_BITS_0_7)
+ {
+ m_fifo_a_in %= 17;
+ m_fifo_a[m_fifo_a_in++] = (data)&0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ m_fifo_a_in %= 17;
+ m_fifo_a[m_fifo_a_in++] = (data>>8)&0xff;
+ }
+ if (ACCESSING_BITS_16_23)
+ {
+ m_fifo_a_in %= 17;
+ m_fifo_a[m_fifo_a_in++] = (data>>16)&0xff;
+ }
+ if (ACCESSING_BITS_24_31)
+ {
+ m_fifo_a_in %= 17;
+ m_fifo_a[m_fifo_a_in++] = (data>>24)&0xff;
+ }
break;
case 0x00a4/4:
- m_fifo_b_in %= 17;
- m_fifo_b[m_fifo_b_in++] = (data)&0xff;
- m_fifo_b_in %= 17;
- m_fifo_b[m_fifo_b_in++] = (data>>8)&0xff;
- m_fifo_b_in %= 17;
- m_fifo_b[m_fifo_b_in++] = (data>>16)&0xff;
- m_fifo_b_in %= 17;
- m_fifo_b[m_fifo_b_in++] = (data>>24)&0xff;
+ if (ACCESSING_BITS_0_7)
+ {
+ m_fifo_b_in %= 17;
+ m_fifo_b[m_fifo_b_in++] = (data)&0xff;
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ m_fifo_b_in %= 17;
+ m_fifo_b[m_fifo_b_in++] = (data>>8)&0xff;
+ }
+ if (ACCESSING_BITS_16_23)
+ {
+ m_fifo_b_in %= 17;
+ m_fifo_b[m_fifo_b_in++] = (data>>16)&0xff;
+ }
+ if (ACCESSING_BITS_24_31)
+ {
+ m_fifo_b_in %= 17;
+ m_fifo_b[m_fifo_b_in++] = (data>>24)&0xff;
+ }
break;
case 0x00b8/4:
case 0x00c4/4: