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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
29 | Cartridge::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
51 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
60 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
77 | bool Cartridge::bankChanged() |
78 | { |
79 | bool changed = myBankChanged; |
80 | myBankChanged = false; |
81 | return changed; |
82 | } |
83 | |
84 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
85 | uInt8 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
106 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
122 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
133 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
143 | uInt16 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
156 | bool Cartridge::randomInitialRAM() const |
157 | { |
158 | return mySettings.getBool(mySettings.getBool("dev.settings" ) ? "dev.ramrandom" : "plr.ramrandom" ); |
159 | } |
160 | |
161 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
162 | bool Cartridge::randomStartBank() const |
163 | { |
164 | return mySettings.getBool(mySettings.getBool("dev.settings" ) ? "dev.bankrandom" : "plr.bankrandom" ); |
165 | } |
166 | |