| 1 | //============================================================================ |
| 2 | // |
| 3 | // SSSS tt lll lll |
| 4 | // SS SS tt ll ll |
| 5 | // SS tttttt eeee ll ll aaaa |
| 6 | // SSSS tt ee ee ll ll aa |
| 7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
| 8 | // SS SS tt ee ll ll aa aa |
| 9 | // SSSS ttt eeeee llll llll aaaaa |
| 10 | // |
| 11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
| 12 | // and the Stella Team |
| 13 | // |
| 14 | // See the file "License.txt" for information on usage and redistribution of |
| 15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
| 16 | //============================================================================ |
| 17 | |
| 18 | #ifndef CART_DEBUG_HXX |
| 19 | #define CART_DEBUG_HXX |
| 20 | |
| 21 | class Settings; |
| 22 | class CartDebugWidget; |
| 23 | |
| 24 | // Function type for CartDebug instance methods |
| 25 | class CartDebug; |
| 26 | using CartMethod = int (CartDebug::*)(); |
| 27 | |
| 28 | #include <map> |
| 29 | #include <set> |
| 30 | #include <list> |
| 31 | |
| 32 | #include "bspf.hxx" |
| 33 | #include "DebuggerSystem.hxx" |
| 34 | |
| 35 | class CartState : public DebuggerState |
| 36 | { |
| 37 | public: |
| 38 | ByteArray ram; // The actual data values |
| 39 | ShortArray rport; // Address for reading from RAM |
| 40 | ShortArray wport; // Address for writing to RAM |
| 41 | string bank; // Current banking layout |
| 42 | }; |
| 43 | |
| 44 | class CartDebug : public DebuggerSystem |
| 45 | { |
| 46 | // The disassembler needs special access to this class |
| 47 | friend class DiStella; |
| 48 | |
| 49 | public: |
| 50 | enum DisasmType { |
| 51 | NONE = 0, |
| 52 | REFERENCED = 1 << 0, /* 0x01, code somewhere in the program references it, |
| 53 | i.e. LDA $F372 referenced $F372 */ |
| 54 | VALID_ENTRY = 1 << 1, /* 0x02, addresses that can have a label placed in front of it. |
| 55 | A good counterexample would be "FF00: LDA $FE00"; $FF01 |
| 56 | would be in the middle of a multi-byte instruction, and |
| 57 | therefore cannot be labelled. */ |
| 58 | |
| 59 | // The following correspond to specific types that can be set within the |
| 60 | // debugger, or specified in a Distella cfg file, and are listed in order |
| 61 | // of decreasing hierarchy |
| 62 | // |
| 63 | CODE = 1 << 7, // 0x80, disassemble-able code segments |
| 64 | TCODE = 1 << 6, // 0x40, (tentative) disassemble-able code segments |
| 65 | GFX = 1 << 5, // 0x20, addresses loaded into GRPx registers |
| 66 | PGFX = 1 << 4, // 0x10, addresses loaded into PFx registers |
| 67 | DATA = 1 << 3, // 0x08, addresses loaded into registers other than GRPx / PFx |
| 68 | ROW = 1 << 2, // 0x04, all other addresses |
| 69 | // special type for poke() |
| 70 | WRITE = TCODE // 0x40, address written to |
| 71 | }; |
| 72 | struct DisassemblyTag { |
| 73 | DisasmType type; |
| 74 | uInt16 address; |
| 75 | string label; |
| 76 | string disasm; |
| 77 | string ccount; |
| 78 | string ctotal; |
| 79 | string bytes; |
| 80 | bool hllabel; |
| 81 | }; |
| 82 | using DisassemblyList = vector<DisassemblyTag>; |
| 83 | struct Disassembly { |
| 84 | DisassemblyList list; |
| 85 | int fieldwidth; |
| 86 | }; |
| 87 | |
| 88 | // Determine 'type' of address (ie, what part of the system accessed) |
| 89 | enum class AddrType { TIA, IO, ZPRAM, ROM }; |
| 90 | AddrType addressType(uInt16 addr) const; |
| 91 | |
| 92 | public: |
| 93 | CartDebug(Debugger& dbg, Console& console, const OSystem& osystem); |
| 94 | virtual ~CartDebug() = default; |
| 95 | |
| 96 | const DebuggerState& getState() override; |
| 97 | const DebuggerState& getOldState() override { return myOldState; } |
| 98 | |
| 99 | void saveOldState() override; |
| 100 | string toString() override; |
| 101 | |
| 102 | // Used to get/set the debug widget, which contains cart-specific |
| 103 | // functionality |
| 104 | CartDebugWidget* getDebugWidget() const { return myDebugWidget; } |
| 105 | void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; } |
| 106 | |
| 107 | // Return the base (= non-mirrored) address of the last CPU read |
| 108 | int lastReadBaseAddress(); |
| 109 | // Return the base (= non-mirrored) address of the last CPU write |
| 110 | int lastWriteBaseAddress(); |
| 111 | |
| 112 | // The following two methods are meant to be used together |
| 113 | // First, a call is made to disassemble(), which updates the disassembly |
| 114 | // list; it will figure out when an actual complete disassembly is |
| 115 | // required, and when the previous results can be used |
| 116 | // |
| 117 | // Later, successive calls to disassemblyList() simply return the |
| 118 | // previous results; no disassembly is done in this case |
| 119 | /** |
| 120 | Disassemble from the given address using the Distella disassembler |
| 121 | Address-to-label mappings (and vice-versa) are also determined here |
| 122 | |
| 123 | @param force Force a re-disassembly, even if the state hasn't changed |
| 124 | |
| 125 | @return True if disassembly changed from previous call, else false |
| 126 | */ |
| 127 | bool disassemble(bool force = false); |
| 128 | |
| 129 | /** |
| 130 | Get the results from the most recent call to disassemble() |
| 131 | */ |
| 132 | const Disassembly& disassembly() const { return myDisassembly; } |
| 133 | |
| 134 | /** |
| 135 | Determine the line in the disassembly that corresponds to the given address. |
| 136 | |
| 137 | @param address The address to search for |
| 138 | |
| 139 | @return Line number of the address, else -1 if no such address exists |
| 140 | */ |
| 141 | int addressToLine(uInt16 address) const; |
| 142 | |
| 143 | /** |
| 144 | Disassemble from the starting address the specified number of lines. |
| 145 | Note that automatic code determination is turned off for this method; |
| 146 | |
| 147 | @param start The start address for disassembly |
| 148 | @param lines The number of disassembled lines to generate |
| 149 | |
| 150 | @return The disassembly represented as a string |
| 151 | */ |
| 152 | string disassemble(uInt16 start, uInt16 lines) const; |
| 153 | |
| 154 | /** |
| 155 | Add a directive to the disassembler. Directives are basically overrides |
| 156 | for the automatic code determination algorithm in Distella, since some |
| 157 | things can't be automatically determined. For now, these directives |
| 158 | have exactly the same syntax as in a distella configuration file. |
| 159 | |
| 160 | @param type Currently, CODE/DATA/GFX are supported |
| 161 | @param start The start address (inclusive) to mark with the given type |
| 162 | @param end The end address (inclusive) to mark with the given type |
| 163 | @param bank Bank to which these directive apply (0 indicated current bank) |
| 164 | |
| 165 | @return True if directive was added, else false if it was removed |
| 166 | */ |
| 167 | bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end, |
| 168 | int bank = -1); |
| 169 | |
| 170 | // The following are convenience methods that query the cartridge object |
| 171 | // for the desired information. |
| 172 | /** |
| 173 | Get the current bank in use by the cartridge |
| 174 | (non-const because of use in YaccParser) |
| 175 | */ |
| 176 | int getBank(uInt16 addr); |
| 177 | |
| 178 | |
| 179 | int getPCBank(); |
| 180 | |
| 181 | /** |
| 182 | Get the total number of banks supported by the cartridge. |
| 183 | */ |
| 184 | int bankCount() const; |
| 185 | |
| 186 | /** |
| 187 | Add a label and associated address. |
| 188 | Labels that reference either TIA or RIOT spaces will not be processed. |
| 189 | */ |
| 190 | bool addLabel(const string& label, uInt16 address); |
| 191 | |
| 192 | /** |
| 193 | Remove the given label and its associated address. |
| 194 | Labels that reference either TIA or RIOT spaces will not be processed. |
| 195 | */ |
| 196 | bool removeLabel(const string& label); |
| 197 | |
| 198 | /** |
| 199 | Accessor methods for labels and addresses |
| 200 | |
| 201 | The mapping from address to label can be one-to-many (ie, an |
| 202 | address can have different labels depending on its context, and |
| 203 | whether its being read or written; if isRead is true, the context |
| 204 | is a read, else it's a write |
| 205 | If places is not -1 and a label hasn't been defined, return a |
| 206 | formatted hexidecimal address |
| 207 | */ |
| 208 | bool getLabel(ostream& buf, uInt16 addr, bool isRead, int places = -1) const; |
| 209 | string getLabel(uInt16 addr, bool isRead, int places = -1) const; |
| 210 | int getAddress(const string& label) const; |
| 211 | |
| 212 | /** |
| 213 | Load constants from list file (as generated by DASM). |
| 214 | */ |
| 215 | string loadListFile(); |
| 216 | |
| 217 | /** |
| 218 | Load user equates from symbol file (as generated by DASM). |
| 219 | */ |
| 220 | string loadSymbolFile(); |
| 221 | |
| 222 | /** |
| 223 | Load/save Distella config files (Distella directives) |
| 224 | */ |
| 225 | string loadConfigFile(); |
| 226 | string saveConfigFile(); |
| 227 | |
| 228 | /** |
| 229 | Save disassembly and ROM file |
| 230 | */ |
| 231 | string saveDisassembly(); |
| 232 | string saveRom(); |
| 233 | |
| 234 | /** |
| 235 | Show Distella directives (both set by the user and determined by Distella) |
| 236 | for the given bank (or all banks, if no bank is specified). |
| 237 | */ |
| 238 | string listConfig(int bank = -1); |
| 239 | |
| 240 | /** |
| 241 | Clear Distella directives (set by the user) for the given bank |
| 242 | (or all banks, if no bank is specified.) |
| 243 | */ |
| 244 | string clearConfig(int bank = -1); |
| 245 | |
| 246 | /** |
| 247 | Methods used by the command parser for tab-completion |
| 248 | In this case, return completions from the equate list(s) |
| 249 | */ |
| 250 | void getCompletions(const char* in, StringList& list) const; |
| 251 | |
| 252 | // Convert given address to corresponding disassembly type and append to buf |
| 253 | void addressTypeAsString(ostream& buf, uInt16 addr) const; |
| 254 | |
| 255 | private: |
| 256 | using AddrToLabel = std::map<uInt16, string>; |
| 257 | using LabelToAddr = std::map<string, uInt16, |
| 258 | std::function<bool(const string&, const string&)>>; |
| 259 | |
| 260 | using AddrTypeArray = std::array<uInt8, 0x1000>; |
| 261 | |
| 262 | struct DirectiveTag { |
| 263 | DisasmType type; |
| 264 | uInt16 start; |
| 265 | uInt16 end; |
| 266 | }; |
| 267 | using AddressList = std::list<uInt16>; |
| 268 | using DirectiveList = std::list<DirectiveTag>; |
| 269 | |
| 270 | struct BankInfo { |
| 271 | uInt16 start; // start of address space |
| 272 | uInt16 end; // end of address space |
| 273 | uInt16 offset; // ORG value |
| 274 | size_t size; // size of a bank (in bytes) |
| 275 | AddressList addressList; // addresses which PC has hit |
| 276 | DirectiveList directiveList; // overrides for automatic code determination |
| 277 | |
| 278 | BankInfo() : start(0), end(0), offset(0), size(0) { } |
| 279 | }; |
| 280 | |
| 281 | // Address type information determined by Distella |
| 282 | AddrTypeArray myDisLabels, myDisDirectives; |
| 283 | |
| 284 | // Information on equates used in the disassembly |
| 285 | struct ReservedEquates { |
| 286 | std::array<bool, 16> TIARead; |
| 287 | std::array<bool, 64> TIAWrite; |
| 288 | std::array<bool, 24> IOReadWrite; |
| 289 | std::array<bool, 128> ZPRAM; |
| 290 | AddrToLabel Label; |
| 291 | bool breakFound; |
| 292 | }; |
| 293 | ReservedEquates myReserved; |
| 294 | |
| 295 | // Actually call DiStella to fill the DisassemblyList structure |
| 296 | // Return whether the search address was actually in the list |
| 297 | bool fillDisassemblyList(BankInfo& bankinfo, uInt16 search); |
| 298 | |
| 299 | // Analyze of bank of ROM, generating a list of Distella directives |
| 300 | // based on its disassembly |
| 301 | void getBankDirectives(ostream& buf, BankInfo& info) const; |
| 302 | |
| 303 | // Get disassembly enum type from 'flags', taking precendence into account |
| 304 | DisasmType disasmTypeAbsolute(uInt8 flags) const; |
| 305 | |
| 306 | // Convert disassembly enum type to corresponding string and append to buf |
| 307 | void disasmTypeAsString(ostream& buf, DisasmType type) const; |
| 308 | |
| 309 | // Convert all disassembly types in 'flags' to corresponding string and |
| 310 | // append to buf |
| 311 | void disasmTypeAsString(ostream& buf, uInt8 flags) const; |
| 312 | |
| 313 | private: |
| 314 | const OSystem& myOSystem; |
| 315 | |
| 316 | CartState myState; |
| 317 | CartState myOldState; |
| 318 | |
| 319 | CartDebugWidget* myDebugWidget; |
| 320 | |
| 321 | // A complete record of relevant diassembly information for each bank |
| 322 | vector<BankInfo> myBankInfo; |
| 323 | |
| 324 | // Used for the disassembly display, and mapping from addresses |
| 325 | // to corresponding lines of text in that display |
| 326 | Disassembly myDisassembly; |
| 327 | std::map<uInt16, int> myAddrToLineList; |
| 328 | bool myAddrToLineIsROM; |
| 329 | |
| 330 | // Mappings from label to address (and vice versa) for items |
| 331 | // defined by the user (either through a DASM symbol file or manually |
| 332 | // from the commandline in the debugger) |
| 333 | AddrToLabel myUserLabels; |
| 334 | LabelToAddr myUserAddresses; |
| 335 | |
| 336 | // Mappings from label to address (and vice versa) for constants |
| 337 | // defined through a DASM lst file |
| 338 | // AddrToLabel myUserCLabels; |
| 339 | // LabelToAddr myUserCAddresses; |
| 340 | |
| 341 | // Mappings for labels to addresses for system-defined equates |
| 342 | // Because system equate addresses can have different names |
| 343 | // (depending on access in read vs. write mode), we can only create |
| 344 | // a mapping from labels to addresses; addresses to labels are |
| 345 | // handled differently |
| 346 | LabelToAddr mySystemAddresses; |
| 347 | |
| 348 | // The maximum length of all labels currently defined |
| 349 | uInt16 myLabelLength; |
| 350 | |
| 351 | // Filenames to use for various I/O (currently these are hardcoded) |
| 352 | string myListFile, mySymbolFile, myCfgFile, myDisasmFile, myRomFile; |
| 353 | |
| 354 | /// Table of instruction mnemonics |
| 355 | static const char* const ourTIAMnemonicR[16]; // read mode |
| 356 | static const char* const ourTIAMnemonicW[64]; // write mode |
| 357 | static const char* const ourIOMnemonic[24]; |
| 358 | static const char* const ourZPMnemonic[128]; |
| 359 | |
| 360 | private: |
| 361 | // Following constructors and assignment operators not supported |
| 362 | CartDebug() = delete; |
| 363 | CartDebug(const CartDebug&) = delete; |
| 364 | CartDebug(CartDebug&&) = delete; |
| 365 | CartDebug& operator=(const CartDebug&) = delete; |
| 366 | CartDebug& operator=(CartDebug&&) = delete; |
| 367 | }; |
| 368 | |
| 369 | #endif |
| 370 | |