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 "CartCM.hxx" |
19 | #include "Debugger.hxx" |
20 | #include "CartDebug.hxx" |
21 | #include "RiotDebug.hxx" |
22 | #include "DataGridWidget.hxx" |
23 | #include "EditTextWidget.hxx" |
24 | #include "PopUpWidget.hxx" |
25 | #include "ToggleBitWidget.hxx" |
26 | #include "CartCMWidget.hxx" |
27 | |
28 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
29 | CartridgeCMWidget::CartridgeCMWidget( |
30 | GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, |
31 | int x, int y, int w, int h, CartridgeCM& cart) |
32 | : CartDebugWidget(boss, lfont, nfont, x, y, w, h), |
33 | myCart(cart) |
34 | { |
35 | uInt16 size = 4 * 4096; |
36 | |
37 | string info = |
38 | "CM cartridge, four 4K banks + 2K RAM\n" |
39 | "2K RAM accessible @ $1800 - $1FFF in read or write-only mode " |
40 | "(no separate ports)\n" |
41 | "All TIA controller registers (INPT0-INPT5) and RIOT SWCHA are " |
42 | "used to control the cart functionality\n" |
43 | "Startup bank = 3 (ROM), RAM disabled\n" ; |
44 | |
45 | int xpos = 2, |
46 | ypos = addBaseInformation(size, "CompuMate" , info) + myLineHeight; |
47 | |
48 | VariantList items; |
49 | VarList::push_back(items, " 0 " ); |
50 | VarList::push_back(items, " 1 " ); |
51 | VarList::push_back(items, " 2 " ); |
52 | VarList::push_back(items, " 3 " ); |
53 | myBank = |
54 | new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth(" 0 " ), |
55 | myLineHeight, items, "Set bank " , |
56 | 0, kBankChanged); |
57 | myBank->setTarget(this); |
58 | addFocusWidget(myBank); |
59 | |
60 | // Raw SWCHA value (this will be broken down further in other UI elements) |
61 | int lwidth = _font.getStringWidth("Current column " ); |
62 | ypos += myLineHeight + 8; |
63 | new StaticTextWidget(boss, _font, xpos, ypos+2, lwidth, myFontHeight, |
64 | "Current SWCHA " , TextAlign::Left); |
65 | xpos += lwidth; |
66 | mySWCHA = new ToggleBitWidget(boss, _nfont, xpos, ypos, 8, 1); |
67 | mySWCHA->setTarget(this); |
68 | mySWCHA->setEditable(false); |
69 | |
70 | // Current column number |
71 | xpos = 10; ypos += myLineHeight + 5; |
72 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
73 | myFontHeight, "Current column " , TextAlign::Left); |
74 | xpos += lwidth; |
75 | |
76 | myColumn = new DataGridWidget(boss, _nfont, xpos, ypos-2, 1, 1, 2, 8, Common::Base::F_16); |
77 | myColumn->setTarget(this); |
78 | myColumn->setEditable(false); |
79 | |
80 | // Relevant pins of SWCHA |
81 | xpos = 30; |
82 | |
83 | // D6 (column part) |
84 | ypos += myLineHeight + 8; |
85 | myIncrease = new CheckboxWidget(boss, _font, xpos, ypos, "Increase Column" ); |
86 | myIncrease->setTarget(this); |
87 | myIncrease->setEditable(false); |
88 | |
89 | int orig_ypos = ypos; // save for when we go to the next column |
90 | |
91 | // D5 (column part) |
92 | ypos += myLineHeight + 4; |
93 | myReset = new CheckboxWidget(boss, _font, xpos, ypos, "Reset Column" ); |
94 | myReset->setTarget(this); |
95 | myReset->setEditable(false); |
96 | |
97 | // Row inputs |
98 | ypos += myLineHeight + 4; |
99 | myRow[0] = new CheckboxWidget(boss, _font, xpos, ypos, "Row 0" ); |
100 | myRow[0]->setTarget(this); |
101 | myRow[0]->setEditable(false); |
102 | ypos += myLineHeight + 4; |
103 | myRow[1] = new CheckboxWidget(boss, _font, xpos, ypos, "Row 1" ); |
104 | myRow[1]->setTarget(this); |
105 | myRow[1]->setEditable(false); |
106 | ypos += myLineHeight + 4; |
107 | myRow[2] = new CheckboxWidget(boss, _font, xpos, ypos, "Row 2" ); |
108 | myRow[2]->setTarget(this); |
109 | myRow[2]->setEditable(false); |
110 | ypos += myLineHeight + 4; |
111 | myRow[3] = new CheckboxWidget(boss, _font, xpos, ypos, "Row 3" ); |
112 | myRow[3]->setTarget(this); |
113 | myRow[3]->setEditable(false); |
114 | |
115 | // Func and Shift keys |
116 | ypos += myLineHeight + 4; |
117 | myFunc = new CheckboxWidget(boss, _font, xpos, ypos, "FUNC key pressed" ); |
118 | myFunc->setTarget(this); |
119 | myFunc->setEditable(false); |
120 | ypos += myLineHeight + 4; |
121 | myShift = new CheckboxWidget(boss, _font, xpos, ypos, "Shift key pressed" ); |
122 | myShift->setTarget(this); |
123 | myShift->setEditable(false); |
124 | |
125 | // Move to next column |
126 | xpos += myShift->getWidth() + 20; ypos = orig_ypos; |
127 | |
128 | // D7 |
129 | myAudIn = new CheckboxWidget(boss, _font, xpos, ypos, "Audio Input" ); |
130 | myAudIn->setTarget(this); |
131 | myAudIn->setEditable(false); |
132 | |
133 | // D6 (audio part) |
134 | ypos += myLineHeight + 4; |
135 | myAudOut = new CheckboxWidget(boss, _font, xpos, ypos, "Audio Output" ); |
136 | myAudOut->setTarget(this); |
137 | myAudOut->setEditable(false); |
138 | |
139 | // Ram state (combination of several bits in SWCHA) |
140 | ypos += myLineHeight + 8; |
141 | lwidth = _font.getStringWidth("Ram State " ); |
142 | new StaticTextWidget(boss, _font, xpos, ypos, lwidth, |
143 | myFontHeight, "Ram State " , TextAlign::Left); |
144 | myRAM = new EditTextWidget(boss, _nfont, xpos+lwidth, ypos-2, |
145 | _nfont.getStringWidth(" Write-only " ), myLineHeight, "" ); |
146 | myRAM->setEditable(false, true); |
147 | } |
148 | |
149 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
150 | void CartridgeCMWidget::saveOldState() |
151 | { |
152 | myOldState.swcha = myCart.mySWCHA; |
153 | myOldState.column = myCart.column(); |
154 | |
155 | myOldState.internalram.clear(); |
156 | for(uInt32 i = 0; i < internalRamSize(); ++i) |
157 | myOldState.internalram.push_back(myCart.myRAM[i]); |
158 | |
159 | myOldState.bank = myCart.getBank(); |
160 | } |
161 | |
162 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
163 | void CartridgeCMWidget::loadConfig() |
164 | { |
165 | myBank->setSelectedIndex(myCart.getBank(), myCart.getBank() != myOldState.bank); |
166 | |
167 | RiotDebug& riot = Debugger::debugger().riotDebug(); |
168 | const RiotState& state = static_cast<const RiotState&>(riot.getState()); |
169 | |
170 | uInt8 swcha = myCart.mySWCHA; |
171 | |
172 | // SWCHA |
173 | BoolArray oldbits, newbits, changed; |
174 | Debugger::set_bits(myOldState.swcha, oldbits); |
175 | Debugger::set_bits(swcha, newbits); |
176 | |
177 | for(uInt32 i = 0; i < oldbits.size(); ++i) |
178 | changed.push_back(oldbits[i] != newbits[i]); |
179 | mySWCHA->setState(newbits, changed); |
180 | |
181 | // Column |
182 | myColumn->setList(0, myCart.column(), myCart.column() != myOldState.column); |
183 | |
184 | // Various bits from SWCHA and INPTx |
185 | myIncrease->setState(swcha & 0x40); |
186 | myReset->setState(swcha & 0x20); |
187 | myRow[0]->setState(!(state.INPT4 & 0x80)); |
188 | myRow[1]->setState(!(swcha & 0x04)); |
189 | myRow[2]->setState(!(state.INPT5 & 0x80)); |
190 | myRow[3]->setState(!(swcha & 0x08)); |
191 | myFunc->setState(state.INPT0 & 0x80); |
192 | myShift->setState(state.INPT3 & 0x80); |
193 | |
194 | // Audio in and out (used for communicating with the external cassette) |
195 | myAudIn->setState(swcha & 0x80); |
196 | myAudOut->setState(swcha & 0x40); |
197 | |
198 | // RAM state (several bits from SWCHA) |
199 | const string& ram = (swcha & 0x10) ? " Inactive" : |
200 | (swcha & 0x20) ? " Read-only" : " Write-only" ; |
201 | myRAM->setText(ram, (swcha & 0x30) != (myOldState.swcha & 0x30)); |
202 | |
203 | CartDebugWidget::loadConfig(); |
204 | } |
205 | |
206 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
207 | void CartridgeCMWidget::handleCommand(CommandSender* sender, |
208 | int cmd, int data, int id) |
209 | { |
210 | if(cmd == kBankChanged) |
211 | { |
212 | myCart.unlockBank(); |
213 | myCart.mySWCHA &= 0xFC; |
214 | myCart.mySWCHA |= myBank->getSelected(); |
215 | myCart.bank(myCart.mySWCHA & 0x03); |
216 | myCart.lockBank(); |
217 | invalidate(); |
218 | } |
219 | } |
220 | |
221 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
222 | string CartridgeCMWidget::bankState() |
223 | { |
224 | ostringstream& buf = buffer(); |
225 | |
226 | buf << "Bank = " << std::dec << myCart.getBank() |
227 | << ", RAM is" << ((myCart.mySWCHA & 0x10) ? " Inactive" : |
228 | (myCart.mySWCHA & 0x20) ? " Read-only" : " Write-only" ); |
229 | |
230 | return buf.str(); |
231 | } |
232 | |
233 | |
234 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
235 | uInt32 CartridgeCMWidget::internalRamSize() |
236 | { |
237 | return 2048; |
238 | } |
239 | |
240 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
241 | uInt32 CartridgeCMWidget::internalRamRPort(int start) |
242 | { |
243 | return 0xF800 + start; |
244 | } |
245 | |
246 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
247 | string CartridgeCMWidget::internalRamDescription() |
248 | { |
249 | ostringstream desc; |
250 | desc << "$F800 - $FFFF used for Exclusive Read\n" |
251 | << " or Exclusive Write Access" ; |
252 | |
253 | return desc.str(); |
254 | } |
255 | |
256 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
257 | const ByteArray& CartridgeCMWidget::internalRamOld(int start, int count) |
258 | { |
259 | myRamOld.clear(); |
260 | for(int i = 0; i < count; i++) |
261 | myRamOld.push_back(myOldState.internalram[start + i]); |
262 | return myRamOld; |
263 | } |
264 | |
265 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
266 | const ByteArray& CartridgeCMWidget::internalRamCurrent(int start, int count) |
267 | { |
268 | myRamCurrent.clear(); |
269 | for(int i = 0; i < count; i++) |
270 | myRamCurrent.push_back(myCart.myRAM[start + i]); |
271 | return myRamCurrent; |
272 | } |
273 | |
274 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
275 | void CartridgeCMWidget::internalRamSetValue(int addr, uInt8 value) |
276 | { |
277 | myCart.myRAM[addr] = value; |
278 | } |
279 | |
280 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
281 | uInt8 CartridgeCMWidget::internalRamGetValue(int addr) |
282 | { |
283 | return myCart.myRAM[addr]; |
284 | } |
285 | |
286 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
287 | string CartridgeCMWidget::internalRamLabel(int addr) |
288 | { |
289 | CartDebug& dbg = instance().debugger().cartDebug(); |
290 | return dbg.getLabel(addr + 0xF800, false); |
291 | } |
292 | |