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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34CpuWidget::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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135void CpuWidget::setOpsWidget(DataGridOpsWidget* w)
136{
137 myPCGrid->setOpsWidget(w);
138 myCpuGrid->setOpsWidget(w);
139 myCpuGridDecValue->setOpsWidget(w);
140 myCpuGridBinValue->setOpsWidget(w);
141}
142
143// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
144void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
256void 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