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