mirror of https://github.com/stella-emu/stella.git
Start to adapt thumbulator.
This commit is contained in:
parent
1e4ab23283
commit
061b0124a4
|
@ -14,6 +14,7 @@
|
|||
"C_Cpp.intelliSenseEngine": "Default",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.associations": {
|
||||
"*.h": "cpp",
|
||||
"__functional_base": "cpp",
|
||||
"array": "cpp",
|
||||
"istream": "cpp",
|
||||
|
@ -103,6 +104,18 @@
|
|||
"version": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp"
|
||||
"concepts": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"any": "cpp",
|
||||
"charconv": "cpp",
|
||||
"csignal": "cpp",
|
||||
"execution": "cpp",
|
||||
"numbers": "cpp",
|
||||
"span": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"variant": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"format": "cpp",
|
||||
"*.inc": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,623 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// This code is based on the "Thumbulator" by David Welch (dwelch@dwelch.com)
|
||||
// Code is public domain and used with the author's consent
|
||||
//============================================================================
|
||||
|
||||
#include "CortexM0.hxx"
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#define READ32(data, addr) ( \
|
||||
(((uInt8*)(data))[(addr)]) | \
|
||||
(((uInt8*)(data))[(addr) + 1] << 8) | \
|
||||
(((uInt8*)(data))[(addr) + 2] << 16) | \
|
||||
(((uInt8*)(data))[(addr) + 3] << 24) \
|
||||
)
|
||||
|
||||
#define READ16(data, addr) ( \
|
||||
(((uInt8*)(data))[(addr)]) | \
|
||||
(((uInt8*)(data))[(addr) + 1] << 8) \
|
||||
)
|
||||
|
||||
#define WRITE32(data, addr, value) \
|
||||
((uInt8*)(data))[(addr)] = (value); \
|
||||
((uInt8*)(data))[(addr) + 1] = (value) >> 8; \
|
||||
((uInt8*)(data))[(addr) + 2] = (value) >> 16; \
|
||||
((uInt8*)(data))[(addr) + 3] = (value) >> 24;
|
||||
|
||||
#define WRITE16(data, addr, value) \
|
||||
((uInt8*)(data))[(addr)] = value; \
|
||||
((uInt8*)(data))[(addr) + 1] = (value) >> 8;
|
||||
#else
|
||||
#define READ32(data, addr) (((uInt32*)(data))[(addr) >> 2])
|
||||
#define READ16(data, addr) (((uInt16*)(data))[(addr) >> 1])
|
||||
|
||||
#define WRITE32(data, addr, value) ((uInt32*)(data))[(addr) >> 2] = value;
|
||||
#define WRITE16(data, addr, value) ((uInt16*)(data))[(addr) >> 1] = value;
|
||||
#endif
|
||||
|
||||
#define read_register(reg) reg_norm[reg]
|
||||
#define write_register(reg, data) reg_norm[reg]=(data)
|
||||
|
||||
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, blx_arm,
|
||||
blx2,
|
||||
bx,
|
||||
cmn,
|
||||
cmp1, cmp2, cmp3,
|
||||
cps,
|
||||
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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
case 0x0: //b eq z set
|
||||
return Op::beq;
|
||||
|
||||
case 0x1: //b ne z clear
|
||||
return Op::bne;
|
||||
|
||||
case 0x2: //b cs c set
|
||||
return Op::bcs;
|
||||
|
||||
case 0x3: //b cc c clear
|
||||
return Op::bcc;
|
||||
|
||||
case 0x4: //b mi n set
|
||||
return Op::bmi;
|
||||
|
||||
case 0x5: //b pl n clear
|
||||
return Op::bpl;
|
||||
|
||||
case 0x6: //b vs v set
|
||||
return Op::bvs;
|
||||
|
||||
case 0x7: //b vc v clear
|
||||
return Op::bvc;
|
||||
|
||||
case 0x8: //b hi c set z clear
|
||||
return Op::bhi;
|
||||
|
||||
case 0x9: //b ls c clear or z set
|
||||
return Op::bls;
|
||||
|
||||
case 0xA: //b ge N == V
|
||||
return Op::bge;
|
||||
|
||||
case 0xB: //b lt N != V
|
||||
return Op::blt;
|
||||
|
||||
case 0xC: //b gt Z==0 and N == V
|
||||
return Op::bgt;
|
||||
|
||||
case 0xD: //b le Z==1 or N != V
|
||||
return Op::ble;
|
||||
|
||||
default:
|
||||
return Op::invalid;
|
||||
}
|
||||
}
|
||||
|
||||
//B(2) unconditional branch
|
||||
if((inst & 0xF800) == 0xE000) return Op::b2;
|
||||
|
||||
//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;
|
||||
else if((inst & 0x1800) == 0x0800) return Op::blx_arm;
|
||||
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;
|
||||
|
||||
//CPS
|
||||
if((inst & 0xFFE8) == 0xB660) return Op::cps;
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::CortexM0()
|
||||
{
|
||||
myPageMap = make_unique<uInt8[]>(PAGEMAP_SIZE);
|
||||
std::memset(myPageMap.get(), 0xff, PAGEMAP_SIZE);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::mapRegionData(uInt32 pageBase,
|
||||
uInt32 pageCount, bool readOnly, uInt8* backingStore)
|
||||
{
|
||||
MemoryRegion& region =
|
||||
setupMapping(pageBase, pageCount, readOnly, MemoryRegionType::directData);
|
||||
|
||||
region.access.accessData.backingStore = backingStore;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::mapRegionCode(uInt32 pageBase,
|
||||
uInt32 pageCount, bool readOnly, uInt8* backingStore)
|
||||
{
|
||||
MemoryRegion& region =
|
||||
setupMapping(pageBase, pageCount, readOnly, MemoryRegionType::directCode);
|
||||
|
||||
region.access.accessCode.backingStore = backingStore;
|
||||
region.access.accessCode.ops = static_cast<uInt8*>(std::malloc((pageCount * PAGE_SIZE) >> 1));
|
||||
|
||||
for (size_t i = 0; i < pageCount * PAGE_SIZE; i += 2)
|
||||
region.access.accessCode.ops[i >> 1] =
|
||||
decodeInstructionWord(READ16(backingStore, i));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::mapRegionDelegate(uInt32 pageBase, uInt32 pageCount, bool readOnly,
|
||||
CortexM0::BusTransactionDelegate* delegate)
|
||||
{
|
||||
MemoryRegion& region =
|
||||
setupMapping(pageBase, pageCount, readOnly, MemoryRegionType::delegate);
|
||||
|
||||
region.access.delegate = delegate;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::mapDefault(CortexM0::BusTransactionDelegate* delegate)
|
||||
{
|
||||
myDefaultDelegate = delegate;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::reset()
|
||||
{
|
||||
reg_norm.fill(0);
|
||||
znFlags = cFlag = vFlag = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CortexM0& CortexM0::setRegister(uInt8 regno, uInt32 value)
|
||||
{
|
||||
write_register(regno, value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
uInt8 CortexM0::decodeInstructionWord(uInt16 instructionWord)
|
||||
{
|
||||
return static_cast<uInt8>(::decodeInstructionWord(instructionWord));
|
||||
}
|
||||
|
||||
CortexM0::MemoryRegion& CortexM0::setupMapping(uInt32 pageBase, uInt32 pageCount,
|
||||
bool readOnly, CortexM0::MemoryRegionType type)
|
||||
{
|
||||
if (myNextRegionIndex == 0xff) throw runtime_error("no free memory region");
|
||||
const uInt8 regionIndex = myNextRegionIndex++;
|
||||
|
||||
MemoryRegion& region = myRegions[regionIndex];
|
||||
|
||||
region.type = type;
|
||||
region.base = pageBase * PAGE_SIZE;
|
||||
region.size = pageCount * PAGE_SIZE;
|
||||
region.readOnly = readOnly;
|
||||
|
||||
for (uInt32 page = pageBase; page < pageBase + pageCount; page++)
|
||||
myPageMap[page] = regionIndex;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
CortexM0::BusTransactionResult CortexM0::read32(uInt32 address, uInt32& value)
|
||||
{
|
||||
MemoryRegion& region = myRegions[myPageMap[address / PAGE_SIZE]];
|
||||
|
||||
switch (region.type) {
|
||||
case MemoryRegionType::delegate:
|
||||
return region.access.delegate->read32(address, value);
|
||||
|
||||
case MemoryRegionType::directCode:
|
||||
value = READ32(region.access.accessCode.backingStore, address - region.base);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
case MemoryRegionType::directData:
|
||||
value = READ32(region.access.accessData.backingStore, address - region.base);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
default:
|
||||
return myDefaultDelegate ?
|
||||
myDefaultDelegate->read32(address, value) : BusTransactionResult::fail;
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::BusTransactionResult CortexM0::read16(uInt32 address, uInt16& value)
|
||||
{
|
||||
MemoryRegion& region = myRegions[myPageMap[address / PAGE_SIZE]];
|
||||
|
||||
switch (region.type) {
|
||||
case MemoryRegionType::delegate:
|
||||
return region.access.delegate->read16(address, value);
|
||||
|
||||
case MemoryRegionType::directCode:
|
||||
value = READ16(region.access.accessCode.backingStore, address - region.base);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
case MemoryRegionType::directData:
|
||||
value = READ16(region.access.accessData.backingStore, address - region.base);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
default:
|
||||
return myDefaultDelegate ?
|
||||
myDefaultDelegate->read16(address, value) : BusTransactionResult::fail;
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::BusTransactionResult CortexM0::write32(uInt32 address, uInt32 value)
|
||||
{
|
||||
MemoryRegion& region = myRegions[myPageMap[address / PAGE_SIZE]];
|
||||
|
||||
switch (region.type) {
|
||||
case MemoryRegionType::delegate:
|
||||
return region.access.delegate->write32(address, value);
|
||||
|
||||
case MemoryRegionType::directCode:
|
||||
WRITE32(region.access.accessCode.backingStore, address - region.base, value);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
case MemoryRegionType::directData:
|
||||
WRITE32(region.access.accessData.backingStore, address - region.base, value);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
default:
|
||||
return myDefaultDelegate ?
|
||||
myDefaultDelegate->write32(address, value) : BusTransactionResult::fail;
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::BusTransactionResult CortexM0::write16(uInt32 address, uInt16 value)
|
||||
{
|
||||
MemoryRegion& region = myRegions[myPageMap[address / PAGE_SIZE]];
|
||||
|
||||
switch (region.type) {
|
||||
case MemoryRegionType::delegate:
|
||||
return region.access.delegate->write16(address, value);
|
||||
|
||||
case MemoryRegionType::directCode: {
|
||||
const uInt32 offset = address - region.base;
|
||||
|
||||
WRITE16(region.access.accessCode.backingStore, offset, value);
|
||||
region.access.accessCode.ops[offset >> 1] = decodeInstructionWord(value);
|
||||
|
||||
return BusTransactionResult::ok;
|
||||
}
|
||||
|
||||
case MemoryRegionType::directData:
|
||||
WRITE16(region.access.accessData.backingStore, address - region.base, value);
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
default:
|
||||
return myDefaultDelegate ?
|
||||
myDefaultDelegate->write16(address, value) : BusTransactionResult::fail;
|
||||
}
|
||||
}
|
||||
|
||||
CortexM0::BusTransactionResult CortexM0::fetch16(uInt32 address, uInt16& value, uInt8& op)
|
||||
{
|
||||
MemoryRegion& region = myRegions[myPageMap[address / PAGE_SIZE]];
|
||||
|
||||
switch (region.type) {
|
||||
case MemoryRegionType::delegate:
|
||||
return region.access.delegate->fetch16(address, value, op);
|
||||
|
||||
case MemoryRegionType::directCode: {
|
||||
const uInt32 offset = address - region.base;
|
||||
|
||||
value = READ16(region.access.accessCode.backingStore, offset);
|
||||
op = region.access.accessCode.ops[offset >> 1];
|
||||
|
||||
return BusTransactionResult::ok;
|
||||
}
|
||||
|
||||
case MemoryRegionType::directData:
|
||||
value = READ16(region.access.accessCode.backingStore, address - region.base);
|
||||
op = decodeInstructionWord(value);
|
||||
|
||||
return BusTransactionResult::ok;
|
||||
|
||||
default:
|
||||
return myDefaultDelegate ?
|
||||
myDefaultDelegate->fetch16(address, value, op) : BusTransactionResult::fail;
|
||||
}
|
||||
}
|
||||
|
||||
void CortexM0::do_cvflag(uInt32 a, uInt32 b, uInt32 c)
|
||||
{
|
||||
uInt32 rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in
|
||||
rc >>= 31; //carry in in lsbit
|
||||
a >>= 31;
|
||||
b >>= 31;
|
||||
uInt32 rd = (rc & 1) + (a & 1) + (b & 1); //carry out
|
||||
rd >>= 1; //carry out in lsbit
|
||||
|
||||
vFlag = (rc ^ rd) & 1; //if carry in != carry out then signed overflow
|
||||
|
||||
rc += a + b; //carry out
|
||||
cFlag = rc & 2;
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// This code is based on the "Thumbulator" by David Welch (dwelch@dwelch.com)
|
||||
// Code is public domain and used with the author's consent
|
||||
//============================================================================
|
||||
|
||||
#ifndef CORTEX_M0
|
||||
#define CORTEX_M0
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
class CortexM0
|
||||
{
|
||||
public:
|
||||
enum class BusTransactionResult : uInt8 {
|
||||
ok,
|
||||
stopAndRollback,
|
||||
stop,
|
||||
fail
|
||||
};
|
||||
|
||||
class BusTransactionDelegate {
|
||||
public:
|
||||
virtual ~BusTransactionDelegate() = default;
|
||||
|
||||
virtual BusTransactionResult read32(uInt32 address, uInt32& value) = 0;
|
||||
virtual BusTransactionResult read16(uInt32 address, uInt16& value) = 0;
|
||||
|
||||
virtual BusTransactionResult write32(uInt32 address, uInt32 value) = 0;
|
||||
virtual BusTransactionResult write16(uInt32 address, uInt16 value) = 0;
|
||||
|
||||
virtual BusTransactionResult fetch16(uInt32 address, uInt16& value, uInt8& op) = 0;
|
||||
};
|
||||
|
||||
static constexpr uInt32 PAGE_SIZE = 4096;
|
||||
|
||||
public:
|
||||
CortexM0();
|
||||
|
||||
CortexM0& mapRegionData(uInt32 pageBase, uInt32 pageCount,
|
||||
bool readOnly, uInt8* backingStore);
|
||||
|
||||
CortexM0& mapRegionCode(uInt32 pageBase, uInt32 pageCount,
|
||||
bool readOnly, uInt8* backingStore);
|
||||
|
||||
CortexM0& mapRegionDelegate(uInt32 pageBase, uInt32 pageCount,
|
||||
bool readOnly, BusTransactionDelegate* delegate);
|
||||
|
||||
CortexM0& mapDefault(BusTransactionDelegate* delegate);
|
||||
|
||||
CortexM0& reset();
|
||||
CortexM0& setRegister(uInt8 regno, uInt32 value);
|
||||
|
||||
static uInt8 decodeInstructionWord(uInt16 instructionWord);
|
||||
|
||||
BusTransactionResult run(uInt32 maxCycles, uInt32& cycles);
|
||||
|
||||
private:
|
||||
|
||||
enum class MemoryRegionType : uInt8 {
|
||||
directData,
|
||||
directCode,
|
||||
delegate,
|
||||
unmapped
|
||||
};
|
||||
|
||||
struct MemoryRegionAccessData {
|
||||
uInt8* backingStore;
|
||||
};
|
||||
|
||||
struct MemoryRegionAccessCode {
|
||||
uInt8* backingStore;
|
||||
uInt8* ops;
|
||||
};
|
||||
|
||||
struct MemoryRegion {
|
||||
~MemoryRegion() {
|
||||
if (type == MemoryRegionType::directCode)
|
||||
std::free(access.accessCode.ops);
|
||||
}
|
||||
|
||||
MemoryRegionType type{MemoryRegionType::unmapped};
|
||||
|
||||
uInt32 base;
|
||||
uInt32 size;
|
||||
bool readOnly;
|
||||
|
||||
union {
|
||||
MemoryRegionAccessData accessData;
|
||||
MemoryRegionAccessCode accessCode;
|
||||
BusTransactionDelegate* delegate;
|
||||
} access;
|
||||
};
|
||||
|
||||
private:
|
||||
MemoryRegion& setupMapping(uInt32 pageBase, uInt32 pageCount,
|
||||
bool readOnly, MemoryRegionType type);
|
||||
|
||||
BusTransactionResult read32(uInt32 address, uInt32& value);
|
||||
BusTransactionResult read16(uInt32 address, uInt16& value);
|
||||
|
||||
BusTransactionResult write32(uInt32 address, uInt32 value);
|
||||
BusTransactionResult write16(uInt32 address, uInt16 value);
|
||||
|
||||
BusTransactionResult fetch16(uInt32 address, uInt16& value, uInt8& op);
|
||||
|
||||
void do_cvflag(uInt32 a, uInt32 b, uInt32 c);
|
||||
|
||||
int execute();
|
||||
|
||||
private:
|
||||
std::array<uInt32, 16> reg_norm; // normal execution mode, do not have a thread mode
|
||||
uInt32 znFlags{0};
|
||||
uInt32 cFlag{0};
|
||||
uInt32 vFlag{0};
|
||||
|
||||
std::array<MemoryRegion, 0x100> myRegions;
|
||||
unique_ptr<uInt8[]> myPageMap;
|
||||
uInt8 myNextRegionIndex{0};
|
||||
BusTransactionDelegate* myDefaultDelegate{nullptr};
|
||||
|
||||
static constexpr uInt32
|
||||
CPSR_N = 1u << 31,
|
||||
CPSR_Z = 1u << 30,
|
||||
CPSR_C = 1u << 29,
|
||||
CPSR_V = 1u << 28;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
CortexM0(const CortexM0&) = delete;
|
||||
CortexM0(CortexM0&&) = delete;
|
||||
CortexM0& operator=(const CortexM0&) = delete;
|
||||
CortexM0& operator=(CortexM0&&) = delete;
|
||||
};
|
||||
|
||||
#endif // CORTEX_M0
|
|
@ -62,6 +62,7 @@ MODULE_OBJS := \
|
|||
src/emucore/Console.o \
|
||||
src/emucore/Control.o \
|
||||
src/emucore/ControllerDetector.o \
|
||||
src/emucore/CortexM0.o \
|
||||
src/emucore/DispatchResult.o \
|
||||
src/emucore/Driving.o \
|
||||
src/emucore/EventHandler.o \
|
||||
|
|
|
@ -695,6 +695,7 @@
|
|||
<ClCompile Include="..\..\emucore\elf\ElfLinker.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfUtil.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfEnvironment.cxx" />
|
||||
<ClCompile Include="..\..\emucore\CortexM0.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationTiming.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationWorker.cxx" />
|
||||
<ClCompile Include="..\..\emucore\FBSurface.cxx" />
|
||||
|
@ -1670,6 +1671,7 @@
|
|||
<ClInclude Include="..\..\emucore\elf\ElfLinker.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfUtil.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfEnvironment.hxx" />
|
||||
<ClInclude Include="..\..\emucore\CortexM0.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationTiming.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationWorker.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EventHandlerConstants.hxx" />
|
||||
|
|
|
@ -1242,6 +1242,9 @@
|
|||
<ClCompile Include="..\..\emucore\CartJANE.cxx">
|
||||
<Filter>Source Files\emucore</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\CortexMß.cxx">
|
||||
<Filter>Source Files\emucore</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\debugger\gui\CartJANEWidget.cxx">
|
||||
<Filter>Source Files\debugger\gui</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2528,6 +2531,9 @@
|
|||
<ClInclude Include="..\..\emucore\CartJANE.hxx">
|
||||
<Filter>Header Files\emucore</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\emucore\CortexM0.hxx">
|
||||
<Filter>Header Files\emucore</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\debugger\gui\CartJANEWidget.hxx">
|
||||
<Filter>Header Files\debugger\gui</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in New Issue