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 "CartDASH.hxx"
19#include "EditTextWidget.hxx"
20#include "PopUpWidget.hxx"
21#include "CartDASHWidget.hxx"
22
23// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24CartridgeDASHWidget::CartridgeDASHWidget(
25 GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
26 int x, int y, int w, int h, CartridgeDASH& cart)
27 : CartDebugWidget(boss, lfont, nfont, x, y, w, h),
28 myCart(cart)
29{
30 size_t size = cart.mySize;
31
32 ostringstream info;
33 info << "DASH cartridge - (64K ROM + RAM)\n"
34 << " 4-64K ROM (1K banks), 32K RAM (512b banks)\n"
35 << "Each 1K ROM selected by writing to $3F\n"
36 "Each 512b RAM selected by writing to $3E\n"
37 " First 512B of bank x (R)\n"
38 " First 512B of bank x+4 (+$800) (W)\n"
39 << "Startup bank = 0/-1/-1/0 (ROM)\n";
40
41 // Eventually, we should query this from the debugger/disassembler
42 uInt16 start = (cart.myImage[size-3] << 8) | cart.myImage[size-4];
43 start -= start % 0x1000;
44 info << "Bank RORG" << " = $" << Common::Base::HEX4 << start << "\n";
45
46 int xpos = 2,
47 ypos = addBaseInformation(size, "A. Davie & T. Jentzsch", info.str()) +
48 myLineHeight;
49
50 VariantList bankno;
51 for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++i)
52 VarList::push_back(bankno, i, i);
53
54 VariantList banktype;
55 VarList::push_back(banktype, "ROM", "ROM");
56 VarList::push_back(banktype, "RAM", "RAM");
57
58 for(uInt32 i = 0; i < 4; ++i)
59 {
60 int xpos_s, ypos_s = ypos;
61
62 ostringstream label;
63 label << "Set segment " << i << " as: ";
64
65 new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()),
66 myFontHeight, label.str(), TextAlign::Left);
67 ypos += myLineHeight + 8;
68
69 xpos += 20;
70 myBankNumber[i] =
71 new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("Slot "),
72 myLineHeight, bankno, "Slot ",
73 6*_font.getMaxCharWidth());
74 addFocusWidget(myBankNumber[i]);
75
76 xpos += myBankNumber[i]->getWidth();
77 myBankType[i] =
78 new PopUpWidget(boss, _font, xpos, ypos-2, 5*_font.getMaxCharWidth(),
79 myLineHeight, banktype, " of ", _font.getStringWidth(" of "));
80 addFocusWidget(myBankType[i]);
81
82 xpos += myBankType[i]->getWidth() + 10;
83
84 myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4,
85 _font.getStringWidth(" Commit "), myButtonHeight,
86 "Commit", bankEnum[i]);
87 myBankCommit[i]->setTarget(this);
88 addFocusWidget(myBankCommit[i]);
89
90 xpos_s = xpos + myBankCommit[i]->getWidth() + 20;
91
92 StaticTextWidget* t;
93 int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF;
94
95 label.str("");
96 label << Common::Base::HEX4 << addr1 << "-" << Common::Base::HEX4 << addr2;
97 t = new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
98 _font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
99
100 int xoffset = xpos_s+t->getWidth() + 10;
101 myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s,
102 w - xoffset - 10, myLineHeight, "");
103 myBankState[2*i]->setEditable(false, true);
104 ypos_s += myLineHeight + 4;
105
106 label.str("");
107 label << Common::Base::HEX4 << (addr2 + 1) << "-" << Common::Base::HEX4 << (addr2 + 1 + 0x1FF);
108 new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
109 _font.getStringWidth(label.str()), myFontHeight, label.str(), TextAlign::Left);
110
111 myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s,
112 w - xoffset - 10, myLineHeight, "");
113 myBankState[2*i+1]->setEditable(false, true);
114
115 xpos = 10;
116 ypos+= 2 * myLineHeight;
117 }
118}
119
120// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121void CartridgeDASHWidget::saveOldState()
122{
123 myOldState.internalram.clear();
124
125 for(uInt32 i = 0; i < internalRamSize(); ++i)
126 myOldState.internalram.push_back(myCart.myRAM[i]);
127}
128
129// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
130void CartridgeDASHWidget::loadConfig()
131{
132 updateUIState();
133 CartDebugWidget::loadConfig();
134}
135
136// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137void CartridgeDASHWidget::handleCommand(CommandSender* sender,
138 int cmd, int data, int id)
139{
140 uInt16 segment = 0;
141 switch(cmd)
142 {
143 case kBank0Changed:
144 segment = 0;
145 break;
146 case kBank1Changed:
147 segment = 1;
148 break;
149 case kBank2Changed:
150 segment = 2;
151 break;
152 case kBank3Changed:
153 segment = 3;
154 break;
155 }
156
157 // Ignore bank if either number or type hasn't been selected
158 if(myBankNumber[segment]->getSelected() < 0 ||
159 myBankType[segment]->getSelected() < 0)
160 return;
161
162 uInt8 bank = (segment << myCart.BANK_BITS) |
163 (myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK);
164
165 myCart.unlockBank();
166
167 if(myBankType[segment]->getSelectedTag() == "ROM")
168 myCart.bankROM(bank);
169 else
170 myCart.bankRAM(bank);
171
172 myCart.lockBank();
173 invalidate();
174 updateUIState();
175}
176
177// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178string CartridgeDASHWidget::bankState()
179{
180 ostringstream& buf = buffer();
181 int lastROMBank = -1;
182 bool lastSlotRAM = false;
183
184 for(int i = 0; i < 8; ++i)
185 {
186 uInt16 bank = myCart.bankInUse[i];
187
188 if(bank == myCart.BANK_UNDEFINED) // never accessed
189 {
190 buf << " U!";
191 }
192 else
193 {
194 int bankno = bank & myCart.BIT_BANK_MASK;
195
196 if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
197 {
198 // RAM will always need a '+' placed somewhere, since it always
199 // consists of 512B segments
200 bool inFirstSlot = (i % 2 == 0);
201 if(!(inFirstSlot || lastSlotRAM))
202 {
203 lastSlotRAM = false;
204 buf << " +";
205 }
206
207 if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
208 buf << " RAM " << bankno << "W";
209 else
210 buf << " RAM " << bankno << "R";
211
212 if(inFirstSlot)
213 {
214 buf << " +";
215 lastSlotRAM = true;
216 }
217 }
218 else
219 {
220 // ROM can be contiguous, since 2 512B segments can form a single
221 // 1K bank; in this case we only show the info once
222 bool highBankSame = (i % 2 == 1) && (bankno == lastROMBank);
223 if(!highBankSame)
224 {
225 buf << " ROM " << bankno;
226 lastROMBank = bankno;
227 }
228 else
229 lastROMBank = -1;
230
231 lastSlotRAM = false;
232 }
233 }
234
235 if((i+1) % 2 == 0 && i < 7)
236 buf << " /";
237 }
238
239 return buf.str();
240}
241
242// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
243void CartridgeDASHWidget::updateUIState()
244{
245 // Set contents for actual banks number and type
246 for(int i = 0; i < 4; ++i)
247 {
248 uInt16 segment = myCart.segmentInUse[i];
249
250 if(segment == myCart.BANK_UNDEFINED)
251 {
252 myBankNumber[i]->clearSelection();
253 myBankType[i]->clearSelection();
254 }
255 else
256 {
257 int bankno = segment & myCart.BIT_BANK_MASK;
258 const string& banktype = (segment & myCart.BITMASK_ROMRAM) ? "RAM" : "ROM";
259
260 myBankNumber[i]->setSelected(bankno);
261 myBankType[i]->setSelected(banktype);
262 }
263 }
264
265 // Set description for each 512b bank state
266 for(int i = 0; i < 8; ++i)
267 {
268 uInt16 bank = myCart.bankInUse[i];
269
270 if(bank == myCart.BANK_UNDEFINED) // never accessed
271 {
272 myBankState[i]->setText("Undefined");
273 }
274 else
275 {
276 ostringstream buf;
277 int bankno = bank & myCart.BIT_BANK_MASK;
278
279 if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
280 {
281 if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
282 {
283 buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
284 << (bankno << myCart.RAM_BANK_TO_POWER) << " (W)";
285 myBankState[i]->setText(buf.str());
286 }
287 else
288 {
289 buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
290 << (bankno << myCart.RAM_BANK_TO_POWER) << " (R)";
291 myBankState[i]->setText(buf.str());
292 }
293 }
294 else
295 {
296 if(bank & myCart.BITMASK_LOWERUPPER) // upper is high 512b
297 {
298 buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
299 << ((bankno << myCart.RAM_BANK_TO_POWER) + myCart.RAM_BANK_SIZE);
300 myBankState[i]->setText(buf.str());
301 }
302 else
303 {
304 buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
305 << (bankno << myCart.RAM_BANK_TO_POWER);
306 myBankState[i]->setText(buf.str());
307 }
308 }
309 }
310 }
311}
312
313// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
314uInt32 CartridgeDASHWidget::internalRamSize()
315{
316 return 32*1024;
317}
318
319// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
320uInt32 CartridgeDASHWidget::internalRamRPort(int start)
321{
322 return 0x0000 + start;
323}
324
325// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
326string CartridgeDASHWidget::internalRamDescription()
327{
328 ostringstream desc;
329 desc << "Accessible 512b at a time via:\n"
330 << " $F000/$F200/$F400/etc used for Read Access\n"
331 << " $F800/$FA00/$FC00/etc used for Write Access (+$800)";
332
333 return desc.str();
334}
335
336// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
337const ByteArray& CartridgeDASHWidget::internalRamOld(int start, int count)
338{
339 myRamOld.clear();
340 for(int i = 0; i < count; i++)
341 myRamOld.push_back(myOldState.internalram[start + i]);
342 return myRamOld;
343}
344
345// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
346const ByteArray& CartridgeDASHWidget::internalRamCurrent(int start, int count)
347{
348 myRamCurrent.clear();
349 for(int i = 0; i < count; i++)
350 myRamCurrent.push_back(myCart.myRAM[start + i]);
351 return myRamCurrent;
352}
353
354// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
355void CartridgeDASHWidget::internalRamSetValue(int addr, uInt8 value)
356{
357 myCart.myRAM[addr] = value;
358}
359
360// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
361uInt8 CartridgeDASHWidget::internalRamGetValue(int addr)
362{
363 return myCart.myRAM[addr];
364}
365
366// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
367const CartridgeDASHWidget::BankID CartridgeDASHWidget::bankEnum[4] = {
368 kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed
369};
370