From d8e03e4e0e8dd9046e750f523ba79d3a5ea9a47d Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch Date: Sun, 18 Jul 2021 09:12:13 +0200 Subject: [PATCH] initial commit, pipeline emulated and working --- src/emucore/Thumbulator.cxx | 258 +++++++++++++++++++++++++----------- src/emucore/Thumbulator.hxx | 11 +- 2 files changed, 190 insertions(+), 79 deletions(-) diff --git a/src/emucore/Thumbulator.cxx b/src/emucore/Thumbulator.cxx index 866cd43e2..ad369bfc2 100644 --- a/src/emucore/Thumbulator.cxx +++ b/src/emucore/Thumbulator.cxx @@ -18,7 +18,7 @@ //============================================================================ // This class provides Thumb emulation code ("Thumbulator") // 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 //============================================================================ @@ -121,6 +121,9 @@ using Common::Base; #define INC_ARM_CYCLES(m) #endif +constexpr float PC_REG = 15; +#define PIPED + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size, 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; reset(); +#ifdef PIPED + fetch(); // pipeline[n-2] // 0 + next(); // 0 -> 1 + fetch(); // pipeline[n-1] // 1 + decode(); // pipeline[n-2] // 1 +#endif for(;;) { - if(execute()) break; + //if(execute()) break; + if(step()) break; #ifndef UNSAFE_OPTIMIZATIONS if(_stats.instructions > 500000) // way more than would otherwise be possible throw runtime_error("instructions > 500000"); @@ -276,19 +286,43 @@ void Thumbulator::dump_regs() statusMsg << endl << "SP = " << Base::HEX8 << reg_norm[13] << " " << "LR = " << Base::HEX8 << reg_norm[14] << " " - << "PC = " << Base::HEX8 << reg_norm[15] << " " + << "PC = " << Base::HEX8 << reg_norm[PC_REG] << " " << endl; } #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 - uInt32 data; + uInt32 inst; #ifdef THUMB_CYCLE_COUNT - _pipeIdx = (++_pipeIdx) % 3; #ifdef MERGE_I_S if(_lastCycleType[2] == CycleType::I) @@ -307,41 +341,71 @@ uInt32 Thumbulator::fetch16(uInt32 addr) // } // else //#endif - INC_S_CYCLES(addr, AccessType::prefetch); + INC_S_CYCLES(pc, AccessType::prefetch); //INC_S_CYCLES(addr, _prefetchAccessType[_pipeIdx]); } else { - INC_N_CYCLES(addr, AccessType::prefetch); // or ::data ? + INC_N_CYCLES(pc, AccessType::prefetch); // or ::data ? //INC_N_CYCLES(addr, _prefetchAccessType[_pipeIdx]); } _prefetchCycleType[_pipeIdx] = CycleType::S; // default //_prefetchAccessType[_pipeIdx] = AccessType::prefetch; // default #endif - switch(addr & 0xF0000000) + switch(pc & 0xF0000000) { case 0x00000000: //ROM - addr &= ROMADDMASK; - if(addr < 0x50) - fatalError("fetch16", addr, "abort"); - addr >>= 1; - data = CONV_RAMROM(rom[addr]); - DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return data; + pc &= ROMADDMASK; + if(pc < 0x50) + fatalError("fetch", pc, "abort"); + pc >>= 1; + inst = CONV_RAMROM(rom[pc]); + DO_DBUG(statusMsg << "fetch(" << Base::HEX8 << pc << ")=" << Base::HEX4 << inst << endl); + break; case 0x40000000: //RAM - addr &= RAMADDMASK; - addr >>= 1; - data = CONV_RAMROM(ram[addr]); - DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return data; + pc &= RAMADDMASK; + pc >>= 1; + inst = CONV_RAMROM(ram[pc]); + DO_DBUG(statusMsg << "fetch(" << Base::HEX8 << pc << ")=" << Base::HEX4 << inst << endl); + break; + + default: + inst = 0; + fatalError("fetch", pc, "abort"); } - return fatalError("fetch16", addr, "abort"); #else - addr &= ROMADDMASK; - addr >>= 1; - return CONV_RAMROM(rom[addr]); + pc &= ROMADDMASK; + pc >>= 1; + 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 } @@ -722,7 +786,7 @@ uInt32 Thumbulator::read_register(uInt32 reg) uInt32 data = reg_norm[reg]; DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl); #ifndef UNSAFE_OPTIMIZATIONS - if(reg == 15) + if(reg == PC_REG) { 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); //#ifndef UNSAFE_OPTIMIZATIONS // this fails when combined with read_register UNSAFE_OPTIMIZATIONS - if(reg == 15) + if(reg == PC_REG) { data &= ~1; if(isFlowBreak) @@ -750,10 +814,20 @@ void Thumbulator::write_register(uInt32 reg, uInt32 data, bool isFlowBreak) ++_stats.taken; #endif // 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_N_CYCLES(reg_norm[15] + 4, AccessType::prefetch); + INC_N_CYCLES(reg_norm[PC_REG] + 4, AccessType::prefetch); 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 @@ -1040,34 +1114,39 @@ Thumbulator::Op Thumbulator::decodeInstructionWord(uint16_t inst) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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; - inst = fetch16(instructionPtr); + //uInt32 instructionPtr = pc - 2; + ////inst = fetch(instructionPtr); + //inst = fetch(); - pc += 2; - write_register(15, pc, false); + //pc += 2; + //write_register(PC_REG, pc, false); DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " "); #ifndef UNSAFE_OPTIMIZATIONS ++_stats.instructions; #endif - Op decodedOp; -#ifndef UNSAFE_OPTIMIZATIONS - if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize) - decodedOp = decodedRom[instructionPtr >> 1]; - else - decodedOp = decodeInstructionWord(inst); -#else - decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1]; -#endif +// Op decodedOp; +//#ifndef UNSAFE_OPTIMIZATIONS +// if ((instructionPtr & 0xF0000000) == 0 && instructionPtr < romSize) +// decodedOp = decodedRom[instructionPtr >> 1]; +// else +// decodedOp = decodeInstructionWord(inst); +//#else +// decodedOp = decodedRom[(instructionPtr & ROMADDMASK) >> 1]; +//#endif -#ifdef COUNT_OPS - ++opCount[int(decodedOp)]; -#endif switch (decodedOp) { //ADC case Op::adc: { @@ -1158,14 +1237,16 @@ int Thumbulator::execute() ra = read_register(rd); rb = read_register(rm); rc = ra + rb; - if(rd == 15) + if(rd == PC_REG) { #ifndef UNSAFE_OPTIMIZATIONS if((rc & 1) == 0) fatalError("add pc", pc, rc, " produced an arm address"); #endif //rc &= ~1; //write_register may do this as well +#ifndef PIPED rc += 2; //The program counter is special +#endif } //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); write_register(rd, rc); @@ -1178,7 +1259,7 @@ int Thumbulator::execute() rd = (inst >> 8) & 0x7; rb <<= 2; 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; write_register(rd, rc); return 0; @@ -1307,83 +1388,84 @@ int Thumbulator::execute() rb |= (~0U) << 8; rb <<= 1; rb += pc; +#ifndef PIPED rb += 2; - +#endif op = (inst >> 8) & 0xF; switch(op) { case 0x0: //b eq z set DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl); if(cpsr & CPSR_Z) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x1: //b ne z clear DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl); if(!(cpsr & CPSR_Z)) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x2: //b cs c set DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl); if(cpsr & CPSR_C) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x3: //b cc c clear DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl); if(!(cpsr & CPSR_C)) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x4: //b mi n set DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl); if(cpsr & CPSR_N) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x5: //b pl n clear DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl); if(!(cpsr & CPSR_N)) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x6: //b vs v set DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl); if(cpsr & CPSR_V) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x7: //b vc v clear DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl); if(!(cpsr & CPSR_V)) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x8: //b hi c set z clear DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl); if((cpsr & CPSR_C) && (!(cpsr & CPSR_Z))) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0x9: //b ls c clear or z set DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl); if((cpsr & CPSR_Z) || (!(cpsr & CPSR_C))) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0xA: //b ge N == V DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl); if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0xB: //b lt N != V DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl); if((!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || (((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0xC: //b gt Z==0 and N == V @@ -1392,7 +1474,7 @@ int Thumbulator::execute() { if(((cpsr & CPSR_N) && (cpsr & CPSR_V)) || ((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V)))) - write_register(15, rb); + write_register(PC_REG, rb); } return 0; @@ -1401,7 +1483,7 @@ int Thumbulator::execute() if((cpsr & CPSR_Z) || (!(cpsr & CPSR_N) && (cpsr & CPSR_V)) || (((cpsr & CPSR_N)) && !(cpsr & CPSR_V))) - write_register(15, rb); + write_register(PC_REG, rb); return 0; case 0xE: @@ -1428,9 +1510,11 @@ int Thumbulator::execute() rb |= (~0U) << 11; rb <<= 1; rb += pc; +#ifndef PIPED rb += 2; +#endif DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl); - write_register(15, rb); + write_register(PC_REG, rb); return 0; } @@ -1474,10 +1558,12 @@ int Thumbulator::execute() //branch to thumb rb = read_register(14); rb += (inst & ((1 << 11) - 1)) << 1; +#ifndef PIPED rb += 2; +#endif DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); write_register(14, (pc-2) | 1); - write_register(15, rb); + write_register(PC_REG, rb); return 0; } else if((inst & 0x1800) == 0x0800) //H=b01 @@ -1488,10 +1574,12 @@ int Thumbulator::execute() rb = read_register(14); rb += (inst & ((1 << 11) - 1)) << 1; rb &= 0xFFFFFFFC; +#ifndef PIPED rb += 2; +#endif DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); write_register(14, (pc-2) | 1); - write_register(15, rb); + write_register(PC_REG, rb); return 0; } break; @@ -1503,12 +1591,14 @@ int Thumbulator::execute() DO_DISS(statusMsg << "blx r" << dec << rm << endl); rc = read_register(rm); //fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc); +#ifndef PIPED rc += 2; +#endif if(rc & 1) { write_register(14, (pc-2) | 1); //rc &= ~1; - write_register(15, rc); + write_register(PC_REG, rc); return 0; } else @@ -1524,13 +1614,15 @@ int Thumbulator::execute() rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "bx r" << dec << rm << endl); rc = read_register(rm); +#ifndef PIPED rc += 2; +#endif //fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc); if(rc & 1) { // branch to odd address denotes 16 bit ARM code //rc &= ~1; - write_register(15, rc); + write_register(PC_REG, rc); return 0; } else @@ -1752,9 +1844,11 @@ int Thumbulator::execute() if (handled) { rc = read_register(14); // lr +#ifndef PIPED rc += 2; +#endif //rc &= ~1; - write_register(15, rc); + write_register(PC_REG, rc); //_totalCycles += 100; // just a wild guess return 0; } @@ -1900,7 +1994,7 @@ int Thumbulator::execute() 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. if((inst & (1 << rn)) == 0) write_register(rn, sp); @@ -1940,7 +2034,7 @@ int Thumbulator::execute() rd = (inst >> 8) & 0x07; rb <<= 2; DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] "); - ra = read_register(15); + ra = read_register(PC_REG); ra &= ~3; rb += ra; DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl); @@ -2226,16 +2320,18 @@ int Thumbulator::execute() rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl); 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); //rc|=1; } - if(rd == 15) +#ifndef PIPED + if(rd == PC_REG) { //rc &= ~1; //write_register may do this as well rc += 2; //The program counter is special } +#endif write_register(rd, rc); return 0; } @@ -2364,8 +2460,10 @@ int Thumbulator::execute() { INC_S_CYCLES(sp, AccessType::data); } +#ifndef PIPED rc += 2; - write_register(15, rc); +#endif + write_register(PC_REG, rc); sp += 4; } write_register(13, sp); @@ -2879,9 +2977,13 @@ int Thumbulator::reset() { reg_norm.fill(0); - reg_norm[13] = cStack; // SP - reg_norm[14] = cBase; // LR - reg_norm[15] = (cStart + 2) | 1; // PC (+2 for pipeline, lower bit for THUMB) + reg_norm[13] = cStack; // SP + reg_norm[14] = cBase; // LR +#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; handler_mode = false; diff --git a/src/emucore/Thumbulator.hxx b/src/emucore/Thumbulator.hxx index dcea53e5e..53649dfa0 100644 --- a/src/emucore/Thumbulator.hxx +++ b/src/emucore/Thumbulator.hxx @@ -210,6 +210,10 @@ class Thumbulator prefetch, branch, data }; #endif + struct Pipeline { + uInt32 inst; + Op op; + }; const std::array ChipProps = {{ { 70.0, 4, 1 }, // LPC2101_02_03 @@ -222,7 +226,6 @@ class Thumbulator string doRun(uInt32& cycles, bool irqDrivenAudio); uInt32 read_register(uInt32 reg); void write_register(uInt32 reg, uInt32 data, bool isFlowBreak = true); - uInt32 fetch16(uInt32 addr); uInt32 read16(uInt32 addr); uInt32 read32(uInt32 addr); #ifndef UNSAFE_OPTIMIZATIONS @@ -251,7 +254,12 @@ class Thumbulator void dump_counters(); void dump_regs(); #endif + void next(); + int step(); + void fetch(); + void decode(); int execute(); + int reset(); #ifdef THUMB_CYCLE_COUNT @@ -309,6 +317,7 @@ class Thumbulator #ifdef THUMB_CYCLE_COUNT double _armCyclesFactor{1.05}; uInt32 _pipeIdx{0}; + Pipeline _pipe[3]; CycleType _prefetchCycleType[3]{CycleType::S}; CycleType _lastCycleType[3]{CycleType::S}; AccessType _prefetchAccessType[3]{AccessType::data};