-arm7: Added rudimentary instruction prefetch buffer. Fixes GBA NES Classics games. [Ryan Holtz]

This commit is contained in:
mooglyguy 2017-10-23 22:28:17 +02:00
parent 9ae5744820
commit 98b7bb5848
4 changed files with 209 additions and 35 deletions

View File

@ -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 );
uint8_t fetched_op[4];
uint32_t op = 0;
int prefetch_index = get_insn_prefetch_index(pc);
if (prefetch_index < 0)
{
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, oprom, opram, options);
{
return CPU_DISASSEMBLE_NAME(arm7thumb_be)(this, stream, pc, fetched_op, opram, options);
}
else
return CPU_DISASSEMBLE_NAME(arm7thumb)(this, stream, pc, oprom, opram, options);
{
return CPU_DISASSEMBLE_NAME(arm7thumb)(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);
else
return CPU_DISASSEMBLE_NAME(arm7arm)(this, stream, pc, oprom, opram, options);
{
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
{
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
{
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;
}

View File

@ -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;

View File

@ -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;

View File

@ -970,24 +970,48 @@ WRITE32_MEMBER(gba_state::gba_io_w)
}
break;
case 0x00a0/4:
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:
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: