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 "Settings.hxx"
19#include "System.hxx"
20#include "MD5.hxx"
21#ifdef DEBUGGER_SUPPORT
22 #include "Debugger.hxx"
23 #include "CartDebug.hxx"
24#endif
25
26#include "Cart.hxx"
27
28// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29Cartridge::Cartridge(const Settings& settings, const string& md5)
30 : mySettings(settings),
31 myBankChanged(true),
32 myCodeAccessBase(nullptr),
33 myRamWriteAccess(0),
34 myStartBank(0),
35 myBankLocked(false)
36{
37 auto to_uInt32 = [](const string& s, uInt32 pos) {
38 return uInt32(std::stoul(s.substr(pos, 8), nullptr, 16));
39 };
40
41 uInt32 seed = to_uInt32(md5, 0) ^ to_uInt32(md5, 8) ^
42 to_uInt32(md5, 16) ^ to_uInt32(md5, 24);
43 Random rand(seed);
44 for(uInt32 i = 0; i < 256; ++i)
45 myRWPRandomValues[i] = rand.next();
46
47 myRAMAccesses.reserve(5);
48}
49
50// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
51void Cartridge::setAbout(const string& about, const string& type,
52 const string& id)
53{
54 myAbout = about;
55 myDetectedType = type;
56 myMultiCartID = id;
57}
58
59// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
60bool Cartridge::saveROM(ofstream& out) const
61{
62 size_t size = 0;
63
64 const uInt8* image = getImage(size);
65 if(image == nullptr || size == 0)
66 {
67 cerr << "save not supported" << endl;
68 return false;
69 }
70
71 out.write(reinterpret_cast<const char*>(image), size);
72
73 return true;
74}
75
76// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
77bool Cartridge::bankChanged()
78{
79 bool changed = myBankChanged;
80 myBankChanged = false;
81 return changed;
82}
83
84// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85uInt8 Cartridge::peekRAM(uInt8& dest, uInt16 address)
86{
87 uInt8 value = myRWPRandomValues[address & 0xFF];
88
89 // Reading from the write port triggers an unwanted write
90 // But this only happens when in normal emulation mode
91#ifdef DEBUGGER_SUPPORT
92 if(!bankLocked() && !mySystem->autodetectMode())
93 {
94 // Record access here; final determination will happen in ::pokeRAM()
95 myRAMAccesses.push_back(address);
96 dest = value;
97 }
98#else
99 if(!mySystem->autodetectMode())
100 dest = value;
101#endif
102 return value;
103}
104
105// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106void Cartridge::pokeRAM(uInt8& dest, uInt16 address, uInt8 value)
107{
108#ifdef DEBUGGER_SUPPORT
109 for(auto i = myRAMAccesses.begin(); i != myRAMAccesses.end(); ++i)
110 {
111 if(*i == address)
112 {
113 myRAMAccesses.erase(i);
114 break;
115 }
116 }
117#endif
118 dest = value;
119}
120
121// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
122void Cartridge::createCodeAccessBase(size_t size)
123{
124#ifdef DEBUGGER_SUPPORT
125 myCodeAccessBase = make_unique<uInt8[]>(size);
126 std::fill_n(myCodeAccessBase.get(), size, CartDebug::ROW);
127#else
128 myCodeAccessBase = nullptr;
129#endif
130}
131
132// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133void Cartridge::initializeRAM(uInt8* arr, size_t size, uInt8 val) const
134{
135 if(randomInitialRAM())
136 for(size_t i = 0; i < size; ++i)
137 arr[i] = mySystem->randGenerator().next();
138 else
139 std::fill_n(arr, size, val);
140}
141
142// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
143uInt16 Cartridge::initializeStartBank(uInt16 defaultBank)
144{
145 int propsBank = myStartBankFromPropsFunc();
146
147 if(randomStartBank())
148 return myStartBank = mySystem->randGenerator().next() % bankCount();
149 else if(propsBank >= 0)
150 return myStartBank = BSPF::clamp(propsBank, 0, bankCount() - 1);
151 else
152 return myStartBank = BSPF::clamp(int(defaultBank), 0, bankCount() - 1);
153}
154
155// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156bool Cartridge::randomInitialRAM() const
157{
158 return mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.ramrandom" : "plr.ramrandom");
159}
160
161// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162bool Cartridge::randomStartBank() const
163{
164 return mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.bankrandom" : "plr.bankrandom");
165}
166