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-2021 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 #include "OSystem.hxx"
19 #include "Settings.hxx"
20 #include "Debugger.hxx"
21 #include "CartDebug.hxx"
22 #include "DiStella.hxx"
23 #include "CpuDebug.hxx"
24 #include "GuiObject.hxx"
25 #include "Font.hxx"
26 #include "DataGridWidget.hxx"
27 #include "EditTextWidget.hxx"
28 #include "PopUpWidget.hxx"
29 #include "ContextMenu.hxx"
30 #include "RomListWidget.hxx"
31 #include "RomWidget.hxx"
32
33 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RomWidget(GuiObject * boss,const GUI::Font & lfont,const GUI::Font & nfont,int x,int y,int w,int h)34 RomWidget::RomWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
35 int x, int y, int w, int h)
36 : Widget(boss, lfont, x, y, w, h),
37 CommandSender(boss)
38 {
39 int xpos, ypos;
40 StaticTextWidget* t;
41 WidgetArray wid;
42
43 // Show current bank state
44 xpos = x; ypos = y + 7;
45 t = new StaticTextWidget(boss, lfont, xpos, ypos, "Info ");
46
47 xpos += t->getRight();
48 myBank = new EditTextWidget(boss, nfont, xpos, ypos-2,
49 _w - 2 - xpos, nfont.getLineHeight());
50 myBank->setEditable(false);
51
52 // Create rom listing
53 xpos = x; ypos += myBank->getHeight() + 4;
54
55 myRomList = new RomListWidget(boss, lfont, nfont, xpos, ypos, _w - 4, _h - ypos - 2);
56 myRomList->setTarget(this);
57 addFocusWidget(myRomList);
58 }
59
60 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadConfig()61 void RomWidget::loadConfig()
62 {
63 Debugger& dbg = instance().debugger();
64 CartDebug& cart = dbg.cartDebug();
65 const CartState& state = static_cast<const CartState&>(cart.getState());
66 const CartState& oldstate = static_cast<const CartState&>(cart.getOldState());
67
68 // Fill romlist the current bank of source or disassembly
69 myListIsDirty |= cart.disassemblePC(myListIsDirty);
70 if(myListIsDirty)
71 {
72 myRomList->setList(cart.disassembly());
73 myListIsDirty = false;
74 }
75
76 // Update romlist to point to current PC (if it has changed)
77 int pcline = cart.addressToLine(dbg.cpuDebug().pc());
78
79 if(pcline >= 0 && pcline != myRomList->getHighlighted())
80 myRomList->setHighlighted(pcline);
81
82 // Set current bank state
83 myBank->setText(state.bank, state.bank != oldstate.bank);
84 }
85
86 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
handleCommand(CommandSender * sender,int cmd,int data,int id)87 void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
88 {
89 switch(cmd)
90 {
91 case RomListWidget::kBPointChangedCmd:
92 // 'data' is the line in the disassemblylist to be accessed
93 toggleBreak(data);
94 break;
95
96 case RomListWidget::kRomChangedCmd:
97 // 'data' is the line in the disassemblylist to be accessed
98 // 'id' is the base to use for the data to be changed
99 patchROM(data, myRomList->getText(), Common::Base::Fmt(id));
100 break;
101
102 case RomListWidget::kSetPCCmd:
103 // 'data' is the line in the disassemblylist to be accessed
104 setPC(data);
105 break;
106
107 case RomListWidget::kRuntoPCCmd:
108 // 'data' is the line in the disassemblylist to be accessed
109 runtoPC(data);
110 break;
111
112 case RomListWidget::kDisassembleCmd:
113 // 'data' is the line in the disassemblylist to be accessed
114 disassemble(data);
115 break;
116
117 case RomListWidget::kTentativeCodeCmd:
118 {
119 // 'data' is the boolean value
120 DiStella::settings.resolveCode = data;
121 instance().settings().setValue("dis.resolve",
122 DiStella::settings.resolveCode);
123 invalidate();
124 break;
125 }
126
127 case RomListWidget::kPCAddressesCmd:
128 // 'data' is the boolean value
129 DiStella::settings.showAddresses = data;
130 instance().settings().setValue("dis.showaddr",
131 DiStella::settings.showAddresses);
132 invalidate();
133 break;
134
135 case RomListWidget::kGfxAsBinaryCmd:
136 // 'data' is the boolean value
137 if(data)
138 {
139 DiStella::settings.gfxFormat = Common::Base::Fmt::_2;
140 instance().settings().setValue("dis.gfxformat", "2");
141 }
142 else
143 {
144 DiStella::settings.gfxFormat = Common::Base::Fmt::_16;
145 instance().settings().setValue("dis.gfxformat", "16");
146 }
147 invalidate();
148 break;
149
150 case RomListWidget::kAddrRelocationCmd:
151 // 'data' is the boolean value
152 DiStella::settings.rFlag = data;
153 instance().settings().setValue("dis.relocate",
154 DiStella::settings.rFlag);
155 invalidate();
156 break;
157
158 default:
159 break;
160 }
161 }
162
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
toggleBreak(int disasm_line)164 void RomWidget::toggleBreak(int disasm_line)
165 {
166 const uInt16 address = getAddress(disasm_line);
167
168 if (address != 0)
169 {
170 Debugger& debugger = instance().debugger();
171
172 debugger.toggleBreakPoint(address, debugger.cartDebug().getBank(address));
173 }
174 }
175
176 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setPC(int disasm_line)177 void RomWidget::setPC(int disasm_line)
178 {
179 const uInt16 address = getAddress(disasm_line);
180
181 if(address != 0)
182 {
183 ostringstream command;
184 command << "pc #" << address;
185 instance().debugger().run(command.str());
186 }
187 }
188
189 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
runtoPC(int disasm_line)190 void RomWidget::runtoPC(int disasm_line)
191 {
192 const uInt16 address = getAddress(disasm_line);
193
194 if(address != 0)
195 {
196 ostringstream command;
197 command << "runtopc #" << address;
198 string msg = instance().debugger().run(command.str());
199 instance().frameBuffer().showTextMessage(msg);
200 }
201 }
202
203 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
disassemble(int disasm_line)204 void RomWidget::disassemble(int disasm_line)
205 {
206 const uInt16 address = getAddress(disasm_line);
207
208 if(address != 0)
209 {
210 CartDebug& cart = instance().debugger().cartDebug();
211
212 cart.disassembleAddr(address, true);
213 invalidate();
214 scrollTo(cart.addressToLine(address)); // the line might have been changed
215 }
216 }
217
218 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
patchROM(int disasm_line,const string & bytes,Common::Base::Fmt base)219 void RomWidget::patchROM(int disasm_line, const string& bytes,
220 Common::Base::Fmt base)
221 {
222 const uInt16 address = getAddress(disasm_line);
223
224 if(address != 0)
225 {
226 ostringstream command;
227
228 // Temporarily set to correct base, so we don't have to prefix each byte
229 // with the type of data
230 Common::Base::Fmt oldbase = Common::Base::format();
231
232 Common::Base::setFormat(base);
233 command << "rom #" << address << " " << bytes;
234 instance().debugger().run(command.str());
235
236 // Restore previous base
237 Common::Base::setFormat(oldbase);
238 }
239 }
240
241 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getAddress(int disasm_line)242 uInt16 RomWidget::getAddress(int disasm_line)
243 {
244 const CartDebug::DisassemblyList& list =
245 instance().debugger().cartDebug().disassembly().list;
246
247 if (disasm_line < int(list.size()) && list[disasm_line].address != 0)
248 return list[disasm_line].address;
249 else
250 return 0;
251 }
252
253 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
scrollTo(int line)254 void RomWidget::scrollTo(int line)
255 {
256 myRomList->setSelected(line);
257 }
258