mirror of https://github.com/stella-emu/stella.git
364 lines
12 KiB
C++
364 lines
12 KiB
C++
//============================================================================
|
|
//
|
|
// 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-2020 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.
|
|
//============================================================================
|
|
|
|
#ifndef CART_DEBUG_HXX
|
|
#define CART_DEBUG_HXX
|
|
|
|
class Settings;
|
|
class CartDebugWidget;
|
|
|
|
// Function type for CartDebug instance methods
|
|
class CartDebug;
|
|
using CartMethod = int (CartDebug::*)();
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <list>
|
|
|
|
#include "bspf.hxx"
|
|
#include "DebuggerSystem.hxx"
|
|
#include "Device.hxx"
|
|
|
|
class CartState : public DebuggerState
|
|
{
|
|
public:
|
|
ByteArray ram; // The actual data values
|
|
ShortArray rport; // Address for reading from RAM
|
|
ShortArray wport; // Address for writing to RAM
|
|
string bank; // Current banking layout
|
|
};
|
|
|
|
class CartDebug : public DebuggerSystem
|
|
{
|
|
// The disassembler needs special access to this class
|
|
friend class DiStella;
|
|
|
|
public:
|
|
struct DisassemblyTag {
|
|
Device::AccessType type{Device::NONE};
|
|
uInt16 address{0};
|
|
string label;
|
|
string disasm;
|
|
string ccount;
|
|
string ctotal;
|
|
string bytes;
|
|
bool hllabel{false};
|
|
};
|
|
using DisassemblyList = vector<DisassemblyTag>;
|
|
struct Disassembly {
|
|
DisassemblyList list;
|
|
int fieldwidth{0};
|
|
};
|
|
|
|
// Determine 'type' of address (ie, what part of the system accessed)
|
|
enum class AddrType { TIA, IO, ZPRAM, ROM };
|
|
AddrType addressType(uInt16 addr) const;
|
|
|
|
public:
|
|
CartDebug(Debugger& dbg, Console& console, const OSystem& osystem);
|
|
virtual ~CartDebug() = default;
|
|
|
|
const DebuggerState& getState() override;
|
|
const DebuggerState& getOldState() override { return myOldState; }
|
|
|
|
void saveOldState() override;
|
|
string toString() override;
|
|
|
|
// Used to get/set the debug widget, which contains cart-specific
|
|
// functionality
|
|
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
|
|
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; }
|
|
|
|
|
|
// Return the address of the last CPU read
|
|
int lastReadAddress();
|
|
// Return the address of the last CPU write
|
|
int lastWriteAddress();
|
|
|
|
// Return the base (= non-mirrored) address of the last CPU read
|
|
int lastReadBaseAddress();
|
|
// Return the base (= non-mirrored) address of the last CPU write
|
|
int lastWriteBaseAddress();
|
|
|
|
// 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()
|
|
*/
|
|
const Disassembly& disassembly() const { return myDisassembly; }
|
|
|
|
/**
|
|
Determine the line in the disassembly that corresponds to the given address.
|
|
|
|
@param address The address to search for
|
|
|
|
@return Line number of the address, else -1 if no such address exists
|
|
*/
|
|
int addressToLine(uInt16 address) const;
|
|
|
|
/**
|
|
Disassemble from the starting address the specified number of lines.
|
|
Note that automatic code determination is turned off for this method;
|
|
|
|
@param start The start address for disassembly
|
|
@param lines The number of disassembled lines to generate
|
|
|
|
@return The disassembly represented as a string
|
|
*/
|
|
string disassembleLines(uInt16 start, uInt16 lines) const;
|
|
|
|
/**
|
|
Add a directive to the disassembler. Directives are basically overrides
|
|
for the automatic code determination algorithm in Distella, since some
|
|
things can't be automatically determined. For now, these directives
|
|
have exactly the same syntax as in a distella configuration file.
|
|
|
|
@param type Currently, CODE/DATA/GFX/PGFX/COL/PCOL/BCOL/AUD/ROW are supported
|
|
@param start The start address (inclusive) to mark with the given type
|
|
@param end The end address (inclusive) to mark with the given type
|
|
@param bank Bank to which these directive apply (0 indicated current bank)
|
|
|
|
@return True if directive was added, else false if it was removed
|
|
*/
|
|
bool addDirective(Device::AccessType type, uInt16 start, uInt16 end,
|
|
int bank = -1);
|
|
|
|
// The following are convenience methods that query the cartridge object
|
|
// for the desired information.
|
|
/**
|
|
Get the current bank in use by the cartridge
|
|
(non-const because of use in YaccParser)
|
|
*/
|
|
int getBank(uInt16 addr);
|
|
|
|
|
|
int getPCBank();
|
|
|
|
/**
|
|
Get the total number of banks supported by the cartridge.
|
|
*/
|
|
int bankCount() const;
|
|
|
|
/**
|
|
Add a label and associated address.
|
|
Labels that reference either TIA or RIOT spaces will not be processed.
|
|
*/
|
|
bool addLabel(const string& label, uInt16 address);
|
|
|
|
/**
|
|
Remove the given label and its associated address.
|
|
Labels that reference either TIA or RIOT spaces will not be processed.
|
|
*/
|
|
bool removeLabel(const string& label);
|
|
|
|
/**
|
|
Accessor methods for labels and addresses
|
|
|
|
The mapping from address to label can be one-to-many (ie, an
|
|
address can have different labels depending on its context, and
|
|
whether its being read or written; if isRead is true, the context
|
|
is a read, else it's a write
|
|
If places is not -1 and a label hasn't been defined, return a
|
|
formatted hexidecimal address
|
|
*/
|
|
bool getLabel(ostream& buf, uInt16 addr, bool isRead, int places = -1) const;
|
|
string getLabel(uInt16 addr, bool isRead, int places = -1) const;
|
|
int getAddress(const string& label) const;
|
|
|
|
/**
|
|
Load constants from list file (as generated by DASM).
|
|
*/
|
|
string loadListFile();
|
|
|
|
/**
|
|
Load user equates from symbol file (as generated by DASM).
|
|
*/
|
|
string loadSymbolFile();
|
|
|
|
/**
|
|
Load/save Distella config files (Distella directives)
|
|
*/
|
|
string loadConfigFile();
|
|
string saveConfigFile();
|
|
|
|
/**
|
|
Save disassembly and ROM file
|
|
*/
|
|
string saveDisassembly();
|
|
string saveRom();
|
|
|
|
/**
|
|
Save access counters file
|
|
*/
|
|
string saveAccessFile();
|
|
|
|
/**
|
|
Show Distella directives (both set by the user and determined by Distella)
|
|
for the given bank (or all banks, if no bank is specified).
|
|
*/
|
|
string listConfig(int bank = -1);
|
|
|
|
/**
|
|
Clear Distella directives (set by the user) for the given bank
|
|
(or all banks, if no bank is specified.)
|
|
*/
|
|
string clearConfig(int bank = -1);
|
|
|
|
/**
|
|
Methods used by the command parser for tab-completion
|
|
In this case, return completions from the equate list(s)
|
|
*/
|
|
void getCompletions(const char* in, StringList& list) const;
|
|
|
|
// Convert given address to corresponding access type and append to buf
|
|
void accessTypeAsString(ostream& buf, uInt16 addr) const;
|
|
|
|
// Convert access enum type to corresponding string and append to buf
|
|
void AccessTypeAsString(ostream& buf, Device::AccessType type) const;
|
|
|
|
private:
|
|
using AddrToLabel = std::map<uInt16, string>;
|
|
using LabelToAddr = std::map<string, uInt16,
|
|
std::function<bool(const string&, const string&)>>;
|
|
|
|
using AddrTypeArray = std::array<uInt16, 0x1000>;
|
|
|
|
struct DirectiveTag {
|
|
Device::AccessType type{Device::NONE};
|
|
uInt16 start{0};
|
|
uInt16 end{0};
|
|
};
|
|
using AddressList = std::list<uInt16>;
|
|
using DirectiveList = std::list<DirectiveTag>;
|
|
|
|
struct BankInfo {
|
|
uInt16 start{0}; // start of address space
|
|
uInt16 end{0}; // end of address space
|
|
uInt16 offset{0}; // ORG value
|
|
size_t size{0}; // size of a bank (in bytes)
|
|
AddressList addressList; // addresses which PC has hit
|
|
DirectiveList directiveList; // overrides for automatic code determination
|
|
};
|
|
|
|
// Address type information determined by Distella
|
|
AddrTypeArray myDisLabels, myDisDirectives;
|
|
|
|
// Information on equates used in the disassembly
|
|
struct ReservedEquates {
|
|
std::array<bool, 16> TIARead;
|
|
std::array<bool, 64> TIAWrite;
|
|
std::array<bool, 24> IOReadWrite;
|
|
std::array<bool, 128> ZPRAM;
|
|
AddrToLabel Label;
|
|
bool breakFound{false};
|
|
};
|
|
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);
|
|
|
|
// Analyze of bank of ROM, generating a list of Distella directives
|
|
// based on its disassembly
|
|
void getBankDirectives(ostream& buf, BankInfo& info) const;
|
|
|
|
// Get access enum type from 'flags', taking precendence into account
|
|
Device::AccessType accessTypeAbsolute(Device::AccessFlags flags) const;
|
|
|
|
// Convert all access types in 'flags' to corresponding string and
|
|
// append to buf
|
|
void AccessTypeAsString(ostream& buf, Device::AccessFlags flags) const;
|
|
|
|
private:
|
|
const OSystem& myOSystem;
|
|
|
|
CartState myState;
|
|
CartState myOldState;
|
|
|
|
CartDebugWidget* myDebugWidget{nullptr};
|
|
|
|
// A complete record of relevant diassembly information for each bank
|
|
vector<BankInfo> myBankInfo;
|
|
|
|
// Used for the disassembly display, and mapping from addresses
|
|
// to corresponding lines of text in that display
|
|
Disassembly myDisassembly;
|
|
std::map<uInt16, int> myAddrToLineList;
|
|
bool myAddrToLineIsROM{true};
|
|
|
|
// Mappings from label to address (and vice versa) for items
|
|
// defined by the user (either through a DASM symbol file or manually
|
|
// from the commandline in the debugger)
|
|
AddrToLabel myUserLabels;
|
|
LabelToAddr myUserAddresses;
|
|
|
|
// Mappings from label to address (and vice versa) for constants
|
|
// defined through a DASM lst file
|
|
// AddrToLabel myUserCLabels;
|
|
// LabelToAddr myUserCAddresses;
|
|
|
|
// Mappings for labels to addresses for system-defined equates
|
|
// Because system equate addresses can have different names
|
|
// (depending on access in read vs. write mode), we can only create
|
|
// a mapping from labels to addresses; addresses to labels are
|
|
// handled differently
|
|
LabelToAddr mySystemAddresses;
|
|
|
|
// The maximum length of all labels currently defined
|
|
uInt16 myLabelLength{8}; // longest pre-defined label
|
|
|
|
// Filenames to use for various I/O (currently these are hardcoded)
|
|
string myListFile, mySymbolFile, myCfgFile, myDisasmFile;
|
|
|
|
/// Table of instruction mnemonics
|
|
static std::array<const char*, 16> ourTIAMnemonicR; // read mode
|
|
static std::array<const char*, 64> ourTIAMnemonicW; // write mode
|
|
static std::array<const char*, 24> ourIOMnemonic;
|
|
static std::array<const char*, 128> ourZPMnemonic;
|
|
|
|
private:
|
|
// Following constructors and assignment operators not supported
|
|
CartDebug() = delete;
|
|
CartDebug(const CartDebug&) = delete;
|
|
CartDebug(CartDebug&&) = delete;
|
|
CartDebug& operator=(const CartDebug&) = delete;
|
|
CartDebug& operator=(CartDebug&&) = delete;
|
|
};
|
|
|
|
#endif
|