| 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 24 | CartridgeDASHWidget::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 121 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 130 | void CartridgeDASHWidget::loadConfig() |
| 131 | { |
| 132 | updateUIState(); |
| 133 | CartDebugWidget::loadConfig(); |
| 134 | } |
| 135 | |
| 136 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 137 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 178 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 243 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 314 | uInt32 CartridgeDASHWidget::internalRamSize() |
| 315 | { |
| 316 | return 32*1024; |
| 317 | } |
| 318 | |
| 319 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 320 | uInt32 CartridgeDASHWidget::internalRamRPort(int start) |
| 321 | { |
| 322 | return 0x0000 + start; |
| 323 | } |
| 324 | |
| 325 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 326 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 337 | const 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 346 | const 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 355 | void CartridgeDASHWidget::internalRamSetValue(int addr, uInt8 value) |
| 356 | { |
| 357 | myCart.myRAM[addr] = value; |
| 358 | } |
| 359 | |
| 360 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 361 | uInt8 CartridgeDASHWidget::internalRamGetValue(int addr) |
| 362 | { |
| 363 | return myCart.myRAM[addr]; |
| 364 | } |
| 365 | |
| 366 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| 367 | const CartridgeDASHWidget::BankID CartridgeDASHWidget::bankEnum[4] = { |
| 368 | kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed |
| 369 | }; |
| 370 | |