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 | #include <sstream> |
19 | |
20 | #include "OSystem.hxx" |
21 | #include "GuiObject.hxx" |
22 | #include "Debugger.hxx" |
23 | #include "CartDebug.hxx" |
24 | #include "CpuDebug.hxx" |
25 | #include "Widget.hxx" |
26 | #include "Font.hxx" |
27 | #include "DataGridWidget.hxx" |
28 | #include "EditTextWidget.hxx" |
29 | #include "ToggleBitWidget.hxx" |
30 | |
31 | #include "CpuWidget.hxx" |
32 | |
33 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
34 | CpuWidget::CpuWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, |
35 | int x, int y, int max_w) |
36 | : Widget(boss, lfont, x, y, 16, 16), |
37 | CommandSender(boss) |
38 | { |
39 | const int fontWidth = lfont.getMaxCharWidth(), |
40 | fontHeight = lfont.getFontHeight(), |
41 | lineHeight = lfont.getLineHeight(); |
42 | int xpos, ypos, lwidth; |
43 | |
44 | // Create a 1x1 grid with label for the PC register |
45 | xpos = x; ypos = y; lwidth = 4 * fontWidth; |
46 | new StaticTextWidget(boss, lfont, xpos, ypos+1, lwidth-2, fontHeight, |
47 | "PC " , TextAlign::Left); |
48 | myPCGrid = |
49 | new DataGridWidget(boss, nfont, xpos + lwidth, ypos, 1, 1, 4, 16, Common::Base::F_16); |
50 | myPCGrid->setTarget(this); |
51 | myPCGrid->setID(kPCRegID); |
52 | addFocusWidget(myPCGrid); |
53 | |
54 | // Create a read-only textbox containing the current PC label |
55 | xpos += lwidth + myPCGrid->getWidth() + 10; |
56 | myPCLabel = new EditTextWidget(boss, nfont, xpos, ypos, (max_w - xpos + x) - 10, |
57 | fontHeight+1, "" ); |
58 | myPCLabel->setEditable(false, true); |
59 | |
60 | // Create a 1x4 grid with labels for the other CPU registers |
61 | xpos = x + lwidth; ypos += myPCGrid->getHeight() + 1; |
62 | myCpuGrid = |
63 | new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 2, 8, Common::Base::F_16); |
64 | myCpuGrid->setTarget(this); |
65 | myCpuGrid->setID(kCpuRegID); |
66 | addFocusWidget(myCpuGrid); |
67 | |
68 | // Create a 1x4 grid with decimal and binary values for the other CPU registers |
69 | xpos = x + lwidth + myPCGrid->getWidth() + 10; |
70 | myCpuGridDecValue = |
71 | new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 3, 8, Common::Base::F_10); |
72 | myCpuGridDecValue->setTarget(this); |
73 | myCpuGridDecValue->setID(kCpuRegDecID); |
74 | addFocusWidget(myCpuGridDecValue); |
75 | |
76 | xpos += myCpuGridDecValue->getWidth() + 5; |
77 | myCpuGridBinValue = |
78 | new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 8, 8, Common::Base::F_2); |
79 | myCpuGridBinValue->setTarget(this); |
80 | myCpuGridBinValue->setID(kCpuRegBinID); |
81 | addFocusWidget(myCpuGridBinValue); |
82 | |
83 | // Calculate real dimensions (_y will be calculated at the end) |
84 | _w = lwidth + myPCGrid->getWidth() + myPCLabel->getWidth() + 20; |
85 | |
86 | // Create labels showing the source of data for SP/A/X/Y registers |
87 | xpos += myCpuGridBinValue->getWidth() + 20; |
88 | int src_y = ypos, src_w = (max_w - xpos + x) - 10; |
89 | for(int i = 0; i < 4; ++i) |
90 | { |
91 | myCpuDataSrc[i] = new EditTextWidget(boss, nfont, xpos, src_y, src_w, |
92 | fontHeight+1, "" ); |
93 | myCpuDataSrc[i]->setEditable(false, true); |
94 | src_y += fontHeight+2; |
95 | } |
96 | int swidth = lfont.getStringWidth("Source Address" ); |
97 | new StaticTextWidget(boss, lfont, xpos, src_y + 4, src_w, |
98 | fontHeight, swidth <= src_w ? "Source Address" : "Source Addr" , |
99 | TextAlign::Center); |
100 | |
101 | // Add labels for other CPU registers |
102 | xpos = x; |
103 | string labels[4] = { "SP " , "A " , "X " , "Y " }; |
104 | for(int row = 0; row < 4; ++row) |
105 | { |
106 | new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 1, |
107 | lwidth-2, fontHeight, |
108 | labels[row], TextAlign::Left); |
109 | } |
110 | |
111 | // Create a bitfield widget for changing the processor status |
112 | xpos = x; ypos += 4*lineHeight + 2; |
113 | new StaticTextWidget(boss, lfont, xpos, ypos+1, lwidth-2, fontHeight, |
114 | "PS " , TextAlign::Left); |
115 | myPSRegister = new ToggleBitWidget(boss, nfont, xpos+lwidth, ypos, 8, 1); |
116 | myPSRegister->setTarget(this); |
117 | addFocusWidget(myPSRegister); |
118 | |
119 | // Set the strings to be used in the PSRegister |
120 | // We only do this once because it's the state that changes, not the strings |
121 | const char* const offstr[] = { "n" , "v" , "-" , "b" , "d" , "i" , "z" , "c" }; |
122 | const char* const onstr[] = { "N" , "V" , "-" , "B" , "D" , "I" , "Z" , "C" }; |
123 | StringList off, on; |
124 | for(int i = 0; i < 8; ++i) |
125 | { |
126 | off.push_back(offstr[i]); |
127 | on.push_back(onstr[i]); |
128 | } |
129 | myPSRegister->setList(off, on); |
130 | |
131 | _h = ypos + myPSRegister->getHeight() - y; |
132 | } |
133 | |
134 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
135 | void CpuWidget::setOpsWidget(DataGridOpsWidget* w) |
136 | { |
137 | myPCGrid->setOpsWidget(w); |
138 | myCpuGrid->setOpsWidget(w); |
139 | myCpuGridDecValue->setOpsWidget(w); |
140 | myCpuGridBinValue->setOpsWidget(w); |
141 | } |
142 | |
143 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
144 | void CpuWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) |
145 | { |
146 | int addr = -1, value = -1; |
147 | CpuDebug& dbg = instance().debugger().cpuDebug(); |
148 | |
149 | switch(cmd) |
150 | { |
151 | case DataGridWidget::kItemDataChangedCmd: |
152 | switch(id) |
153 | { |
154 | case kPCRegID: |
155 | addr = myPCGrid->getSelectedAddr(); |
156 | value = myPCGrid->getSelectedValue(); |
157 | break; |
158 | |
159 | case kCpuRegID: |
160 | addr = myCpuGrid->getSelectedAddr(); |
161 | value = myCpuGrid->getSelectedValue(); |
162 | break; |
163 | |
164 | case kCpuRegDecID: |
165 | addr = myCpuGridDecValue->getSelectedAddr(); |
166 | value = myCpuGridDecValue->getSelectedValue(); |
167 | break; |
168 | |
169 | case kCpuRegBinID: |
170 | addr = myCpuGridBinValue->getSelectedAddr(); |
171 | value = myCpuGridBinValue->getSelectedValue(); |
172 | break; |
173 | } |
174 | |
175 | switch(addr) |
176 | { |
177 | case kPCRegAddr: |
178 | { |
179 | // Use the parser to set PC, since we want to propagate the |
180 | // event the rest of the debugger widgets |
181 | ostringstream command; |
182 | command << "pc #" << value; |
183 | instance().debugger().run(command.str()); |
184 | break; |
185 | } |
186 | |
187 | case kSPRegAddr: |
188 | dbg.setSP(value); |
189 | myCpuGrid->setValueInternal(0, value); |
190 | myCpuGridDecValue->setValueInternal(0, value); |
191 | myCpuGridBinValue->setValueInternal(0, value); |
192 | break; |
193 | |
194 | case kARegAddr: |
195 | dbg.setA(value); |
196 | myCpuGrid->setValueInternal(1, value); |
197 | myCpuGridDecValue->setValueInternal(1, value); |
198 | myCpuGridBinValue->setValueInternal(1, value); |
199 | break; |
200 | |
201 | case kXRegAddr: |
202 | dbg.setX(value); |
203 | myCpuGrid->setValueInternal(2, value); |
204 | myCpuGridDecValue->setValueInternal(2, value); |
205 | myCpuGridBinValue->setValueInternal(2, value); |
206 | break; |
207 | |
208 | case kYRegAddr: |
209 | dbg.setY(value); |
210 | myCpuGrid->setValueInternal(3, value); |
211 | myCpuGridDecValue->setValueInternal(3, value); |
212 | myCpuGridBinValue->setValueInternal(3, value); |
213 | break; |
214 | } |
215 | break; |
216 | |
217 | case ToggleWidget::kItemDataChangedCmd: |
218 | { |
219 | bool state = myPSRegister->getSelectedState(); |
220 | |
221 | switch(data) |
222 | { |
223 | case kPSRegN: |
224 | dbg.setN(state); |
225 | break; |
226 | |
227 | case kPSRegV: |
228 | dbg.setV(state); |
229 | break; |
230 | |
231 | case kPSRegB: |
232 | dbg.setB(state); |
233 | break; |
234 | |
235 | case kPSRegD: |
236 | dbg.setD(state); |
237 | break; |
238 | |
239 | case kPSRegI: |
240 | dbg.setI(state); |
241 | break; |
242 | |
243 | case kPSRegZ: |
244 | dbg.setZ(state); |
245 | break; |
246 | |
247 | case kPSRegC: |
248 | dbg.setC(state); |
249 | break; |
250 | } |
251 | } |
252 | } |
253 | } |
254 | |
255 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
256 | void CpuWidget::loadConfig() |
257 | { |
258 | IntArray alist; |
259 | IntArray vlist; |
260 | BoolArray changed; |
261 | |
262 | // We push the enumerated items as addresses, and deal with the real |
263 | // address in the callback (handleCommand) |
264 | Debugger& dbg = instance().debugger(); |
265 | CartDebug& cart = dbg.cartDebug(); |
266 | CpuDebug& cpu = dbg.cpuDebug(); |
267 | const CpuState& state = static_cast<const CpuState&>(cpu.getState()); |
268 | const CpuState& oldstate = static_cast<const CpuState&>(cpu.getOldState()); |
269 | |
270 | // Add PC to its own DataGridWidget |
271 | alist.push_back(kPCRegAddr); |
272 | vlist.push_back(state.PC); |
273 | changed.push_back(state.PC != oldstate.PC); |
274 | |
275 | myPCGrid->setList(alist, vlist, changed); |
276 | |
277 | // Add the other registers |
278 | alist.clear(); vlist.clear(); changed.clear(); |
279 | alist.push_back(kSPRegAddr); |
280 | alist.push_back(kARegAddr); |
281 | alist.push_back(kXRegAddr); |
282 | alist.push_back(kYRegAddr); |
283 | |
284 | // And now fill the values |
285 | vlist.push_back(state.SP); |
286 | vlist.push_back(state.A); |
287 | vlist.push_back(state.X); |
288 | vlist.push_back(state.Y); |
289 | |
290 | // Figure out which items have changed |
291 | changed.push_back(state.SP != oldstate.SP); |
292 | changed.push_back(state.A != oldstate.A); |
293 | changed.push_back(state.X != oldstate.X); |
294 | changed.push_back(state.Y != oldstate.Y); |
295 | |
296 | // Finally, update the register list |
297 | myCpuGrid->setList(alist, vlist, changed); |
298 | myCpuGridDecValue->setList(alist, vlist, changed); |
299 | myCpuGridBinValue->setList(alist, vlist, changed); |
300 | |
301 | // Update the data sources for the SP/A/X/Y registers |
302 | const string& srcS = state.srcS < 0 ? "IMM" : cart.getLabel(state.srcS, true); |
303 | myCpuDataSrc[0]->setText((srcS != EmptyString ? srcS : Common::Base::toString(state.srcS)), |
304 | state.srcS != oldstate.srcS); |
305 | const string& srcA = state.srcA < 0 ? "IMM" : cart.getLabel(state.srcA, true); |
306 | myCpuDataSrc[1]->setText((srcA != EmptyString ? srcA : Common::Base::toString(state.srcA)), |
307 | state.srcA != oldstate.srcA); |
308 | const string& srcX = state.srcX < 0 ? "IMM" : cart.getLabel(state.srcX, true); |
309 | myCpuDataSrc[2]->setText((srcX != EmptyString ? srcX : Common::Base::toString(state.srcX)), |
310 | state.srcX != oldstate.srcX); |
311 | const string& srcY = state.srcY < 0 ? "IMM" : cart.getLabel(state.srcY, true); |
312 | myCpuDataSrc[3]->setText((srcY != EmptyString ? srcY : Common::Base::toString(state.srcY)), |
313 | state.srcY != oldstate.srcY); |
314 | |
315 | // Update the PS register booleans |
316 | changed.clear(); |
317 | for(uInt32 i = 0; i < state.PSbits.size(); ++i) |
318 | changed.push_back(state.PSbits[i] != oldstate.PSbits[i]); |
319 | |
320 | myPSRegister->setState(state.PSbits, changed); |
321 | myPCLabel->setText(dbg.cartDebug().getLabel(state.PC, true)); |
322 | } |
323 | |