diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index df27a6e64..303ec050e 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -35,6 +35,8 @@ using namespace elfEnvironment; namespace { constexpr size_t TRANSACTION_QUEUE_CAPACITY = 16384; + constexpr uInt32 ARM_RUNAHED_MIN = 3; + constexpr uInt32 ARM_RUNAHED_MAX = 6; #ifdef DUMP_ELF void dumpElf(const ElfFile& elf) @@ -201,6 +203,7 @@ void CartridgeELF::reset() std::fill_n(myLastPeekResult.get(), 0x1000, 0); myIsBusDriven = false; myDriveBusValue = 0; + myArmCycles = 0; std::memset(mySectionStack.get(), 0, STACK_SIZE); std::memset(mySectionText.get(), 0, TEXT_SIZE); @@ -292,6 +295,8 @@ const ByteBuffer& CartridgeELF::getImage(size_t& size) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value) { + runArm(); + value = driveBus(address, value); if (address & 0x1000) { @@ -305,6 +310,8 @@ uInt8 CartridgeELF::overdrivePeek(uInt16 address, uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt8 CartridgeELF::overdrivePoke(uInt16 address, uInt8 value) { + runArm(); + return driveBus(address, value); } @@ -452,4 +459,18 @@ void CartridgeELF::jumpToMain() .setRegister(15, myArmEntrypoint); } +void CartridgeELF::runArm() +{ + if (myArmCycles >= (mySystem->cycles() + ARM_RUNAHED_MIN) * myArmCyclesPer6502Cycle) return; + + const uInt32 cyclesGoal = + (mySystem->cycles() + ARM_RUNAHED_MAX) * myArmCyclesPer6502Cycle - myArmCycles; + uInt32 cycles; + + const CortexM0::err_t err = myCortexEmu.run(cyclesGoal, cycles); + myArmCycles += cycles; + + if (err != 0) FatalEmulationError::raise("error executing ARM code: " + CortexM0::describeError(err)); +} + diff --git a/src/emucore/CartELF.hxx b/src/emucore/CartELF.hxx index f5186859c..8fcfe67dc 100644 --- a/src/emucore/CartELF.hxx +++ b/src/emucore/CartELF.hxx @@ -75,6 +75,8 @@ class CartridgeELF: public Cartridge { uInt32 getSystemType() const; void jumpToMain(); + void runArm(); + private: ByteBuffer myImage; size_t myImageSize{0}; @@ -102,6 +104,8 @@ class CartridgeELF: public Cartridge { ConsoleTiming myConsoleTiming{ConsoleTiming::ntsc}; uInt32 myArmCyclesPer6502Cycle{80}; + + uInt64 myArmCycles{0}; }; #endif // CARTRIDGE_ELF diff --git a/src/emucore/CortexM0.cxx b/src/emucore/CortexM0.cxx index f1a6a8ebe..702ffe081 100644 --- a/src/emucore/CortexM0.cxx +++ b/src/emucore/CortexM0.cxx @@ -84,333 +84,391 @@ namespace { constexpr uInt32 PAGEMAP_SIZE = 0x100000000 / 4096; enum class Op : uInt8 { - adc, - add1, add2, add3, add4, add5, add6, add7, - and_, - asr1, asr2, - // b1 variants: - beq, bne, bcs, bcc, bmi, bpl, bvs, bvc, bhi, bls, bge, blt, bgt, ble, - b2, - bic, - bkpt, - // blx1 variants: - bl, blx_thumb, - blx2, - bx, - cmn, - cmp1, cmp2, cmp3, - cpy, - eor, - ldmia, - ldr1, ldr2, ldr3, ldr4, - ldrb1, ldrb2, - ldrh1, ldrh2, - ldrsb, - ldrsh, - lsl1, lsl2, - lsr1, lsr2, - mov1, mov2, mov3, - mul, - mvn, - neg, - orr, - pop, - push, - rev, - rev16, - revsh, - ror, - sbc, - setend, - stmia, - str1, str2, str3, - strb1, strb2, - strh1, strh2, - sub1, sub2, sub3, sub4, - swi, - sxtb, - sxth, - tst, - uxtb, - uxth, - numOps, - invalid, - }; + adc, + add1, add2, add3, add4, add5, add6, add7, + and_, + asr1, asr2, + // b1 variants: + beq, bne, bcs, bcc, bmi, bpl, bvs, bvc, bhi, bls, bge, blt, bgt, ble, + b2, + bic, + bkpt, + // blx1 variants: + bl, blx_thumb, + blx2, + bx, + cmn, + cmp1, cmp2, cmp3, + cpy, + eor, + ldmia, + ldr1, ldr2, ldr3, ldr4, + ldrb1, ldrb2, + ldrh1, ldrh2, + ldrsb, + ldrsh, + lsl1, lsl2, + lsr1, lsr2, + mov1, mov2, mov3, + mul, + mvn, + neg, + orr, + pop, + push, + rev, + rev16, + revsh, + ror, + sbc, + setend, + stmia, + str1, str2, str3, + strb1, strb2, + strh1, strh2, + sub1, sub2, sub3, sub4, + swi, + sxtb, + sxth, + tst, + uxtb, + uxth, + numOps, + invalid, + }; - inline Op decodeInstructionWord(uInt16 inst) + inline Op decodeInstructionWord(uInt16 inst) + { + //ADC add with carry + if((inst & 0xFFC0) == 0x4140) return Op::adc; + + //ADD(1) small immediate two registers + if((inst & 0xFE00) == 0x1C00 && (inst >> 6) & 0x7) return Op::add1; + + //ADD(2) big immediate one register + if((inst & 0xF800) == 0x3000) return Op::add2; + + //ADD(3) three registers + if((inst & 0xFE00) == 0x1800) return Op::add3; + + //ADD(4) two registers one or both high no flags + if((inst & 0xFF00) == 0x4400) return Op::add4; + + //ADD(5) rd = pc plus immediate + if((inst & 0xF800) == 0xA000) return Op::add5; + + //ADD(6) rd = sp plus immediate + if((inst & 0xF800) == 0xA800) return Op::add6; + + //ADD(7) sp plus immediate + if((inst & 0xFF80) == 0xB000) return Op::add7; + + //AND + if((inst & 0xFFC0) == 0x4000) return Op::and_; + + //ASR(1) two register immediate + if((inst & 0xF800) == 0x1000) return Op::asr1; + + //ASR(2) two register + if((inst & 0xFFC0) == 0x4100) return Op::asr2; + + //B(1) conditional branch, decoded into its variants + if((inst & 0xF000) == 0xD000) { - //ADC add with carry - if((inst & 0xFFC0) == 0x4140) return Op::adc; - - //ADD(1) small immediate two registers - if((inst & 0xFE00) == 0x1C00 && (inst >> 6) & 0x7) return Op::add1; - - //ADD(2) big immediate one register - if((inst & 0xF800) == 0x3000) return Op::add2; - - //ADD(3) three registers - if((inst & 0xFE00) == 0x1800) return Op::add3; - - //ADD(4) two registers one or both high no flags - if((inst & 0xFF00) == 0x4400) return Op::add4; - - //ADD(5) rd = pc plus immediate - if((inst & 0xF800) == 0xA000) return Op::add5; - - //ADD(6) rd = sp plus immediate - if((inst & 0xF800) == 0xA800) return Op::add6; - - //ADD(7) sp plus immediate - if((inst & 0xFF80) == 0xB000) return Op::add7; - - //AND - if((inst & 0xFFC0) == 0x4000) return Op::and_; - - //ASR(1) two register immediate - if((inst & 0xF800) == 0x1000) return Op::asr1; - - //ASR(2) two register - if((inst & 0xFFC0) == 0x4100) return Op::asr2; - - //B(1) conditional branch, decoded into its variants - if((inst & 0xF000) == 0xD000) + switch((inst >> 8) & 0xF) { - switch((inst >> 8) & 0xF) - { - case 0x0: //b eq z set - return Op::beq; + case 0x0: //b eq z set + return Op::beq; - case 0x1: //b ne z clear - return Op::bne; + case 0x1: //b ne z clear + return Op::bne; - case 0x2: //b cs c set - return Op::bcs; + case 0x2: //b cs c set + return Op::bcs; - case 0x3: //b cc c clear - return Op::bcc; + case 0x3: //b cc c clear + return Op::bcc; - case 0x4: //b mi n set - return Op::bmi; + case 0x4: //b mi n set + return Op::bmi; - case 0x5: //b pl n clear - return Op::bpl; + case 0x5: //b pl n clear + return Op::bpl; - case 0x6: //b vs v set - return Op::bvs; + case 0x6: //b vs v set + return Op::bvs; - case 0x7: //b vc v clear - return Op::bvc; + case 0x7: //b vc v clear + return Op::bvc; - case 0x8: //b hi c set z clear - return Op::bhi; + case 0x8: //b hi c set z clear + return Op::bhi; - case 0x9: //b ls c clear or z set - return Op::bls; + case 0x9: //b ls c clear or z set + return Op::bls; - case 0xA: //b ge N == V - return Op::bge; + case 0xA: //b ge N == V + return Op::bge; - case 0xB: //b lt N != V - return Op::blt; + case 0xB: //b lt N != V + return Op::blt; - case 0xC: //b gt Z==0 and N == V - return Op::bgt; + case 0xC: //b gt Z==0 and N == V + return Op::bgt; - case 0xD: //b le Z==1 or N != V - return Op::ble; + case 0xD: //b le Z==1 or N != V + return Op::ble; - default: - return Op::invalid; - } + default: + return Op::invalid; } + } - //B(2) unconditional branch - if((inst & 0xF800) == 0xE000) return Op::b2; + //B(2) unconditional branch + if((inst & 0xF800) == 0xE000) return Op::b2; - //BIC - if((inst & 0xFFC0) == 0x4380) return Op::bic; + //BIC + if((inst & 0xFFC0) == 0x4380) return Op::bic; - //BKPT - if((inst & 0xFF00) == 0xBE00) return Op::bkpt; - - //BL/BLX(1) decoded into its variants - if((inst & 0xE000) == 0xE000) - { - if((inst & 0x1800) == 0x1000) return Op::bl; - else if((inst & 0x1800) == 0x1800) return Op::blx_thumb; - return Op::invalid; - } - - //BLX(2) - if((inst & 0xFF87) == 0x4780) return Op::blx2; - - //BX - if((inst & 0xFF87) == 0x4700) return Op::bx; - - //CMN - if((inst & 0xFFC0) == 0x42C0) return Op::cmn; - - //CMP(1) compare immediate - if((inst & 0xF800) == 0x2800) return Op::cmp1; - - //CMP(2) compare register - if((inst & 0xFFC0) == 0x4280) return Op::cmp2; - - //CMP(3) compare high register - if((inst & 0xFF00) == 0x4500) return Op::cmp3; - - //CPY copy high register - if((inst & 0xFFC0) == 0x4600) return Op::cpy; - - //EOR - if((inst & 0xFFC0) == 0x4040) return Op::eor; - - //LDMIA - if((inst & 0xF800) == 0xC800) return Op::ldmia; - - //LDR(1) two register immediate - if((inst & 0xF800) == 0x6800) return Op::ldr1; - - //LDR(2) three register - if((inst & 0xFE00) == 0x5800) return Op::ldr2; - - //LDR(3) - if((inst & 0xF800) == 0x4800) return Op::ldr3; - - //LDR(4) - if((inst & 0xF800) == 0x9800) return Op::ldr4; - - //LDRB(1) - if((inst & 0xF800) == 0x7800) return Op::ldrb1; - - //LDRB(2) - if((inst & 0xFE00) == 0x5C00) return Op::ldrb2; - - //LDRH(1) - if((inst & 0xF800) == 0x8800) return Op::ldrh1; - - //LDRH(2) - if((inst & 0xFE00) == 0x5A00) return Op::ldrh2; - - //LDRSB - if((inst & 0xFE00) == 0x5600) return Op::ldrsb; - - //LDRSH - if((inst & 0xFE00) == 0x5E00) return Op::ldrsh; - - //LSL(1) - if((inst & 0xF800) == 0x0000) return Op::lsl1; - - //LSL(2) two register - if((inst & 0xFFC0) == 0x4080) return Op::lsl2; - - //LSR(1) two register immediate - if((inst & 0xF800) == 0x0800) return Op::lsr1; - - //LSR(2) two register - if((inst & 0xFFC0) == 0x40C0) return Op::lsr2; - - //MOV(1) immediate - if((inst & 0xF800) == 0x2000) return Op::mov1; - - //MOV(2) two low registers - if((inst & 0xFFC0) == 0x1C00) return Op::mov2; - - //MOV(3) - if((inst & 0xFF00) == 0x4600) return Op::mov3; - - //MUL - if((inst & 0xFFC0) == 0x4340) return Op::mul; - - //MVN - if((inst & 0xFFC0) == 0x43C0) return Op::mvn; - - //NEG - if((inst & 0xFFC0) == 0x4240) return Op::neg; - - //ORR - if((inst & 0xFFC0) == 0x4300) return Op::orr; - - //POP - if((inst & 0xFE00) == 0xBC00) return Op::pop; - - //PUSH - if((inst & 0xFE00) == 0xB400) return Op::push; - - //REV - if((inst & 0xFFC0) == 0xBA00) return Op::rev; - - //REV16 - if((inst & 0xFFC0) == 0xBA40) return Op::rev16; - - //REVSH - if((inst & 0xFFC0) == 0xBAC0) return Op::revsh; - - //ROR - if((inst & 0xFFC0) == 0x41C0) return Op::ror; - - //SBC - if((inst & 0xFFC0) == 0x4180) return Op::sbc; - - //SETEND - if((inst & 0xFFF7) == 0xB650) return Op::setend; - - //STMIA - if((inst & 0xF800) == 0xC000) return Op::stmia; - - //STR(1) - if((inst & 0xF800) == 0x6000) return Op::str1; - - //STR(2) - if((inst & 0xFE00) == 0x5000) return Op::str2; - - //STR(3) - if((inst & 0xF800) == 0x9000) return Op::str3; - - //STRB(1) - if((inst & 0xF800) == 0x7000) return Op::strb1; - - //STRB(2) - if((inst & 0xFE00) == 0x5400) return Op::strb2; - - //STRH(1) - if((inst & 0xF800) == 0x8000) return Op::strh1; - - //STRH(2) - if((inst & 0xFE00) == 0x5200) return Op::strh2; - - //SUB(1) - if((inst & 0xFE00) == 0x1E00) return Op::sub1; - - //SUB(2) - if((inst & 0xF800) == 0x3800) return Op::sub2; - - //SUB(3) - if((inst & 0xFE00) == 0x1A00) return Op::sub3; - - //SUB(4) - if((inst & 0xFF80) == 0xB080) return Op::sub4; - - //SWI SoftWare Interupt - if((inst & 0xFF00) == 0xDF00) return Op::swi; - - //SXTB - if((inst & 0xFFC0) == 0xB240) return Op::sxtb; - - //SXTH - if((inst & 0xFFC0) == 0xB200) return Op::sxth; - - //TST - if((inst & 0xFFC0) == 0x4200) return Op::tst; - - //UXTB - if((inst & 0xFFC0) == 0xB2C0) return Op::uxtb; - - //UXTH Zero extend Halfword - if((inst & 0xFFC0) == 0xB280) return Op::uxth; + //BKPT + if((inst & 0xFF00) == 0xBE00) return Op::bkpt; + //BL/BLX(1) decoded into its variants + if((inst & 0xE000) == 0xE000) + { + if((inst & 0x1800) == 0x1000) return Op::bl; + else if((inst & 0x1800) == 0x1800) return Op::blx_thumb; return Op::invalid; } + + //BLX(2) + if((inst & 0xFF87) == 0x4780) return Op::blx2; + + //BX + if((inst & 0xFF87) == 0x4700) return Op::bx; + + //CMN + if((inst & 0xFFC0) == 0x42C0) return Op::cmn; + + //CMP(1) compare immediate + if((inst & 0xF800) == 0x2800) return Op::cmp1; + + //CMP(2) compare register + if((inst & 0xFFC0) == 0x4280) return Op::cmp2; + + //CMP(3) compare high register + if((inst & 0xFF00) == 0x4500) return Op::cmp3; + + //CPY copy high register + if((inst & 0xFFC0) == 0x4600) return Op::cpy; + + //EOR + if((inst & 0xFFC0) == 0x4040) return Op::eor; + + //LDMIA + if((inst & 0xF800) == 0xC800) return Op::ldmia; + + //LDR(1) two register immediate + if((inst & 0xF800) == 0x6800) return Op::ldr1; + + //LDR(2) three register + if((inst & 0xFE00) == 0x5800) return Op::ldr2; + + //LDR(3) + if((inst & 0xF800) == 0x4800) return Op::ldr3; + + //LDR(4) + if((inst & 0xF800) == 0x9800) return Op::ldr4; + + //LDRB(1) + if((inst & 0xF800) == 0x7800) return Op::ldrb1; + + //LDRB(2) + if((inst & 0xFE00) == 0x5C00) return Op::ldrb2; + + //LDRH(1) + if((inst & 0xF800) == 0x8800) return Op::ldrh1; + + //LDRH(2) + if((inst & 0xFE00) == 0x5A00) return Op::ldrh2; + + //LDRSB + if((inst & 0xFE00) == 0x5600) return Op::ldrsb; + + //LDRSH + if((inst & 0xFE00) == 0x5E00) return Op::ldrsh; + + //LSL(1) + if((inst & 0xF800) == 0x0000) return Op::lsl1; + + //LSL(2) two register + if((inst & 0xFFC0) == 0x4080) return Op::lsl2; + + //LSR(1) two register immediate + if((inst & 0xF800) == 0x0800) return Op::lsr1; + + //LSR(2) two register + if((inst & 0xFFC0) == 0x40C0) return Op::lsr2; + + //MOV(1) immediate + if((inst & 0xF800) == 0x2000) return Op::mov1; + + //MOV(2) two low registers + if((inst & 0xFFC0) == 0x1C00) return Op::mov2; + + //MOV(3) + if((inst & 0xFF00) == 0x4600) return Op::mov3; + + //MUL + if((inst & 0xFFC0) == 0x4340) return Op::mul; + + //MVN + if((inst & 0xFFC0) == 0x43C0) return Op::mvn; + + //NEG + if((inst & 0xFFC0) == 0x4240) return Op::neg; + + //ORR + if((inst & 0xFFC0) == 0x4300) return Op::orr; + + //POP + if((inst & 0xFE00) == 0xBC00) return Op::pop; + + //PUSH + if((inst & 0xFE00) == 0xB400) return Op::push; + + //REV + if((inst & 0xFFC0) == 0xBA00) return Op::rev; + + //REV16 + if((inst & 0xFFC0) == 0xBA40) return Op::rev16; + + //REVSH + if((inst & 0xFFC0) == 0xBAC0) return Op::revsh; + + //ROR + if((inst & 0xFFC0) == 0x41C0) return Op::ror; + + //SBC + if((inst & 0xFFC0) == 0x4180) return Op::sbc; + + //SETEND + if((inst & 0xFFF7) == 0xB650) return Op::setend; + + //STMIA + if((inst & 0xF800) == 0xC000) return Op::stmia; + + //STR(1) + if((inst & 0xF800) == 0x6000) return Op::str1; + + //STR(2) + if((inst & 0xFE00) == 0x5000) return Op::str2; + + //STR(3) + if((inst & 0xF800) == 0x9000) return Op::str3; + + //STRB(1) + if((inst & 0xF800) == 0x7000) return Op::strb1; + + //STRB(2) + if((inst & 0xFE00) == 0x5400) return Op::strb2; + + //STRH(1) + if((inst & 0xF800) == 0x8000) return Op::strh1; + + //STRH(2) + if((inst & 0xFE00) == 0x5200) return Op::strh2; + + //SUB(1) + if((inst & 0xFE00) == 0x1E00) return Op::sub1; + + //SUB(2) + if((inst & 0xF800) == 0x3800) return Op::sub2; + + //SUB(3) + if((inst & 0xFE00) == 0x1A00) return Op::sub3; + + //SUB(4) + if((inst & 0xFF80) == 0xB080) return Op::sub4; + + //SWI SoftWare Interupt + if((inst & 0xFF00) == 0xDF00) return Op::swi; + + //SXTB + if((inst & 0xFFC0) == 0xB240) return Op::sxtb; + + //SXTH + if((inst & 0xFFC0) == 0xB200) return Op::sxth; + + //TST + if((inst & 0xFFC0) == 0x4200) return Op::tst; + + //UXTB + if((inst & 0xFFC0) == 0xB2C0) return Op::uxtb; + + //UXTH Zero extend Halfword + if((inst & 0xFFC0) == 0xB280) return Op::uxth; + + return Op::invalid; + } + + string describeErrorCode(CortexM0::err_t err) { + if (CortexM0::isErrCustom(err)) { + ostringstream s; + s << "custom error " << CortexM0::getErrCustom(err); + + return s.str(); + } + + switch (CortexM0::getErrInstrinsic(err)) { + case CortexM0::ERR_UNMAPPED_READ32: + return "unmapped read32"; + + case CortexM0::ERR_UNMAPPED_READ16: + return "unmapped read16"; + + case CortexM0::ERR_UNMAPPED_READ8: + return "unmapped read8"; + + case CortexM0::ERR_UNMAPPED_WRITE32: + return "unmapped write32"; + + case CortexM0::ERR_UNMAPPED_WRITE16: + return "unmapped write16"; + + case CortexM0::ERR_UNMAPPED_WRITE8: + return "unmapped write8"; + + case CortexM0::ERR_UNMAPPED_FETCH16: + return "unmapped fetch"; + + case CortexM0::ERR_WRITE_ACCESS_DENIED: + return "write access denied"; + + case CortexM0::ERR_ACCESS_ALIGNMENT_FAULT: + return "alignment fault"; + + case CortexM0::ERR_BKPT: + return "breakpoint encountered"; + + case CortexM0::ERR_INVALID_OPERATING_MODE: + return "invalid operation mode"; + + case CortexM0::ERR_UNIMPLEMENTED_INST: + return "instruction not implemented"; + + case CortexM0::ERR_SWI: + return "supervisor call"; + + case CortexM0::ERR_UNDEFINED_INST: + return "undefined instruction"; + } + + ostringstream s; + s << "unknown instrinsic error " << CortexM0::getErrInstrinsic(err); + + return s.str(); + } } CortexM0::err_t CortexM0::BusTransactionDelegate::read32(uInt32 address, uInt32& value, CortexM0& cortex) @@ -454,6 +512,19 @@ CortexM0::err_t CortexM0::BusTransactionDelegate::fetch16( return 0; } +string CortexM0::describeError(err_t err) { + if (err == ERR_NONE) return "no error"; + + ostringstream s; + s + << describeErrorCode(err) << " : 0x" + << std::hex << std::setw(8) << std::setfill('0') + << getErrExtra(err); + + return s.str(); +} + + CortexM0::CortexM0() { myPageMap = make_unique(PAGEMAP_SIZE); diff --git a/src/emucore/CortexM0.hxx b/src/emucore/CortexM0.hxx index babf422f3..81c271504 100644 --- a/src/emucore/CortexM0.hxx +++ b/src/emucore/CortexM0.hxx @@ -71,6 +71,10 @@ class CortexM0 return (err & 0xffffffff) >> 8; } + static inline uInt8 getErrInstrinsic(err_t err) { + return err; + } + static inline uInt32 getErrExtra(err_t err) { return err >> 32; } @@ -83,6 +87,8 @@ class CortexM0 return static_cast(code) | (static_cast(extra) << 32); } + static string describeError(err_t error); + public: CortexM0();