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 "CartFA.hxx"
20
21// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22CartridgeFA::CartridgeFA(const ByteBuffer& image, size_t size,
23 const string& md5, const Settings& settings)
24 : Cartridge(settings, md5),
25 myBankOffset(0)
26{
27 // Copy the ROM image into my buffer
28 std::copy_n(image.get(), std::min(myImage.size(), size), myImage.begin());
29 createCodeAccessBase(myImage.size());
30}
31
32// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33void CartridgeFA::reset()
34{
35 initializeRAM(myRAM.data(), myRAM.size());
36 initializeStartBank(2);
37
38 // Upon reset we switch to the startup bank
39 bank(startBank());
40}
41
42// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
43void CartridgeFA::install(System& system)
44{
45 mySystem = &system;
46
47 System::PageAccess access(this, System::PageAccessType::READ);
48
49 // Set the page accessing method for the RAM writing pages
50 // Map access to this class, since we need to inspect all accesses to
51 // check if RWP happens
52 access.type = System::PageAccessType::WRITE;
53 for(uInt16 addr = 0x1000; addr < 0x1100; addr += System::PAGE_SIZE)
54 {
55 access.codeAccessBase = &myCodeAccessBase[addr & 0x00FF];
56 mySystem->setPageAccess(addr, access);
57 }
58
59 // Set the page accessing method for the RAM reading pages
60 access.type = System::PageAccessType::READ;
61 for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE)
62 {
63 access.directPeekBase = &myRAM[addr & 0x00FF];
64 access.codeAccessBase = &myCodeAccessBase[0x100 + (addr & 0x00FF)];
65 mySystem->setPageAccess(addr, access);
66 }
67
68 // Install pages for the startup bank
69 bank(startBank());
70}
71
72// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73uInt8 CartridgeFA::peek(uInt16 address)
74{
75 uInt16 peekAddress = address;
76 address &= 0x0FFF;
77
78 // Switch banks if necessary
79 switch(address)
80 {
81 case 0x0FF8:
82 // Set the current bank to the lower 4k bank
83 bank(0);
84 break;
85
86 case 0x0FF9:
87 // Set the current bank to the middle 4k bank
88 bank(1);
89 break;
90
91 case 0x0FFA:
92 // Set the current bank to the upper 4k bank
93 bank(2);
94 break;
95
96 default:
97 break;
98 }
99
100 if(address < 0x0100) // Write port is at 0xF000 - 0xF0FF (256 bytes)
101 return peekRAM(myRAM[address], peekAddress);
102 else
103 return myImage[myBankOffset + address];
104}
105
106// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107bool CartridgeFA::poke(uInt16 address, uInt8 value)
108{
109 // Switch banks if necessary
110 switch(address & 0x0FFF)
111 {
112 case 0x0FF8:
113 // Set the current bank to the lower 4k bank
114 bank(0);
115 return false;
116
117 case 0x0FF9:
118 // Set the current bank to the middle 4k bank
119 bank(1);
120 return false;
121
122 case 0x0FFA:
123 // Set the current bank to the upper 4k bank
124 bank(2);
125 return false;
126
127 default:
128 break;
129 }
130
131 if (!(address & 0x100))
132 {
133 pokeRAM(myRAM[address & 0x00FF], address, value);
134 return true;
135 }
136 else
137 {
138 // Writing to the read port should be ignored, but trigger a break if option enabled
139 uInt8 dummy;
140
141 pokeRAM(dummy, address, value);
142 myRamWriteAccess = address;
143 return false;
144 }
145}
146
147// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
148bool CartridgeFA::bank(uInt16 bank)
149{
150 if(bankLocked()) return false;
151
152 // Remember what bank we're in
153 myBankOffset = bank << 12;
154
155 System::PageAccess access(this, System::PageAccessType::READ);
156
157 // Set the page accessing methods for the hot spots
158 for(uInt16 addr = (0x1FF8 & ~System::PAGE_MASK); addr < 0x2000;
159 addr += System::PAGE_SIZE)
160 {
161 access.codeAccessBase = &myCodeAccessBase[myBankOffset + (addr & 0x0FFF)];
162 mySystem->setPageAccess(addr, access);
163 }
164
165 // Setup the page access methods for the current bank
166 for(uInt16 addr = 0x1200; addr < (0x1FF8U & ~System::PAGE_MASK);
167 addr += System::PAGE_SIZE)
168 {
169 access.directPeekBase = &myImage[myBankOffset + (addr & 0x0FFF)];
170 access.codeAccessBase = &myCodeAccessBase[myBankOffset + (addr & 0x0FFF)];
171 mySystem->setPageAccess(addr, access);
172 }
173 return myBankChanged = true;
174}
175
176// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177uInt16 CartridgeFA::getBank(uInt16) const
178{
179 return myBankOffset >> 12;
180}
181
182// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
183uInt16 CartridgeFA::bankCount() const
184{
185 return 3;
186}
187
188// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
189bool CartridgeFA::patch(uInt16 address, uInt8 value)
190{
191 address &= 0x0FFF;
192
193 if(address < 0x0200)
194 {
195 // Normally, a write to the read port won't do anything
196 // However, the patch command is special in that ignores such
197 // cart restrictions
198 myRAM[address & 0x00FF] = value;
199 }
200 else
201 myImage[myBankOffset + address] = value;
202
203 return myBankChanged = true;
204}
205
206// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
207const uInt8* CartridgeFA::getImage(size_t& size) const
208{
209 size = myImage.size();
210 return myImage.data();
211}
212
213// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
214bool CartridgeFA::save(Serializer& out) const
215{
216 try
217 {
218 out.putShort(myBankOffset);
219 out.putByteArray(myRAM.data(), myRAM.size());
220 }
221 catch(...)
222 {
223 cerr << "ERROR: CartridgeFA::save" << endl;
224 return false;
225 }
226
227 return true;
228}
229
230// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231bool CartridgeFA::load(Serializer& in)
232{
233 try
234 {
235 myBankOffset = in.getShort();
236 in.getByteArray(myRAM.data(), myRAM.size());
237 }
238 catch(...)
239 {
240 cerr << "ERROR: CartridgeFA::load" << endl;
241 return false;
242 }
243
244 // Remember what bank we were in
245 bank(myBankOffset >> 12);
246
247 return true;
248}
249