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