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 "PopUpWidget.hxx"
19 #include "OSystem.hxx"
20 #include "Debugger.hxx"
21 #include "CartDebug.hxx"
22 #include "CartEnhanced.hxx"
23 #include "CartEnhancedWidget.hxx"
24
25 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeEnhancedWidget(GuiObject * boss,const GUI::Font & lfont,const GUI::Font & nfont,int x,int y,int w,int h,CartridgeEnhanced & cart)26 CartridgeEnhancedWidget::CartridgeEnhancedWidget(GuiObject* boss, const GUI::Font& lfont,
27 const GUI::Font& nfont,
28 int x, int y, int w, int h,
29 CartridgeEnhanced& cart)
30 : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
31 myCart{cart}
32 {
33 }
34
35 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
initialize()36 int CartridgeEnhancedWidget::initialize()
37 {
38 int ypos = addBaseInformation(size(), manufacturer(), description(), descriptionLines())
39 + myLineHeight;
40
41 bankSelect(ypos);
42
43 return ypos;
44 }
45
46 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
size()47 size_t CartridgeEnhancedWidget::size()
48 {
49 size_t size;
50
51 myCart.getImage(size);
52
53 return size;
54 }
55
56 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
description()57 string CartridgeEnhancedWidget::description()
58 {
59 ostringstream info;
60
61 if (myCart.myRamSize > 0)
62 info << ramDescription();
63 info << romDescription();
64
65 return info.str();
66 }
67
68 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
descriptionLines()69 int CartridgeEnhancedWidget::descriptionLines()
70 {
71 return 18; // should be enough for almost all types
72 }
73
74 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ramDescription()75 string CartridgeEnhancedWidget::ramDescription()
76 {
77 ostringstream info;
78
79 if(myCart.ramBankCount() == 0)
80 info << myCart.myRamSize << " bytes RAM @ "
81 << "$" << Common::Base::HEX4 << ADDR_BASE << " - "
82 << "$" << (ADDR_BASE | (myCart.myRamSize * 2 - 1)) << "\n";
83
84 info << " $" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset)
85 << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask)) << " (R)"
86 << ", $" << (ADDR_BASE | myCart.myWriteOffset)
87 << " - $" << (ADDR_BASE | (myCart.myWriteOffset + myCart.myRamMask)) << " (W)\n";
88
89 return info.str();
90 }
91
92 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
romDescription()93 string CartridgeEnhancedWidget::romDescription()
94 {
95 ostringstream info;
96 size_t size;
97 const ByteBuffer& image = myCart.getImage(size);
98
99 if(myCart.romBankCount() > 1)
100 {
101 for(int bank = 0, offset = 0xFFC; bank < myCart.romBankCount(); ++bank, offset += 0x1000)
102 {
103 uInt16 start = (image[offset + 1] << 8) | image[offset];
104 start -= start % 0x1000;
105 string hash = myCart.romBankCount() > 10 && bank < 10 ? " #" : "#";
106
107 info << "Bank " << hash << std::dec << bank << " @ $"
108 << Common::Base::HEX4 << (start + myCart.myRomOffset) << " - $" << (start + 0xFFF);
109 if(myCart.hotspot() != 0)
110 {
111 string hs = hotspotStr(bank, 0, true);
112 if(hs.length() > 22)
113 info << "\n ";
114 info << " " << hs;
115 }
116 info << "\n";
117 }
118 info << "Startup bank = #" << std::dec << myCart.startBank() << " or undetermined\n";
119 }
120 else
121 {
122 uInt16 start = (image[myCart.mySize - 3] << 8) | image[myCart.mySize - 4];
123 uInt16 end;
124
125 start -= start % std::min(int(size), 0x1000);
126 end = start + uInt16(myCart.mySize) - 1;
127 // special check for ROMs where the extra RAM is not included in the image (e.g. CV).
128 if((start & 0xFFFU) < size)
129 {
130 start += myCart.myRomOffset;
131 }
132 info << "ROM accessible @ $"
133 << Common::Base::HEX4 << start << " - $"
134 << Common::Base::HEX4 << end;
135 }
136
137 return info.str();
138 }
139
140 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bankList(uInt16 bankCount,int seg,VariantList & items,int & width)141 void CartridgeEnhancedWidget::bankList(uInt16 bankCount, int seg, VariantList& items, int& width)
142 {
143 width = 0;
144
145 for(int bank = 0; bank < bankCount; ++bank)
146 {
147 ostringstream buf;
148
149 buf << std::setw(bank < 10 ? 2 : 1) << "#" << std::dec << bank;
150 if(myCart.hotspot() != 0 && myHotspotDelta > 0)
151 buf << " " << hotspotStr(bank, seg);
152 VarList::push_back(items, buf.str());
153 width = std::max(width, _font.getStringWidth(buf.str()));
154 }
155 }
156
157 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bankSelect(int & ypos)158 void CartridgeEnhancedWidget::bankSelect(int& ypos)
159 {
160 if(myCart.romBankCount() > 1)
161 {
162 int xpos = 2;
163
164 myBankWidgets = make_unique<PopUpWidget* []>(bankSegs());
165
166 for(int seg = 0; seg < bankSegs(); ++seg)
167 {
168 // fill bank and hotspot list
169 VariantList items;
170 int pw = 0;
171
172 bankList(myCart.romBankCount(), seg, items, pw);
173
174 // create widgets
175 ostringstream buf;
176
177 buf << "Set bank";
178 if(bankSegs() > 1)
179 buf << " for segment #" << seg << " ";
180 else
181 buf << " "; // align with info
182
183 myBankWidgets[seg] = new PopUpWidget(_boss, _font, xpos, ypos - 2,
184 pw, myLineHeight, items, buf.str(),
185 0, kBankChanged);
186 myBankWidgets[seg]->setTarget(this);
187 myBankWidgets[seg]->setID(seg);
188 addFocusWidget(myBankWidgets[seg]);
189
190 ypos += myBankWidgets[seg]->getHeight() + 4;
191 }
192 }
193 }
194
195 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bankState()196 string CartridgeEnhancedWidget::bankState()
197 {
198 if(myCart.romBankCount() > 1)
199 {
200 ostringstream& buf = buffer();
201 uInt16 hotspot = myCart.hotspot();
202 bool hasRamBanks = myCart.myRamBankCount > 0;
203
204 if(bankSegs() > 1)
205 {
206 buf << "Segments: ";
207
208 for(int seg = 0; seg < bankSegs(); ++seg)
209 {
210 int bank = myCart.getSegmentBank(seg);
211 bool isRamBank = (bank >= myCart.romBankCount());
212
213
214 if(seg > 0)
215 buf << " / ";
216
217 buf << "#" << std::dec << (bank - (isRamBank ? myCart.romBankCount() : 0));
218
219 if(isRamBank) // was RAM mapped here?
220 buf << " RAM";
221 else if (hasRamBanks)
222 buf << " ROM";
223
224 //if(hotspot >= 0x100)
225 if(hotspot != 0 && myHotspotDelta > 0)
226 buf << " " << hotspotStr(bank, 0, bankSegs() < 3);
227 }
228 }
229 else
230 {
231 buf << "Bank #" << std::dec << myCart.getBank();
232
233 if(hotspot != 0 && myHotspotDelta > 0)
234 buf << " " << hotspotStr(myCart.getBank(), 0, true);
235 }
236 return buf.str();
237 }
238 return "non-bankswitched";
239 }
240
241 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
hotspotStr(int bank,int segment,bool prefix)242 string CartridgeEnhancedWidget::hotspotStr(int bank, int segment, bool prefix)
243 {
244 ostringstream info;
245 uInt16 hotspot = myCart.hotspot();
246
247 if(hotspot & 0x1000)
248 hotspot |= ADDR_BASE;
249
250 info << "(" << (prefix ? "hotspot " : "");
251 info << "$" << Common::Base::HEX1 << (hotspot + bank * myHotspotDelta);
252 info << ")";
253
254 return info.str();
255 }
256
257 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bankSegs()258 uInt16 CartridgeEnhancedWidget::bankSegs()
259 {
260 return myCart.myBankSegs;
261 }
262
263 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
saveOldState()264 void CartridgeEnhancedWidget::saveOldState()
265 {
266 myOldState.internalRam.clear();
267 for(uInt32 i = 0; i < myCart.myRamSize; ++i)
268 myOldState.internalRam.push_back(myCart.myRAM[i]);
269
270 myOldState.banks.clear();
271 if (bankSegs() > 1)
272 for(int seg = 0; seg < bankSegs(); ++seg)
273 myOldState.banks.push_back(myCart.getSegmentBank(seg));
274 else
275 myOldState.banks.push_back(myCart.getBank());
276 }
277
278 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadConfig()279 void CartridgeEnhancedWidget::loadConfig()
280 {
281 if(myBankWidgets != nullptr)
282 {
283 if (bankSegs() > 1)
284 for(int seg = 0; seg < bankSegs(); ++seg)
285 myBankWidgets[seg]->setSelectedIndex(myCart.getSegmentBank(seg),
286 myCart.getSegmentBank(seg) != myOldState.banks[seg]);
287 else
288 myBankWidgets[0]->setSelectedIndex(myCart.getBank(),
289 myCart.getBank() != myOldState.banks[0]);
290 }
291 CartDebugWidget::loadConfig();
292 }
293
294 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
handleCommand(CommandSender * sender,int cmd,int data,int id)295 void CartridgeEnhancedWidget::handleCommand(CommandSender* sender,
296 int cmd, int data, int id)
297 {
298 if(cmd == kBankChanged)
299 {
300 myCart.unlockHotspots();
301 myCart.bank(myBankWidgets[id]->getSelected(), id);
302 myCart.lockHotspots();
303 invalidate();
304 }
305 }
306
307 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamSize()308 uInt32 CartridgeEnhancedWidget::internalRamSize()
309 {
310 return uInt32(myCart.myRamSize);
311 }
312
313 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamRPort(int start)314 uInt32 CartridgeEnhancedWidget::internalRamRPort(int start)
315 {
316 if(myCart.ramBankCount() == 0)
317 return ADDR_BASE + myCart.myReadOffset + start;
318 else
319 return start;
320 }
321
322 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamDescription()323 string CartridgeEnhancedWidget::internalRamDescription()
324 {
325 ostringstream desc;
326 string indent = "";
327
328 if(myCart.ramBankCount())
329 {
330 desc << "Accessible ";
331 if (myCart.bankSize() >> 1 >= 1024)
332 desc << ((myCart.bankSize() >> 1) / 1024) << "K";
333 else
334 desc << (myCart.bankSize() >> 1) << " bytes";
335 desc << " at a time via:\n";
336 indent = " ";
337 }
338
339 // order RW by addresses
340 if(myCart.myReadOffset <= myCart.myWriteOffset)
341 {
342 desc << indent << "$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset)
343 << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask))
344 << " used for read access\n";
345 }
346
347 desc << indent << "$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myWriteOffset)
348 << " - $" << (ADDR_BASE | (myCart.myWriteOffset + myCart.myRamMask))
349 << " used for write access";
350
351 if(myCart.myReadOffset > myCart.myWriteOffset)
352 {
353 desc << indent << "\n$" << Common::Base::HEX4 << (ADDR_BASE | myCart.myReadOffset)
354 << " - $" << (ADDR_BASE | (myCart.myReadOffset + myCart.myRamMask))
355 << " used for read access";
356 }
357
358 return desc.str();
359 }
360
361 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamOld(int start,int count)362 const ByteArray& CartridgeEnhancedWidget::internalRamOld(int start, int count)
363 {
364 myRamOld.clear();
365 for(int i = 0; i < count; i++)
366 myRamOld.push_back(myOldState.internalRam[start + i]);
367 return myRamOld;
368 }
369
370 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamCurrent(int start,int count)371 const ByteArray& CartridgeEnhancedWidget::internalRamCurrent(int start, int count)
372 {
373 myRamCurrent.clear();
374 for(int i = 0; i < count; i++)
375 myRamCurrent.push_back(myCart.myRAM[start + i]);
376 return myRamCurrent;
377 }
378
379 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamSetValue(int addr,uInt8 value)380 void CartridgeEnhancedWidget::internalRamSetValue(int addr, uInt8 value)
381 {
382 myCart.myRAM[addr] = value;
383 }
384
385 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamGetValue(int addr)386 uInt8 CartridgeEnhancedWidget::internalRamGetValue(int addr)
387 {
388 return myCart.myRAM[addr];
389 }
390
391 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
internalRamLabel(int addr)392 string CartridgeEnhancedWidget::internalRamLabel(int addr)
393 {
394 CartDebug& dbg = instance().debugger().cartDebug();
395 return dbg.getLabel(addr + ADDR_BASE + myCart.myReadOffset, false, -1, true);
396 }
397