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 "TIA.hxx"
20#include "CartCVPlus.hxx"
21
22// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
23CartridgeCVPlus::CartridgeCVPlus(const ByteBuffer& image, size_t size,
24 const string& md5, const Settings& settings)
25 : Cartridge(settings, md5),
26 mySize(size),
27 myCurrentBank(0)
28{
29 // Allocate array for the ROM image
30 myImage = make_unique<uInt8[]>(mySize);
31
32 // Copy the ROM image into my buffer
33 std::copy_n(image.get(), mySize, myImage.get());
34 createCodeAccessBase(mySize + myRAM.size());
35}
36
37// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38void CartridgeCVPlus::reset()
39{
40 initializeRAM(myRAM.data(), myRAM.size());
41 initializeStartBank(0);
42
43 // We'll map the startup bank into the first segment upon reset
44 bank(startBank());
45}
46
47// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48void CartridgeCVPlus::install(System& system)
49{
50 mySystem = &system;
51
52 System::PageAccess access(this, System::PageAccessType::READWRITE);
53
54 // The hotspot ($3D) is in TIA address space, so we claim it here
55 for(uInt16 addr = 0x00; addr < 0x40; addr += System::PAGE_SIZE)
56 mySystem->setPageAccess(addr, access);
57
58 // Set the page accessing method for the RAM writing pages
59 // Map access to this class, since we need to inspect all accesses to
60 // check if RWP happens
61 access.directPeekBase = access.directPokeBase = nullptr;
62 access.codeAccessBase = nullptr;
63 access.type = System::PageAccessType::WRITE;
64 for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE)
65 {
66 access.codeAccessBase = &myCodeAccessBase[mySize + (addr & 0x03FF)];
67 mySystem->setPageAccess(addr, access);
68 }
69
70 // Set the page accessing method for the RAM reading pages
71 access.type = System::PageAccessType::READ;
72 for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE)
73 {
74 access.directPeekBase = &myRAM[addr & 0x03FF];
75 access.codeAccessBase = &myCodeAccessBase[mySize + (addr & 0x03FF)];
76 mySystem->setPageAccess(addr, access);
77 }
78
79 // Install pages for the startup bank into the first segment
80 bank(startBank());
81}
82
83// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
84uInt8 CartridgeCVPlus::peek(uInt16 address)
85{
86 if((address & 0x0FFF) < 0x0800) // Write port is at 0xF400 - 0xF7FF (1024 bytes)
87 return peekRAM(myRAM[address & 0x03FF], address);
88 else
89 return myImage[(address & 0x07FF) + (myCurrentBank << 11)];
90}
91
92// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
93bool CartridgeCVPlus::poke(uInt16 address, uInt8 value)
94{
95 uInt16 pokeAddress = address;
96 address &= 0x0FFF;
97
98 if(address < 0x0040)
99 {
100 // Switch banks if necessary
101 if(address == 0x003D)
102 bank(value);
103
104 // Handle TIA space that we claimed above
105 return mySystem->tia().poke(address, value);
106 }
107 else
108 pokeRAM(myRAM[address & 0x03FF], pokeAddress, value);
109
110 return true;
111}
112
113// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114bool CartridgeCVPlus::bank(uInt16 bank)
115{
116 if(bankLocked()) return false;
117
118 // Make sure the bank they're asking for is reasonable
119 if((uInt32(bank) << 11) < mySize)
120 {
121 myCurrentBank = bank;
122 }
123 else
124 {
125 // Oops, the bank they're asking for isn't valid so let's wrap it
126 // around to a valid bank number
127 myCurrentBank = bank % (mySize >> 11);
128 }
129
130 uInt32 offset = myCurrentBank << 11;
131
132 // Setup the page access methods for the current bank
133 System::PageAccess access(this, System::PageAccessType::READ);
134
135 // Map ROM image into the system
136 for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE)
137 {
138 access.directPeekBase = &myImage[offset + (addr & 0x07FF)];
139 access.codeAccessBase = &myCodeAccessBase[offset + (addr & 0x07FF)];
140 mySystem->setPageAccess(addr, access);
141 }
142
143 return myBankChanged = true;
144}
145
146// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147uInt16 CartridgeCVPlus::getBank(uInt16) const
148{
149 return myCurrentBank;
150}
151
152// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
153uInt16 CartridgeCVPlus::bankCount() const
154{
155 return uInt16(mySize >> 11);
156}
157
158// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159bool CartridgeCVPlus::patch(uInt16 address, uInt8 value)
160{
161 address &= 0x0FFF;
162
163 if(address < 0x0800)
164 {
165 // Normally, a write to the read port won't do anything
166 // However, the patch command is special in that ignores such
167 // cart restrictions
168 // The following will work for both reads and writes
169 myRAM[address & 0x03FF] = value;
170 }
171 else
172 myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value;
173
174 return myBankChanged = true;
175}
176
177// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178const uInt8* CartridgeCVPlus::getImage(size_t& size) const
179{
180 size = mySize;
181 return myImage.get();
182}
183
184// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
185bool CartridgeCVPlus::save(Serializer& out) const
186{
187 try
188 {
189 out.putShort(myCurrentBank);
190 out.putByteArray(myRAM.data(), myRAM.size());
191 }
192 catch(...)
193 {
194 cerr << "ERROR: CartridgeCVPlus::save" << endl;
195 return false;
196 }
197
198 return true;
199}
200
201// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
202bool CartridgeCVPlus::load(Serializer& in)
203{
204 try
205 {
206 myCurrentBank = in.getShort();
207 in.getByteArray(myRAM.data(), myRAM.size());
208 }
209 catch(...)
210 {
211 cerr << "ERROR: CartridgeCVPlus::load" << endl;
212 return false;
213 }
214
215 // Now, go to the current bank
216 bank(myCurrentBank);
217
218 return true;
219}
220