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
21class Settings;
22class CartDebugWidget;
23
24// Function type for CartDebug instance methods
25class CartDebug;
26using CartMethod = int (CartDebug::*)();
27
28#include <map>
29#include <set>
30#include <list>
31
32#include "bspf.hxx"
33#include "DebuggerSystem.hxx"
34
35class 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
44class 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