From cae2266df0ea6143c8c5997177f65317930e7c3d Mon Sep 17 00:00:00 2001 From: thrust26 Date: Tue, 31 Mar 2020 18:12:38 +0200 Subject: [PATCH] Enhance disassembly (determine correct bank offset, preliminary solution for simple 4K bankswitching, e.g. standard Atari) --- src/debugger/CartDebug.cxx | 54 ++++++++++++++++++++++++++++++++++---- src/debugger/CartDebug.hxx | 36 ++++++++++++++----------- src/debugger/DiStella.cxx | 6 ++--- src/emucore/Device.hxx | 22 +++++++++------- src/emucore/System.cxx | 8 +++--- 5 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 9e8fc88b6..89c3d94d0 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -238,11 +238,51 @@ string CartDebug::toString() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDebug::disassemble(bool force) +{ + uInt16 PC = myDebugger.cpuDebug().pc(); + int bank = (PC & 0x1000) ? getBank(PC) : int(myBankInfo.size()) - 1; + + return disassemble(bank, PC, force); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDebug::disassembleBank(int bank) +{ + // isolate the high 3 address bits, count them and + // select the most frequent to define the bank offset + BankInfo& info = myBankInfo[bank]; + uInt16 count[8]; + + for(int i = 0; i < 8; ++i) + count[i] = 0; + + for(uInt32 addr = 0x1000; addr < 0x1000 + info.size; ++addr) + { + Device::AccessFlags flags = mySystem.getAccessFlags(addr); + // only count really accessed addresses + if (flags & ~Device::ROW) + count[(flags & Device::HADDR) >> 13]++; + } + uInt16 max = 0, maxIdx = 0; + for(int idx = 0; idx < 8; ++idx) + { + if(count[idx] > max) + { + max = count[idx]; + maxIdx = idx; + } + } + info.offset = maxIdx << 13 | 0x1000; + + return disassemble(bank, info.offset, true); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDebug::disassemble(int bank, uInt16 PC, bool force) { // Test current disassembly; don't re-disassemble if it hasn't changed // Also check if the current PC is in the current list bool bankChanged = myConsole.cartridge().bankChanged(); - uInt16 PC = myDebugger.cpuDebug().pc(); int pcline = addressToLine(PC); bool pcfound = (pcline != -1) && (uInt32(pcline) < myDisassembly.list.size()) && (myDisassembly.list[pcline].disasm[0] != '.'); @@ -254,8 +294,9 @@ bool CartDebug::disassemble(bool force) if(changed) { // Are we disassembling from ROM or ZP RAM? - BankInfo& info = (PC & 0x1000) ? myBankInfo[getBank(PC)] : - myBankInfo[myBankInfo.size()-1]; + BankInfo& info = myBankInfo[bank]; + //(PC & 0x1000) ? myBankInfo[getBank(PC)] : + //myBankInfo[myBankInfo.size()-1]; // If the offset has changed, all old addresses must be 'converted' // For example, if the list contains any $fxxx and the address space is now @@ -1044,6 +1085,7 @@ string CartDebug::saveDisassembly() // prepare for switching banks myConsole.cartridge().unlockBank(); + uInt32 origin = 0; for(int bank = 0; bank < bankCount; ++bank) { @@ -1055,7 +1097,8 @@ string CartDebug::saveDisassembly() BankInfo& info = myBankInfo[bank]; // TODO: make PageAccess ready for multi-bank ROMs - disassemble(); + // TODO: define offset if still undefined + disassembleBank(bank); // An empty address list means that DiStella can't do a disassembly if(info.addressList.size() == 0) @@ -1081,8 +1124,9 @@ string CartDebug::saveDisassembly() if(bankCount == 1) buf << " ORG $" << Base::HEX4 << info.offset << "\n\n"; else - buf << " ORG $" << Base::HEX4 << ((0x0000 + bank * 0x1000) & 0xffff) << "\n" + buf << " ORG $" << Base::HEX4 << origin << "\n" << " RORG $" << Base::HEX4 << info.offset << "\n\n"; + origin += info.size; // Format in 'distella' style for(uInt32 i = 0; i < disasm.list.size(); ++i) diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx index c6ba615b5..327450560 100644 --- a/src/debugger/CartDebug.hxx +++ b/src/debugger/CartDebug.hxx @@ -94,23 +94,16 @@ class CartDebug : public DebuggerSystem // Return the base (= non-mirrored) address of the last CPU write int lastWriteBaseAddress(); - // The following two methods are meant to be used together - // First, a call is made to disassemble(), which updates the disassembly - // list; it will figure out when an actual complete disassembly is - // required, and when the previous results can be used - // - // Later, successive calls to disassemblyList() simply return the - // previous results; no disassembly is done in this case - /** - Disassemble from the given address using the Distella disassembler - Address-to-label mappings (and vice-versa) are also determined here - - @param force Force a re-disassembly, even if the state hasn't changed - - @return True if disassembly changed from previous call, else false - */ + // TODO bool disassemble(bool force = false); + bool disassembleBank(int bank); + // First, a call is made to disassemble(), which updates the disassembly + // list, is required; it will figure out when an actual complete + // disassembly is required, and when the previous results can be used + // + // Later, successive calls to disassembly() simply return the + // previous results; no disassembly is done in this case /** Get the results from the most recent call to disassemble() */ @@ -278,6 +271,19 @@ class CartDebug : public DebuggerSystem }; ReservedEquates myReserved; + /** + Disassemble from the given address using the Distella disassembler + Address-to-label mappings (and vice-versa) are also determined here + + @param bank The current bank to disassemble + @param PC A program counter to start with + @param force Force a re-disassembly, even if the state hasn't changed + + @return True if disassembly changed from previous call, else false + */ + bool disassemble(int bank, uInt16 PC, bool force = false); + + // Actually call DiStella to fill the DisassemblyList structure // Return whether the search address was actually in the list bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search); diff --git a/src/debugger/DiStella.cxx b/src/debugger/DiStella.cxx index 2aa304ac6..8aff0b568 100644 --- a/src/debugger/DiStella.cxx +++ b/src/debugger/DiStella.cxx @@ -969,9 +969,9 @@ bool DiStella::checkBit(uInt16 address, uInt16 mask, bool useDebugger) const // Since they're set only in the labels array (as the lower two bits), // they must be included in the other bitfields uInt16 label = myLabels[address & myAppData.end], - lastbits = label & 0x03, - directive = myDirectives[address & myAppData.end] & ~0x03, - debugger = Debugger::debugger().getAccessFlags(address | myOffset) & ~0x03; + lastbits = label & (Device::REFERENCED | Device::VALID_ENTRY), + directive = myDirectives[address & myAppData.end] & ~(Device::REFERENCED | Device::VALID_ENTRY), + debugger = Debugger::debugger().getAccessFlags(address | myOffset) & ~(Device::REFERENCED | Device::VALID_ENTRY); // Any address marked by a manual directive always takes priority if (directive) diff --git a/src/emucore/Device.hxx b/src/emucore/Device.hxx index d8b9cbaa9..ecb857e21 100644 --- a/src/emucore/Device.hxx +++ b/src/emucore/Device.hxx @@ -44,18 +44,20 @@ class Device : public Serializable // The following correspond to specific types that can be set within the // debugger, or specified in a Distella cfg file, and are listed in order - // of decreasing hierarchy + // of increasing hierarchy // - CODE = 1 << 11, // 0x800, disassemble-able code segments - TCODE = 1 << 10, // 0x400, (tentative) disassemble-able code segments - GFX = 1 << 9, // 0x200, addresses loaded into GRPx registers - PGFX = 1 << 8, // 0x100, addresses loaded into PFx registers - COL = 1 << 7, // 0x080, addresses loaded into COLUPx registers - PCOL = 1 << 6, // 0x040, addresses loaded into COLUPF register - BCOL = 1 << 5, // 0x020, addresses loaded into COLUBK register - AUD = 1 << 4, // 0x010, addresses loaded into audio registers - DATA = 1 << 3, // 0x008, addresses loaded into registers other than GRPx / PFx / COLUxx, AUDxx ROW = 1 << 2, // 0x004, all other addresses + DATA = 1 << 3, // 0x008, addresses loaded into registers other than GRPx / PFx / COLUxx, AUDxx + AUD = 1 << 4, // 0x010, addresses loaded into audio registers + BCOL = 1 << 5, // 0x020, addresses loaded into COLUBK register + PCOL = 1 << 6, // 0x040, addresses loaded into COLUPF register + COL = 1 << 7, // 0x080, addresses loaded into COLUPx registers + PGFX = 1 << 8, // 0x100, addresses loaded into PFx registers + GFX = 1 << 9, // 0x200, addresses loaded into GRPx registers + TCODE = 1 << 10, // 0x400, (tentative) disassemble-able code segments + CODE = 1 << 11, // 0x800, disassemble-able code segments + // special bits for address + HADDR = 1 << 13 | 1 << 14 | 1 << 15, // 0xe000, // highest 3 address bits // special type for poke() WRITE = TCODE // 0x200, address written to }; diff --git a/src/emucore/System.cxx b/src/emucore/System.cxx index f11a05fe4..c94f465fd 100644 --- a/src/emucore/System.cxx +++ b/src/emucore/System.cxx @@ -106,7 +106,7 @@ uInt8 System::peek(uInt16 addr, Device::AccessFlags flags) #ifdef DEBUGGER_SUPPORT // Set access type if(access.romAccessBase) - *(access.romAccessBase + (addr & PAGE_MASK)) |= flags; + *(access.romAccessBase + (addr & PAGE_MASK)) |= (flags | (addr & Device::HADDR)); else access.device->setAccessFlags(addr, flags); #endif @@ -134,8 +134,8 @@ void System::poke(uInt16 addr, uInt8 value, Device::AccessFlags flags) #ifdef DEBUGGER_SUPPORT // Set access type - if (access.romAccessBase) - *(access.romAccessBase + (addr & PAGE_MASK)) |= flags; + if(access.romAccessBase) + *(access.romAccessBase + (addr & PAGE_MASK)) |= (flags | (addr & Device::HADDR)); else access.device->setAccessFlags(addr, flags); #endif @@ -177,7 +177,7 @@ void System::setAccessFlags(uInt16 addr, Device::AccessFlags flags) const PageAccess& access = getPageAccess(addr); if(access.romAccessBase) - *(access.romAccessBase + (addr & PAGE_MASK)) |= flags; + *(access.romAccessBase + (addr & PAGE_MASK)) |= (flags | (addr & Device::HADDR)); else access.device->setAccessFlags(addr, flags); }