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 "CartDPCPlus.hxx" |
19 | #include "DataGridWidget.hxx" |
20 | #include "PopUpWidget.hxx" |
21 | #include "CartDPCPlusWidget.hxx" |
22 | |
23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
24 | CartridgeDPCPlusWidget::CartridgeDPCPlusWidget( |
25 | GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, |
26 | int x, int y, int w, int h, CartridgeDPCPlus& 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 << "Extended DPC cartridge, six 4K banks, 4K display bank, 1K frequency table, " |
34 | << "8K DPC RAM\n" |
35 | << "DPC registers accessible @ $F000 - $F07F\n" |
36 | << " $F000 - $F03F (R), $F040 - $F07F (W)\n" |
37 | << "Banks accessible at hotspots $FFF6 to $FFFB\n" |
38 | << "Startup bank = " << cart.startBank() << "\n" ; |
39 | |
40 | #if 0 |
41 | // Eventually, we should query this from the debugger/disassembler |
42 | for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF6; i < 6; ++i, offset += 0x1000) |
43 | { |
44 | uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset]; |
45 | start -= start % 0x1000; |
46 | info << "Bank " << i << " @ $" << HEX4 << (start + 0x80) << " - " |
47 | << "$" << (start + 0xFFF) << " (hotspot = $" << (spot+i) << ")\n" ; |
48 | } |
49 | #endif |
50 | |
51 | int xpos = 2, |
52 | ypos = addBaseInformation(size, "Activision (Pitfall II)" , info.str()) + |
53 | myLineHeight; |
54 | |
55 | VariantList items; |
56 | VarList::push_back(items, "0 ($FFF6)" ); |
57 | VarList::push_back(items, "1 ($FFF7)" ); |
58 | VarList::push_back(items, "2 ($FFF8)" ); |
59 | VarList::push_back(items, "3 ($FFF9)" ); |
60 | VarList::push_back(items, "4 ($FFFA)" ); |
61 | VarList::push_back(items, "5 ($FFFB)" ); |
62 | myBank = |
63 | new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFFx)" ), |
64 | myLineHeight, items, "Set bank " , |
65 | 0, kBankChanged); |
66 | myBank->setTarget(this); |
67 | addFocusWidget(myBank); |
68 | |
69 | // Top registers |
70 | int lwidth = _font.getStringWidth("Counter Registers " ); |
71 | xpos = 2; ypos += myLineHeight + 8; |
72 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
73 | myFontHeight, "Top Registers " , TextAlign::Left); |
74 | xpos += lwidth; |
75 | |
76 | myTops = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::F_16); |
77 | myTops->setTarget(this); |
78 | myTops->setEditable(false); |
79 | |
80 | // Bottom registers |
81 | xpos = 2; ypos += myLineHeight + 4; |
82 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
83 | myFontHeight, "Bottom Registers " , TextAlign::Left); |
84 | xpos += lwidth; |
85 | |
86 | myBottoms = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::F_16); |
87 | myBottoms->setTarget(this); |
88 | myBottoms->setEditable(false); |
89 | |
90 | // Counter registers |
91 | xpos = 2; ypos += myLineHeight + 4; |
92 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
93 | myFontHeight, "Counter Registers " , TextAlign::Left); |
94 | xpos += lwidth; |
95 | |
96 | myCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 4, 16, Common::Base::F_16_4); |
97 | myCounters->setTarget(this); |
98 | myCounters->setEditable(false); |
99 | |
100 | // Fractional counter registers |
101 | xpos = 2; ypos += myLineHeight + 4; |
102 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
103 | myFontHeight, "Frac Counters " , TextAlign::Left); |
104 | xpos += lwidth; |
105 | |
106 | myFracCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 4, 2, 8, 32, Common::Base::F_16_8); |
107 | myFracCounters->setTarget(this); |
108 | myFracCounters->setEditable(false); |
109 | |
110 | // Fractional increment registers |
111 | xpos = 2; ypos += myFracCounters->getHeight() + 8; |
112 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
113 | myFontHeight, "Frac Increments " , TextAlign::Left); |
114 | xpos += lwidth; |
115 | |
116 | myFracIncrements = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::F_16); |
117 | myFracIncrements->setTarget(this); |
118 | myFracIncrements->setEditable(false); |
119 | |
120 | // Special function parameters |
121 | xpos = 2; ypos += myLineHeight + 4; |
122 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
123 | myFontHeight, "Function Params " , TextAlign::Left); |
124 | xpos += lwidth; |
125 | |
126 | myParameter = new DataGridWidget(boss, _nfont, xpos, ypos-2, 8, 1, 2, 8, Common::Base::F_16); |
127 | myParameter->setTarget(this); |
128 | myParameter->setEditable(false); |
129 | |
130 | // Music counters |
131 | xpos = 2; ypos += myLineHeight + 4; |
132 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
133 | myFontHeight, "Music Counters " , TextAlign::Left); |
134 | xpos += lwidth; |
135 | |
136 | myMusicCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8); |
137 | myMusicCounters->setTarget(this); |
138 | myMusicCounters->setEditable(false); |
139 | |
140 | // Music frequencies |
141 | xpos = 2; ypos += myLineHeight + 4; |
142 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
143 | myFontHeight, "Music Frequencies " , TextAlign::Left); |
144 | xpos += lwidth; |
145 | |
146 | myMusicFrequencies = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8); |
147 | myMusicFrequencies->setTarget(this); |
148 | myMusicFrequencies->setEditable(false); |
149 | |
150 | // Music waveforms |
151 | xpos = 2; ypos += myLineHeight + 4; |
152 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
153 | myFontHeight, "Music Waveforms " , TextAlign::Left); |
154 | xpos += lwidth; |
155 | |
156 | myMusicWaveforms = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_4); |
157 | myMusicWaveforms->setTarget(this); |
158 | myMusicWaveforms->setEditable(false); |
159 | |
160 | // Current random number |
161 | lwidth = _font.getStringWidth("Current random number " ); |
162 | xpos = 2; ypos += myLineHeight + 4; |
163 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
164 | myFontHeight, "Current random number " , TextAlign::Left); |
165 | xpos += lwidth; |
166 | |
167 | myRandom = new DataGridWidget(boss, _nfont, xpos, ypos-2, 1, 1, 8, 32, Common::Base::F_16_8); |
168 | myRandom->setTarget(this); |
169 | myRandom->setEditable(false); |
170 | |
171 | // Fast fetch and immediate mode LDA flags |
172 | xpos += myRandom->getWidth() + 30; |
173 | myFastFetch = new CheckboxWidget(boss, _font, xpos, ypos, "Fast Fetcher enabled" ); |
174 | myFastFetch->setTarget(this); |
175 | myFastFetch->setEditable(false); |
176 | ypos += myLineHeight + 4; |
177 | myIMLDA = new CheckboxWidget(boss, _font, xpos, ypos, "Immediate mode LDA" ); |
178 | myIMLDA->setTarget(this); |
179 | myIMLDA->setEditable(false); |
180 | } |
181 | |
182 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
183 | void CartridgeDPCPlusWidget::saveOldState() |
184 | { |
185 | myOldState.tops.clear(); |
186 | myOldState.bottoms.clear(); |
187 | myOldState.counters.clear(); |
188 | myOldState.fraccounters.clear(); |
189 | myOldState.fracinc.clear(); |
190 | myOldState.param.clear(); |
191 | myOldState.mcounters.clear(); |
192 | myOldState.mfreqs.clear(); |
193 | myOldState.mwaves.clear(); |
194 | myOldState.internalram.clear(); |
195 | |
196 | for(uInt32 i = 0; i < 8; ++i) |
197 | { |
198 | myOldState.tops.push_back(myCart.myTops[i]); |
199 | myOldState.bottoms.push_back(myCart.myBottoms[i]); |
200 | myOldState.counters.push_back(myCart.myCounters[i]); |
201 | myOldState.fraccounters.push_back(myCart.myFractionalCounters[i]); |
202 | myOldState.fracinc.push_back(myCart.myFractionalIncrements[i]); |
203 | myOldState.param.push_back(myCart.myParameter[i]); |
204 | } |
205 | for(uInt32 i = 0; i < 3; ++i) |
206 | { |
207 | myOldState.mcounters.push_back(myCart.myMusicCounters[i]); |
208 | myOldState.mfreqs.push_back(myCart.myMusicFrequencies[i]); |
209 | myOldState.mwaves.push_back(myCart.myMusicWaveforms[i]); |
210 | } |
211 | |
212 | myOldState.random = myCart.myRandomNumber; |
213 | |
214 | for(uInt32 i = 0; i < internalRamSize(); ++i) |
215 | myOldState.internalram.push_back(myCart.myDisplayImage[i]); |
216 | |
217 | myOldState.bank = myCart.getBank(); |
218 | } |
219 | |
220 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
221 | void CartridgeDPCPlusWidget::loadConfig() |
222 | { |
223 | myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); |
224 | |
225 | // Get registers, using change tracking |
226 | IntArray alist; |
227 | IntArray vlist; |
228 | BoolArray changed; |
229 | |
230 | alist.clear(); vlist.clear(); changed.clear(); |
231 | for(int i = 0; i < 8; ++i) |
232 | { |
233 | alist.push_back(0); vlist.push_back(myCart.myTops[i]); |
234 | changed.push_back(myCart.myTops[i] != myOldState.tops[i]); |
235 | } |
236 | myTops->setList(alist, vlist, changed); |
237 | |
238 | alist.clear(); vlist.clear(); changed.clear(); |
239 | for(int i = 0; i < 8; ++i) |
240 | { |
241 | alist.push_back(0); vlist.push_back(myCart.myBottoms[i]); |
242 | changed.push_back(myCart.myBottoms[i] != myOldState.bottoms[i]); |
243 | } |
244 | myBottoms->setList(alist, vlist, changed); |
245 | |
246 | alist.clear(); vlist.clear(); changed.clear(); |
247 | for(int i = 0; i < 8; ++i) |
248 | { |
249 | alist.push_back(0); vlist.push_back(myCart.myCounters[i]); |
250 | changed.push_back(myCart.myCounters[i] != myOldState.counters[i]); |
251 | } |
252 | myCounters->setList(alist, vlist, changed); |
253 | |
254 | alist.clear(); vlist.clear(); changed.clear(); |
255 | for(int i = 0; i < 8; ++i) |
256 | { |
257 | alist.push_back(0); vlist.push_back(myCart.myFractionalCounters[i]); |
258 | changed.push_back(myCart.myFractionalCounters[i] != uInt32(myOldState.fraccounters[i])); |
259 | } |
260 | myFracCounters->setList(alist, vlist, changed); |
261 | |
262 | alist.clear(); vlist.clear(); changed.clear(); |
263 | for(int i = 0; i < 8; ++i) |
264 | { |
265 | alist.push_back(0); vlist.push_back(myCart.myFractionalIncrements[i]); |
266 | changed.push_back(myCart.myFractionalIncrements[i] != myOldState.fracinc[i]); |
267 | } |
268 | myFracIncrements->setList(alist, vlist, changed); |
269 | |
270 | alist.clear(); vlist.clear(); changed.clear(); |
271 | for(int i = 0; i < 8; ++i) |
272 | { |
273 | alist.push_back(0); vlist.push_back(myCart.myParameter[i]); |
274 | changed.push_back(myCart.myParameter[i] != myOldState.param[i]); |
275 | } |
276 | myParameter->setList(alist, vlist, changed); |
277 | |
278 | alist.clear(); vlist.clear(); changed.clear(); |
279 | for(int i = 0; i < 3; ++i) |
280 | { |
281 | alist.push_back(0); vlist.push_back(myCart.myMusicCounters[i]); |
282 | changed.push_back(myCart.myMusicCounters[i] != uInt32(myOldState.mcounters[i])); |
283 | } |
284 | myMusicCounters->setList(alist, vlist, changed); |
285 | |
286 | alist.clear(); vlist.clear(); changed.clear(); |
287 | for(int i = 0; i < 3; ++i) |
288 | { |
289 | alist.push_back(0); vlist.push_back(myCart.myMusicFrequencies[i]); |
290 | changed.push_back(myCart.myMusicFrequencies[i] != uInt32(myOldState.mfreqs[i])); |
291 | } |
292 | myMusicFrequencies->setList(alist, vlist, changed); |
293 | |
294 | alist.clear(); vlist.clear(); changed.clear(); |
295 | for(int i = 0; i < 3; ++i) |
296 | { |
297 | alist.push_back(0); vlist.push_back(myCart.myMusicWaveforms[i]); |
298 | changed.push_back(myCart.myMusicWaveforms[i] != myOldState.mwaves[i]); |
299 | } |
300 | myMusicWaveforms->setList(alist, vlist, changed); |
301 | |
302 | myRandom->setList(0, myCart.myRandomNumber, |
303 | myCart.myRandomNumber != myOldState.random); |
304 | |
305 | myFastFetch->setState(myCart.myFastFetch); |
306 | myIMLDA->setState(myCart.myLDAimmediate); |
307 | |
308 | CartDebugWidget::loadConfig(); |
309 | } |
310 | |
311 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
312 | void CartridgeDPCPlusWidget::handleCommand(CommandSender* sender, |
313 | int cmd, int data, int id) |
314 | { |
315 | if(cmd == kBankChanged) |
316 | { |
317 | myCart.unlockBank(); |
318 | myCart.bank(myBank->getSelected()); |
319 | myCart.lockBank(); |
320 | invalidate(); |
321 | } |
322 | } |
323 | |
324 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
325 | string CartridgeDPCPlusWidget::bankState() |
326 | { |
327 | ostringstream& buf = buffer(); |
328 | |
329 | static const char* const spot[] = { |
330 | "$FFF6" , "$FFF7" , "$FFF8" , "$FFF9" , "$FFFA" , "$FFFB" |
331 | }; |
332 | buf << "Bank = " << std::dec << myCart.getBank() |
333 | << ", hotspot = " << spot[myCart.getBank()]; |
334 | |
335 | return buf.str(); |
336 | } |
337 | |
338 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
339 | uInt32 CartridgeDPCPlusWidget::internalRamSize() |
340 | { |
341 | return 5*1024; |
342 | } |
343 | |
344 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
345 | uInt32 CartridgeDPCPlusWidget::internalRamRPort(int start) |
346 | { |
347 | return 0x0000 + start; |
348 | } |
349 | |
350 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
351 | string CartridgeDPCPlusWidget::internalRamDescription() |
352 | { |
353 | ostringstream desc; |
354 | desc << "$0000 - $0FFF - 4K display data\n" |
355 | << " indirectly accessible to 6507\n" |
356 | << " via DPC+'s Data Fetcher registers\n" |
357 | << "$1000 - $13FF - 1K frequency table,\n" |
358 | << " C variables and C stack\n" |
359 | << " not accessible to 6507" ; |
360 | |
361 | return desc.str(); |
362 | } |
363 | |
364 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
365 | const ByteArray& CartridgeDPCPlusWidget::internalRamOld(int start, int count) |
366 | { |
367 | myRamOld.clear(); |
368 | for(int i = 0; i < count; i++) |
369 | myRamOld.push_back(myOldState.internalram[start + i]); |
370 | return myRamOld; |
371 | } |
372 | |
373 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
374 | const ByteArray& CartridgeDPCPlusWidget::internalRamCurrent(int start, int count) |
375 | { |
376 | myRamCurrent.clear(); |
377 | for(int i = 0; i < count; i++) |
378 | myRamCurrent.push_back(myCart.myDisplayImage[start + i]); |
379 | return myRamCurrent; |
380 | } |
381 | |
382 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
383 | void CartridgeDPCPlusWidget::internalRamSetValue(int addr, uInt8 value) |
384 | { |
385 | myCart.myDisplayImage[addr] = value; |
386 | } |
387 | |
388 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
389 | uInt8 CartridgeDPCPlusWidget::internalRamGetValue(int addr) |
390 | { |
391 | return myCart.myDisplayImage[addr]; |
392 | } |
393 | |