initial commit, pipeline emulated and working

This commit is contained in:
Thomas Jentzsch 2021-07-18 09:12:13 +02:00
parent 7ade95c03b
commit d8e03e4e0e
2 changed files with 190 additions and 79 deletions

View File

@ -18,7 +18,7 @@
//============================================================================ //============================================================================
// This class provides Thumb emulation code ("Thumbulator") // This class provides Thumb emulation code ("Thumbulator")
// by David Welch (dwelch@dwelch.com) // by David Welch (dwelch@dwelch.com)
// Modified by Fred Quimby // Modified by Fred Quimby & Thomas Jentzsch
// Code is public domain and used with the author's consent // Code is public domain and used with the author's consent
//============================================================================ //============================================================================
@ -121,6 +121,9 @@ using Common::Base;
#define INC_ARM_CYCLES(m) #define INC_ARM_CYCLES(m)
#endif #endif
constexpr float PC_REG = 15;
#define PIPED
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size, Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size,
const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack, const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack,
@ -155,9 +158,16 @@ string Thumbulator::doRun(uInt32& cycles, bool irqDrivenAudio)
{ {
_irqDrivenAudio = irqDrivenAudio; _irqDrivenAudio = irqDrivenAudio;
reset(); reset();
#ifdef PIPED
fetch(); // pipeline[n-2] // 0
next(); // 0 -> 1
fetch(); // pipeline[n-1] // 1
decode(); // pipeline[n-2] // 1
#endif
for(;;) for(;;)
{ {
if(execute()) break; //if(execute()) break;
if(step()) break;
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
if(_stats.instructions > 500000) // way more than would otherwise be possible if(_stats.instructions > 500000) // way more than would otherwise be possible
throw runtime_error("instructions > 500000"); throw runtime_error("instructions > 500000");
@ -276,19 +286,43 @@ void Thumbulator::dump_regs()
statusMsg << endl statusMsg << endl
<< "SP = " << Base::HEX8 << reg_norm[13] << " " << "SP = " << Base::HEX8 << reg_norm[13] << " "
<< "LR = " << Base::HEX8 << reg_norm[14] << " " << "LR = " << Base::HEX8 << reg_norm[14] << " "
<< "PC = " << Base::HEX8 << reg_norm[15] << " " << "PC = " << Base::HEX8 << reg_norm[PC_REG] << " "
<< endl; << endl;
} }
#endif #endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Thumbulator::fetch16(uInt32 addr) void Thumbulator::next()
{ {
_pipeIdx = (++_pipeIdx) % 3;
uInt32 pc = read_register(PC_REG);
write_register(PC_REG, pc + 2, false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Thumbulator::step()
{
next(); // n++
int result = execute(); // pipeline[n-2];
decode(); // pipeline[n-1]
fetch(); // pipeline[n]
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::fetch()
{
#ifdef PIPED
uInt32 pc = read_register(PC_REG);
#else
uInt32 pc = read_register(PC_REG) - 4;
#endif
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
uInt32 data; uInt32 inst;
#ifdef THUMB_CYCLE_COUNT #ifdef THUMB_CYCLE_COUNT
_pipeIdx = (++_pipeIdx) % 3;
#ifdef MERGE_I_S #ifdef MERGE_I_S
if(_lastCycleType[2] == CycleType::I) if(_lastCycleType[2] == CycleType::I)
@ -307,41 +341,71 @@ uInt32 Thumbulator::fetch16(uInt32 addr)
// } // }
// else // else
//#endif //#endif
INC_S_CYCLES(addr, AccessType::prefetch); INC_S_CYCLES(pc, AccessType::prefetch);
//INC_S_CYCLES(addr, _prefetchAccessType[_pipeIdx]); //INC_S_CYCLES(addr, _prefetchAccessType[_pipeIdx]);
} }
else else
{ {
INC_N_CYCLES(addr, AccessType::prefetch); // or ::data ? INC_N_CYCLES(pc, AccessType::prefetch); // or ::data ?
//INC_N_CYCLES(addr, _prefetchAccessType[_pipeIdx]); //INC_N_CYCLES(addr, _prefetchAccessType[_pipeIdx]);
} }
_prefetchCycleType[_pipeIdx] = CycleType::S; // default _prefetchCycleType[_pipeIdx] = CycleType::S; // default
//_prefetchAccessType[_pipeIdx] = AccessType::prefetch; // default //_prefetchAccessType[_pipeIdx] = AccessType::prefetch; // default
#endif #endif
switch(addr & 0xF0000000) switch(pc & 0xF0000000)
{ {
case 0x00000000: //ROM case 0x00000000: //ROM
addr &= ROMADDMASK; pc &= ROMADDMASK;
if(addr < 0x50) if(pc < 0x50)
fatalError("fetch16", addr, "abort"); fatalError("fetch", pc, "abort");
addr >>= 1; pc >>= 1;
data = CONV_RAMROM(rom[addr]); inst = CONV_RAMROM(rom[pc]);
DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); DO_DBUG(statusMsg << "fetch(" << Base::HEX8 << pc << ")=" << Base::HEX4 << inst << endl);
return data; break;
case 0x40000000: //RAM case 0x40000000: //RAM
addr &= RAMADDMASK; pc &= RAMADDMASK;
addr >>= 1; pc >>= 1;
data = CONV_RAMROM(ram[addr]); inst = CONV_RAMROM(ram[pc]);
DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); DO_DBUG(statusMsg << "fetch(" << Base::HEX8 << pc << ")=" << Base::HEX4 << inst << endl);
return data; break;
default:
inst = 0;
fatalError("fetch", pc, "abort");
} }
return fatalError("fetch16", addr, "abort");
#else #else
addr &= ROMADDMASK; pc &= ROMADDMASK;
addr >>= 1; pc >>= 1;
return CONV_RAMROM(rom[addr]); inst = CONV_RAMROM(rom[pc]);
#endif
_pipe[_pipeIdx].inst = inst;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Thumbulator::decode()
{
#ifdef PIPED
uInt32 pipeIdx = (_pipeIdx + 2) % 3;
uInt32 pc = read_register(PC_REG) - 2; // wie fetch!
#else
uInt32 pipeIdx = _pipeIdx;
uInt32 pc = read_register(PC_REG) - 4; // wie fetch!
#endif
Op decodedOp;
#ifndef UNSAFE_OPTIMIZATIONS
if((pc & 0xF0000000) == 0 && pc < romSize)
decodedOp = decodedRom[pc >> 1];
else
decodedOp = decodeInstructionWord(_pipe[pipeIdx].inst);
#else
decodedOp = decodedRom[(pc & ROMADDMASK) >> 1];
#endif
_pipe[pipeIdx].op = decodedOp;
#ifdef COUNT_OPS
++opCount[int(decodedOp)];
#endif #endif
} }
@ -722,7 +786,7 @@ uInt32 Thumbulator::read_register(uInt32 reg)
uInt32 data = reg_norm[reg]; uInt32 data = reg_norm[reg];
DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl); DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl);
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
if(reg == 15) if(reg == PC_REG)
{ {
if(data & 1) if(data & 1)
{ {
@ -741,7 +805,7 @@ void Thumbulator::write_register(uInt32 reg, uInt32 data, bool isFlowBreak)
DO_DBUG(statusMsg << "write_register(" << dec << reg << "," << Base::HEX8 << data << ")" << endl); DO_DBUG(statusMsg << "write_register(" << dec << reg << "," << Base::HEX8 << data << ")" << endl);
//#ifndef UNSAFE_OPTIMIZATIONS // this fails when combined with read_register UNSAFE_OPTIMIZATIONS //#ifndef UNSAFE_OPTIMIZATIONS // this fails when combined with read_register UNSAFE_OPTIMIZATIONS
if(reg == 15) if(reg == PC_REG)
{ {
data &= ~1; data &= ~1;
if(isFlowBreak) if(isFlowBreak)
@ -750,10 +814,20 @@ void Thumbulator::write_register(uInt32 reg, uInt32 data, bool isFlowBreak)
++_stats.taken; ++_stats.taken;
#endif #endif
// dummy fetch + fill the pipeline // dummy fetch + fill the pipeline
//INC_N_CYCLES(reg_norm[15] - 2, AccessType::prefetch); //INC_N_CYCLES(reg_norm[PC_REG] - 2, AccessType::prefetch);
//INC_S_CYCLES(data - 2, AccessType::branch); //INC_S_CYCLES(data - 2, AccessType::branch);
INC_N_CYCLES(reg_norm[15] + 4, AccessType::prefetch); INC_N_CYCLES(reg_norm[PC_REG] + 4, AccessType::prefetch);
INC_S_CYCLES(data, AccessType::branch); INC_S_CYCLES(data, AccessType::branch);
fetch(); // dummy
reg_norm[PC_REG] = data;
fetch(); // pipeline[n-2]
#ifdef PIPED
next();
#endif
fetch(); // pipeline[n-1]
decode(); // pipeline[n-2]
return;
} }
} }
//#endif //#endif
@ -1040,34 +1114,39 @@ Thumbulator::Op Thumbulator::decodeInstructionWord(uint16_t inst) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Thumbulator::execute() int Thumbulator::execute()
{ {
uInt32 pc, sp, inst, ra, rb, rc, rm, rd, rn, rs, op; #ifdef PIPED
uInt32 pipeIdx = (_pipeIdx + 1) % 3;
#else
uInt32 pipeIdx = _pipeIdx;
#endif
pc = read_register(15); uInt32 sp, ra, rb, rc, rm, rd, rn, rs, op;
uInt32 pc = read_register(PC_REG);
uInt32 inst = _pipe[pipeIdx].inst;
Op decodedOp = _pipe[pipeIdx].op;
uInt32 instructionPtr = pc - 2; //uInt32 instructionPtr = pc - 2;
inst = fetch16(instructionPtr); ////inst = fetch(instructionPtr);
//inst = fetch();
pc += 2; //pc += 2;
write_register(15, pc, false); //write_register(PC_REG, pc, false);
DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " "); DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " ");
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
++_stats.instructions; ++_stats.instructions;
#endif #endif
Op decodedOp; // Op decodedOp;
#ifndef UNSAFE_OPTIMIZATIONS //#ifndef UNSAFE_OPTIMIZATIONS
if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize) // if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize)
decodedOp = decodedRom[instructionPtr >> 1]; // decodedOp = decodedRom[instructionPtr >> 1];
else // else
decodedOp = decodeInstructionWord(inst); // decodedOp = decodeInstructionWord(inst);
#else //#else
decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1]; // decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1];
#endif //#endif
#ifdef COUNT_OPS
++opCount[int(decodedOp)];
#endif
switch (decodedOp) { switch (decodedOp) {
//ADC //ADC
case Op::adc: { case Op::adc: {
@ -1158,14 +1237,16 @@ int Thumbulator::execute()
ra = read_register(rd); ra = read_register(rd);
rb = read_register(rm); rb = read_register(rm);
rc = ra + rb; rc = ra + rb;
if(rd == 15) if(rd == PC_REG)
{ {
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
if((rc & 1) == 0) if((rc & 1) == 0)
fatalError("add pc", pc, rc, " produced an arm address"); fatalError("add pc", pc, rc, " produced an arm address");
#endif #endif
//rc &= ~1; //write_register may do this as well //rc &= ~1; //write_register may do this as well
#ifndef PIPED
rc += 2; //The program counter is special rc += 2; //The program counter is special
#endif
} }
//fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb);
write_register(rd, rc); write_register(rd, rc);
@ -1178,7 +1259,7 @@ int Thumbulator::execute()
rd = (inst >> 8) & 0x7; rd = (inst >> 8) & 0x7;
rb <<= 2; rb <<= 2;
DO_DISS(statusMsg << "add r" << dec << rd << ",PC,#0x" << Base::HEX2 << rb << endl); DO_DISS(statusMsg << "add r" << dec << rd << ",PC,#0x" << Base::HEX2 << rb << endl);
ra = read_register(15); ra = read_register(PC_REG);
rc = (ra & (~3U)) + rb; rc = (ra & (~3U)) + rb;
write_register(rd, rc); write_register(rd, rc);
return 0; return 0;
@ -1307,83 +1388,84 @@ int Thumbulator::execute()
rb |= (~0U) << 8; rb |= (~0U) << 8;
rb <<= 1; rb <<= 1;
rb += pc; rb += pc;
#ifndef PIPED
rb += 2; rb += 2;
#endif
op = (inst >> 8) & 0xF; op = (inst >> 8) & 0xF;
switch(op) switch(op)
{ {
case 0x0: //b eq z set case 0x0: //b eq z set
DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl);
if(cpsr & CPSR_Z) if(cpsr & CPSR_Z)
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x1: //b ne z clear case 0x1: //b ne z clear
DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl);
if(!(cpsr & CPSR_Z)) if(!(cpsr & CPSR_Z))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x2: //b cs c set case 0x2: //b cs c set
DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl);
if(cpsr & CPSR_C) if(cpsr & CPSR_C)
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x3: //b cc c clear case 0x3: //b cc c clear
DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl);
if(!(cpsr & CPSR_C)) if(!(cpsr & CPSR_C))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x4: //b mi n set case 0x4: //b mi n set
DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl);
if(cpsr & CPSR_N) if(cpsr & CPSR_N)
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x5: //b pl n clear case 0x5: //b pl n clear
DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl);
if(!(cpsr & CPSR_N)) if(!(cpsr & CPSR_N))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x6: //b vs v set case 0x6: //b vs v set
DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl);
if(cpsr & CPSR_V) if(cpsr & CPSR_V)
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x7: //b vc v clear case 0x7: //b vc v clear
DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl);
if(!(cpsr & CPSR_V)) if(!(cpsr & CPSR_V))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x8: //b hi c set z clear case 0x8: //b hi c set z clear
DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl);
if((cpsr & CPSR_C) && (!(cpsr & CPSR_Z))) if((cpsr & CPSR_C) && (!(cpsr & CPSR_Z)))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0x9: //b ls c clear or z set case 0x9: //b ls c clear or z set
DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl);
if((cpsr & CPSR_Z) || (!(cpsr & CPSR_C))) if((cpsr & CPSR_Z) || (!(cpsr & CPSR_C)))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0xA: //b ge N == V case 0xA: //b ge N == V
DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl);
if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) ||
((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V))))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0xB: //b lt N != V case 0xB: //b lt N != V
DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl);
if((!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || if((!(cpsr & CPSR_N) && (cpsr & CPSR_V)) ||
(((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) (((cpsr & CPSR_N)) && !(cpsr & CPSR_V)))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0xC: //b gt Z==0 and N == V case 0xC: //b gt Z==0 and N == V
@ -1392,7 +1474,7 @@ int Thumbulator::execute()
{ {
if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) ||
((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V))))
write_register(15, rb); write_register(PC_REG, rb);
} }
return 0; return 0;
@ -1401,7 +1483,7 @@ int Thumbulator::execute()
if((cpsr & CPSR_Z) || if((cpsr & CPSR_Z) ||
(!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || (!(cpsr & CPSR_N) && (cpsr & CPSR_V)) ||
(((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) (((cpsr & CPSR_N)) && !(cpsr & CPSR_V)))
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
case 0xE: case 0xE:
@ -1428,9 +1510,11 @@ int Thumbulator::execute()
rb |= (~0U) << 11; rb |= (~0U) << 11;
rb <<= 1; rb <<= 1;
rb += pc; rb += pc;
#ifndef PIPED
rb += 2; rb += 2;
#endif
DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl);
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
} }
@ -1474,10 +1558,12 @@ int Thumbulator::execute()
//branch to thumb //branch to thumb
rb = read_register(14); rb = read_register(14);
rb += (inst & ((1 << 11) - 1)) << 1; rb += (inst & ((1 << 11) - 1)) << 1;
#ifndef PIPED
rb += 2; rb += 2;
#endif
DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl);
write_register(14, (pc-2) | 1); write_register(14, (pc-2) | 1);
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
} }
else if((inst & 0x1800) == 0x0800) //H=b01 else if((inst & 0x1800) == 0x0800) //H=b01
@ -1488,10 +1574,12 @@ int Thumbulator::execute()
rb = read_register(14); rb = read_register(14);
rb += (inst & ((1 << 11) - 1)) << 1; rb += (inst & ((1 << 11) - 1)) << 1;
rb &= 0xFFFFFFFC; rb &= 0xFFFFFFFC;
#ifndef PIPED
rb += 2; rb += 2;
#endif
DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl);
write_register(14, (pc-2) | 1); write_register(14, (pc-2) | 1);
write_register(15, rb); write_register(PC_REG, rb);
return 0; return 0;
} }
break; break;
@ -1503,12 +1591,14 @@ int Thumbulator::execute()
DO_DISS(statusMsg << "blx r" << dec << rm << endl); DO_DISS(statusMsg << "blx r" << dec << rm << endl);
rc = read_register(rm); rc = read_register(rm);
//fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc); //fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc);
#ifndef PIPED
rc += 2; rc += 2;
#endif
if(rc & 1) if(rc & 1)
{ {
write_register(14, (pc-2) | 1); write_register(14, (pc-2) | 1);
//rc &= ~1; //rc &= ~1;
write_register(15, rc); write_register(PC_REG, rc);
return 0; return 0;
} }
else else
@ -1524,13 +1614,15 @@ int Thumbulator::execute()
rm = (inst >> 3) & 0xF; rm = (inst >> 3) & 0xF;
DO_DISS(statusMsg << "bx r" << dec << rm << endl); DO_DISS(statusMsg << "bx r" << dec << rm << endl);
rc = read_register(rm); rc = read_register(rm);
#ifndef PIPED
rc += 2; rc += 2;
#endif
//fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc); //fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc);
if(rc & 1) if(rc & 1)
{ {
// branch to odd address denotes 16 bit ARM code // branch to odd address denotes 16 bit ARM code
//rc &= ~1; //rc &= ~1;
write_register(15, rc); write_register(PC_REG, rc);
return 0; return 0;
} }
else else
@ -1752,9 +1844,11 @@ int Thumbulator::execute()
if (handled) if (handled)
{ {
rc = read_register(14); // lr rc = read_register(14); // lr
#ifndef PIPED
rc += 2; rc += 2;
#endif
//rc &= ~1; //rc &= ~1;
write_register(15, rc); write_register(PC_REG, rc);
//_totalCycles += 100; // just a wild guess //_totalCycles += 100; // just a wild guess
return 0; return 0;
} }
@ -1900,7 +1994,7 @@ int Thumbulator::execute()
sp += 4; sp += 4;
} }
} }
INC_I_CYCLES; // Note: destination PC not possible, see pop instead INC_I_CYCLES; // Note: destination PC_REG not possible, see pop instead
//there is a write back exception. //there is a write back exception.
if((inst & (1 << rn)) == 0) if((inst & (1 << rn)) == 0)
write_register(rn, sp); write_register(rn, sp);
@ -1940,7 +2034,7 @@ int Thumbulator::execute()
rd = (inst >> 8) & 0x07; rd = (inst >> 8) & 0x07;
rb <<= 2; rb <<= 2;
DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] "); DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] ");
ra = read_register(15); ra = read_register(PC_REG);
ra &= ~3; ra &= ~3;
rb += ra; rb += ra;
DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl); DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl);
@ -2226,16 +2320,18 @@ int Thumbulator::execute()
rm = (inst >> 3) & 0xF; rm = (inst >> 3) & 0xF;
DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl); DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl);
rc = read_register(rm); rc = read_register(rm);
if((rd == 14) && (rm == 15)) if((rd == 14) && (rm == PC_REG))
{ {
//printf("mov lr,pc warning 0x%08X\n",pc-2); //printf("mov lr,pc warning 0x%08X\n",pc-2);
//rc|=1; //rc|=1;
} }
if(rd == 15) #ifndef PIPED
if(rd == PC_REG)
{ {
//rc &= ~1; //write_register may do this as well //rc &= ~1; //write_register may do this as well
rc += 2; //The program counter is special rc += 2; //The program counter is special
} }
#endif
write_register(rd, rc); write_register(rd, rc);
return 0; return 0;
} }
@ -2364,8 +2460,10 @@ int Thumbulator::execute()
{ {
INC_S_CYCLES(sp, AccessType::data); INC_S_CYCLES(sp, AccessType::data);
} }
#ifndef PIPED
rc += 2; rc += 2;
write_register(15, rc); #endif
write_register(PC_REG, rc);
sp += 4; sp += 4;
} }
write_register(13, sp); write_register(13, sp);
@ -2879,9 +2977,13 @@ int Thumbulator::reset()
{ {
reg_norm.fill(0); reg_norm.fill(0);
reg_norm[13] = cStack; // SP reg_norm[13] = cStack; // SP
reg_norm[14] = cBase; // LR reg_norm[14] = cBase; // LR
reg_norm[15] = (cStart + 2) | 1; // PC (+2 for pipeline, lower bit for THUMB) #ifdef PIPED
reg_norm[PC_REG] = cStart | 1; // PC_REG (lower bit for THUMB)
#else
reg_norm[PC_REG] = (cStart + 2) | 1; // PC_REG (lower bit for THUMB)
#endif
cpsr = 0; cpsr = 0;
handler_mode = false; handler_mode = false;

View File

@ -210,6 +210,10 @@ class Thumbulator
prefetch, branch, data prefetch, branch, data
}; };
#endif #endif
struct Pipeline {
uInt32 inst;
Op op;
};
const std::array<ChipPropsType, uInt32(ChipType::numTypes)> ChipProps = const std::array<ChipPropsType, uInt32(ChipType::numTypes)> ChipProps =
{{ {{
{ 70.0, 4, 1 }, // LPC2101_02_03 { 70.0, 4, 1 }, // LPC2101_02_03
@ -222,7 +226,6 @@ class Thumbulator
string doRun(uInt32& cycles, bool irqDrivenAudio); string doRun(uInt32& cycles, bool irqDrivenAudio);
uInt32 read_register(uInt32 reg); uInt32 read_register(uInt32 reg);
void write_register(uInt32 reg, uInt32 data, bool isFlowBreak = true); void write_register(uInt32 reg, uInt32 data, bool isFlowBreak = true);
uInt32 fetch16(uInt32 addr);
uInt32 read16(uInt32 addr); uInt32 read16(uInt32 addr);
uInt32 read32(uInt32 addr); uInt32 read32(uInt32 addr);
#ifndef UNSAFE_OPTIMIZATIONS #ifndef UNSAFE_OPTIMIZATIONS
@ -251,7 +254,12 @@ class Thumbulator
void dump_counters(); void dump_counters();
void dump_regs(); void dump_regs();
#endif #endif
void next();
int step();
void fetch();
void decode();
int execute(); int execute();
int reset(); int reset();
#ifdef THUMB_CYCLE_COUNT #ifdef THUMB_CYCLE_COUNT
@ -309,6 +317,7 @@ class Thumbulator
#ifdef THUMB_CYCLE_COUNT #ifdef THUMB_CYCLE_COUNT
double _armCyclesFactor{1.05}; double _armCyclesFactor{1.05};
uInt32 _pipeIdx{0}; uInt32 _pipeIdx{0};
Pipeline _pipe[3];
CycleType _prefetchCycleType[3]{CycleType::S}; CycleType _prefetchCycleType[3]{CycleType::S};
CycleType _lastCycleType[3]{CycleType::S}; CycleType _lastCycleType[3]{CycleType::S};
AccessType _prefetchAccessType[3]{AccessType::data}; AccessType _prefetchAccessType[3]{AccessType::data};