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 "System.hxx"
19#include "CartMNetwork.hxx"
20
21// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22CartridgeMNetwork::CartridgeMNetwork(const ByteBuffer& image, size_t size,
23 const string& md5, const Settings& settings)
24 : Cartridge(settings, md5),
25 mySize(size),
26 myCurrentRAM(0),
27 myRAMSlice(0)
28{
29}
30
31// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32void CartridgeMNetwork::initialize(const ByteBuffer& image, size_t size)
33{
34 // Allocate array for the ROM image
35 myImage = make_unique<uInt8[]>(size);
36
37 // Copy the ROM image into my buffer
38 std::copy_n(image.get(), std::min<size_t>(romSize(), size), myImage.get());
39 createCodeAccessBase(romSize() + myRAM.size());
40
41 myRAMSlice = bankCount() - 1;
42}
43
44// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45void CartridgeMNetwork::reset()
46{
47 initializeRAM(myRAM.data(), myRAM.size());
48
49 initializeStartBank(0);
50 uInt32 ramBank = randomStartBank() ?
51 mySystem->randGenerator().next() % 4 : 0;
52
53 // Install some default banks for the RAM and first segment
54 bankRAM(ramBank);
55 bank(startBank());
56
57 myBankChanged = true;
58}
59
60// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
61void CartridgeMNetwork::setAccess(uInt16 addrFrom, uInt16 size,
62 uInt16 directOffset, uInt8* directData, uInt16 codeOffset,
63 System::PageAccessType type, uInt16 addrMask)
64{
65 if(addrMask == 0)
66 addrMask = size - 1;
67 System::PageAccess access(this, type);
68
69 for(uInt16 addr = addrFrom; addr < addrFrom + size; addr += System::PAGE_SIZE)
70 {
71 if(type == System::PageAccessType::READ)
72 access.directPeekBase = &directData[directOffset + (addr & addrMask)];
73 else if(type == System::PageAccessType::WRITE) // all RAM writes mapped to ::poke()
74 access.directPokeBase = nullptr;
75 access.codeAccessBase = &myCodeAccessBase[codeOffset + (addr & addrMask)];
76 mySystem->setPageAccess(addr, access);
77 }
78}
79
80// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81void CartridgeMNetwork::install(System& system)
82{
83 mySystem = &system;
84
85 System::PageAccess access(this, System::PageAccessType::READ);
86
87 // Set the page accessing methods for the hot spots
88 for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000;
89 addr += System::PAGE_SIZE)
90 {
91 access.codeAccessBase = &myCodeAccessBase[0x1fc0];
92 mySystem->setPageAccess(addr, access);
93 }
94 /*setAccess(0x1FE0 & ~System::PAGE_MASK, System::PAGE_SIZE,
95 0, nullptr, 0x1fc0, System::PA_NONE, 0x1fc0);*/
96
97 // Setup the second segment to always point to the last ROM slice
98 setAccess(0x1A00, 0x1FE0U & (~System::PAGE_MASK - 0x1A00),
99 myRAMSlice * BANK_SIZE, myImage.get(), myRAMSlice * BANK_SIZE, System::PageAccessType::READ, BANK_SIZE - 1);
100 myCurrentSlice[1] = myRAMSlice;
101
102 // Install some default banks for the RAM and first segment
103 bankRAM(0);
104 bank(startBank());
105}
106
107// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
108uInt8 CartridgeMNetwork::peek(uInt16 address)
109{
110 uInt16 peekAddress = address;
111 address &= 0x0FFF;
112
113 // Switch banks if necessary
114 checkSwitchBank(address);
115
116 if((myCurrentSlice[0] == myRAMSlice) && (address < BANK_SIZE / 2))
117 {
118 // Reading from the 1K write port @ $1000 triggers an unwanted write
119 return peekRAM(myRAM[address & (BANK_SIZE / 2 - 1)], peekAddress);
120 }
121 else if((address >= 0x0800) && (address <= 0x08FF))
122 {
123 // Reading from the 256B write port @ $1800 triggers an unwanted write
124 return peekRAM(myRAM[0x0400 + (myCurrentRAM << 8) + (address & 0x00FF)], peekAddress);
125 }
126 else
127 return myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE - 1))];
128}
129
130// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
131bool CartridgeMNetwork::poke(uInt16 address, uInt8 value)
132{
133 uInt16 pokeAddress = address;
134 address &= 0x0FFF;
135
136 // Switch banks if necessary
137 checkSwitchBank(address);
138
139 // All RAM writes are mapped here
140 if((myCurrentSlice[0] == myRAMSlice) && (address < BANK_SIZE / 2))
141 {
142 pokeRAM(myRAM[address & (BANK_SIZE / 2 - 1)], pokeAddress, value);
143 return true;
144 }
145 else if((address >= 0x0800) && (address <= 0x08FF))
146 {
147 pokeRAM(myRAM[0x0400 + (myCurrentRAM << 8) + (address & 0x00FF)], pokeAddress, value);
148 return true;
149 }
150
151 return false;
152}
153
154// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155void CartridgeMNetwork::bankRAM(uInt16 bank)
156{
157 if(bankLocked()) return;
158
159 // Remember what bank we're in
160 myCurrentRAM = bank;
161 uInt16 offset = bank << 8; // * RAM_SLICE_SIZE (256)
162
163 // Setup the page access methods for the current bank
164 // Set the page accessing method for the 256 bytes of RAM reading pages
165 setAccess(0x1800, 0x100, 0x0400 + offset, myRAM.data(), romSize() + BANK_SIZE / 2, System::PageAccessType::WRITE);
166 // Set the page accessing method for the 256 bytes of RAM reading pages
167 setAccess(0x1900, 0x100, 0x0400 + offset, myRAM.data(), romSize() + BANK_SIZE / 2, System::PageAccessType::READ);
168
169 myBankChanged = true;
170}
171
172// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173bool CartridgeMNetwork::bank(uInt16 slice)
174{
175 if(bankLocked()) return false;
176
177 // Remember what bank we're in
178 myCurrentSlice[0] = slice;
179
180 // Setup the page access methods for the current bank
181 if(slice != myRAMSlice)
182 {
183 uInt16 offset = slice << 11; // * BANK_SIZE (2048)
184
185 // Map ROM image into first segment
186 setAccess(0x1000, BANK_SIZE, offset, myImage.get(), offset, System::PageAccessType::READ);
187 }
188 else
189 {
190 // Set the page accessing method for the 1K slice of RAM writing pages
191 setAccess(0x1000, BANK_SIZE / 2, 0, myRAM.data(), romSize(), System::PageAccessType::WRITE);
192 // Set the page accessing method for the 1K slice of RAM reading pages
193 setAccess(0x1000 + BANK_SIZE / 2, BANK_SIZE / 2, 0, myRAM.data(), romSize(), System::PageAccessType::READ);
194 }
195 return myBankChanged = true;
196}
197
198// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
199uInt16 CartridgeMNetwork::getBank(uInt16 address) const
200{
201 return myCurrentSlice[(address & 0xFFF) >> 11]; // 2K slices
202}
203
204// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
205bool CartridgeMNetwork::patch(uInt16 address, uInt8 value)
206{
207 address = address & 0x0FFF;
208
209 if(address < 0x0800)
210 {
211 if(myCurrentSlice[0] == myRAMSlice)
212 {
213 // Normally, a write to the read port won't do anything
214 // However, the patch command is special in that ignores such
215 // cart restrictions
216 myRAM[address & 0x03FF] = value;
217 }
218 else
219 myImage[(myCurrentSlice[0] << 11) + (address & (BANK_SIZE-1))] = value;
220 }
221 else if(address < 0x0900)
222 {
223 // Normally, a write to the read port won't do anything
224 // However, the patch command is special in that ignores such
225 // cart restrictions
226 myRAM[0x0400 + (myCurrentRAM << 8) + (address & 0x00FF)] = value;
227 }
228 else
229 myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE-1))] = value;
230
231 return myBankChanged = true;
232}
233
234// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
235const uInt8* CartridgeMNetwork::getImage(size_t& size) const
236{
237 size = bankCount() * BANK_SIZE;
238 return myImage.get();
239}
240
241// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
242bool CartridgeMNetwork::save(Serializer& out) const
243{
244 try
245 {
246 out.putShortArray(myCurrentSlice.data(), myCurrentSlice.size());
247 out.putShort(myCurrentRAM);
248 out.putByteArray(myRAM.data(), myRAM.size());
249 }
250 catch(...)
251 {
252 cerr << "ERROR: " << name() << "::save" << endl;
253 return false;
254 }
255
256 return true;
257}
258
259// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
260bool CartridgeMNetwork::load(Serializer& in)
261{
262 try
263 {
264 in.getShortArray(myCurrentSlice.data(), myCurrentSlice.size());
265 myCurrentRAM = in.getShort();
266 in.getByteArray(myRAM.data(), myRAM.size());
267 }
268 catch(...)
269 {
270 cerr << "ERROR: " << name() << "::load" << endl;
271 return false;
272 }
273
274 // Set up the previously used banks for the RAM and segment
275 bankRAM(myCurrentRAM);
276 bank(myCurrentSlice[0]);
277
278 return true;
279}
280
281// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
282uInt16 CartridgeMNetwork::bankCount() const
283{
284 return uInt16(mySize >> 11);
285}
286
287// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
288uInt16 CartridgeMNetwork::romSize() const
289{
290 return bankCount() * BANK_SIZE;
291}
292