1
//============================================================================
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
11
// Copyright (c) 1995-1999 by Bradford W. Mott
13
// See the file "license" for information on usage and redistribution of
14
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
// $Id: CartAR.cxx,v 1.5 1999/02/22 16:30:30 bwmott Exp $
17
//============================================================================
22
#include "M6502Hi.hxx"
26
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
27
CartridgeAR::CartridgeAR(const uInt8* image, uInt32 size)
32
// Create a load image buffer and copy the given image
33
myLoadImages = new uInt8[size];
34
myNumberOfLoadImages = size / 8448;
35
memcpy(myLoadImages, image, size);
37
// Initialize RAM with random values
39
for(i = 0; i < 6 * 1024; ++i)
41
myImage[i] = random.next();
44
// Initialize ROM with an invalid 6502 opcode
45
for(i = 6 * 1024; i < 8 * 1024; ++i)
51
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
52
CartridgeAR::~CartridgeAR()
54
delete[] myLoadImages;
57
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58
const char* CartridgeAR::name() const
63
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
64
void CartridgeAR::reset()
66
// Try to load the first load into RAM upon reset
71
myWriteEnabled = false;
74
myNumberOfDistinctAccesses = 0;
75
myWritePending = false;
77
// Set bank configuration upon reset so ROM is selected
78
myImageOffset[0] = 0 * 2048;
79
myImageOffset[1] = 3 * 2048;
82
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
83
void CartridgeAR::systemCyclesReset()
85
// Get the current system cycle
86
uInt32 cycles = mySystem->cycles();
88
// Adjust cycle values
89
myPowerRomCycle -= cycles;
92
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
93
void CartridgeAR::install(System& system)
96
uInt16 shift = mySystem->pageShift();
97
uInt16 mask = mySystem->pageMask();
99
my6502 = &(M6502High&)mySystem->m6502();
101
// Make sure the system we're being installed in has a page size that'll work
102
assert((0x1000 & mask) == 0);
104
System::PageAccess access;
105
for(uInt32 i = 0x1000; i < 0x2000; i += (1 << shift))
107
access.directPeekBase = 0;
108
access.directPokeBase = 0;
109
access.device = this;
110
mySystem->setPageAccess(i >> shift, access);
113
bankConfiguration(0);
116
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117
uInt8 CartridgeAR::peek(uInt16 addr)
119
// Check to see if the Supercharger ROM is being accessed?
120
if(myImageOffset[1] == 3 * 2048)
122
Int32 cycles = mySystem->cycles();
124
// Is the tape rewind routine being accessed?
125
if((addr & 0x1FFF) == 0x180A)
127
// See if the ROM has been powered up long enough
128
if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles)))
130
cerr << "ERROR: Supercharger ROM has not been powered up!\n";
134
cerr << "ERROR: Supercharger code doesn't handle rewinding tape!\n";
137
// Is the multiload routine being accessed?
138
else if((addr & 0x1FFF) == 0x1800)
140
// See if the ROM has been powered up long enough
141
if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles)))
143
cerr << "ERROR: Supercharger ROM has not been powered up!\n";
147
// Get the load they're trying to access
148
uInt8 load = mySystem->peek(0x00FA);
150
// Load the specified load into RAM
153
return myImage[(addr & 0x07FF) + myImageOffset[1]];
158
// Are the "value" registers being accessed?
159
if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending))
162
myNumberOfDistinctAccesses = my6502->distinctAccesses();
163
myWritePending = true;
165
// Is the bank configuration hotspot being accessed?
166
else if((addr & 0x1FFF) == 0x1FF8)
168
// Yes, so handle bank configuration
169
myWritePending = false;
170
bankConfiguration(myLastAccess);
172
// Handle poke if writing enabled
173
else if(myWriteEnabled && myWritePending)
175
if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5)
177
if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5)
179
if((addr & 0x0800) == 0)
180
myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess;
181
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
182
myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess;
184
myWritePending = false;
188
return myImage[(addr & 0x07FF) + myImageOffset[(addr & 0x0800) ? 1 : 0]];
191
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
192
void CartridgeAR::poke(uInt16 addr, uInt8)
194
// Are the "value" registers being accessed?
195
if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending))
198
myNumberOfDistinctAccesses = my6502->distinctAccesses();
199
myWritePending = true;
201
// Is the bank configuration hotspot being accessed?
202
else if((addr & 0x1FFF) == 0x1FF8)
204
// Yes, so handle bank configuration
205
myWritePending = false;
206
bankConfiguration(myLastAccess);
208
// Handle poke if writing enabled
209
else if(myWriteEnabled && myWritePending)
211
if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5)
213
if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5)
215
if((addr & 0x0800) == 0)
216
myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess;
217
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
218
myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess;
220
myWritePending = false;
225
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
226
void CartridgeAR::bankConfiguration(uInt8 configuration)
228
// D7-D5 of this byte: Write Pulse Delay (n/a for emulator)
230
// D4-D0: RAM/ROM configuration:
231
// $F000-F7FF $F800-FFFF Address range that banks map into
234
// 010wp 2 0 as used in Commie Mutants and many others
235
// 011wp 0 2 as used in Suicide Mission
238
// 110wp 2 1 as used in Killer Satellites
239
// 111wp 1 2 as we use for 2k/4k ROM cloning
241
// w = Write Enable (1 = enabled; accesses to $F000-$F0FF cause writes
242
// to happen. 0 = disabled, and the cart acts like ROM.)
243
// p = ROM Power (0 = enabled, 1 = off.) Only power the ROM if you're
244
// wanting to access the ROM for multiloads. Otherwise set to 1.
246
// Handle ROM power configuration
247
myPower = !(configuration & 0x01);
251
myPowerRomCycle = mySystem->cycles();
254
myWriteEnabled = configuration & 0x02;
256
switch((configuration >> 2) & 0x07)
260
myImageOffset[0] = 2 * 2048;
261
myImageOffset[1] = 3 * 2048;
267
myImageOffset[0] = 0 * 2048;
268
myImageOffset[1] = 3 * 2048;
274
myImageOffset[0] = 2 * 2048;
275
myImageOffset[1] = 0 * 2048;
281
myImageOffset[0] = 0 * 2048;
282
myImageOffset[1] = 2 * 2048;
288
myImageOffset[0] = 2 * 2048;
289
myImageOffset[1] = 3 * 2048;
295
myImageOffset[0] = 1 * 2048;
296
myImageOffset[1] = 3 * 2048;
302
myImageOffset[0] = 2 * 2048;
303
myImageOffset[1] = 1 * 2048;
309
myImageOffset[0] = 1 * 2048;
310
myImageOffset[1] = 2 * 2048;
316
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
317
void CartridgeAR::setupROM()
319
static uInt8 dummyROMCode[] = {
320
0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0,
321
0x80, 0xd0, 0xf9, 0x4c, 0x2b, 0xfa, 0xff, 0xff,
322
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
323
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
324
0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0,
325
0x1e, 0xd0, 0xf9, 0xa2, 0x0, 0xbd, 0x45, 0xfa,
326
0x95, 0xfa, 0xe8, 0xe0, 0x6, 0xd0, 0xf6, 0xa2,
327
0xff, 0xa0, 0x0, 0xa9, 0x0, 0x85, 0x80, 0xcd,
328
0x0, 0xf0, 0x4c, 0xfa, 0x0, 0xad, 0xf8, 0xff,
332
int size = sizeof(dummyROMCode);
334
// Copy the "dummy" ROM code into the ROM area
335
for(int i = 0; i < size; ++i)
337
myImage[0x1A00 + i] = dummyROMCode[i];
340
// Put a JMP $FA20 at multiload entry point ($F800)
341
myImage[0x1800] = 0x4C;
342
myImage[0x1801] = 0x20;
343
myImage[0x1802] = 0xFA;
345
// Update ROM code to have the correct reset address and bank configuration
346
myImage[0x1A00 + size - 2] = myHeader[0];
347
myImage[0x1A00 + size - 1] = myHeader[1];
348
myImage[0x1A00 + size - 11] = myHeader[2];
349
myImage[0x1A00 + size - 15] = myHeader[2];
351
// Finally set 6502 vectors to point to this "dummy" code at 0xFA00
352
myImage[3 * 2048 + 2044] = 0x00;
353
myImage[3 * 2048 + 2045] = 0xFA;
354
myImage[3 * 2048 + 2046] = 0x00;
355
myImage[3 * 2048 + 2047] = 0xFA;
358
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
359
uInt8 CartridgeAR::checksum(uInt8* s, uInt16 length)
363
for(uInt32 i = 0; i < length; ++i)
371
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
372
void CartridgeAR::loadIntoRAM(uInt8 load)
376
// Scan through all of the loads to see if we find the one we're looking for
377
for(image = 0; image < myNumberOfLoadImages; ++image)
379
// Is this the correct load?
380
if(myLoadImages[(image * 8448) + 8192 + 5] == load)
382
// Copy the load's header
383
memcpy(myHeader, myLoadImages + (image * 8448) + 8192, 256);
385
// Verify the load's header
386
if(checksum(myHeader, 8) != 0x55)
388
cerr << "WARNING: The Supercharger header checksum is invalid...\n";
391
// Load all of the pages from the load
392
bool invalidPageChecksumSeen = false;
393
for(uInt32 j = 0; j < myHeader[3]; ++j)
395
uInt32 bank = myHeader[16 + j] & 0x03;
396
uInt32 page = (myHeader[16 + j] >> 2) & 0x07;
397
uInt8* src = myLoadImages + (image * 8448) + (j * 256);
398
uInt8 sum = checksum(src, 256) + myHeader[16 + j] + myHeader[64 + j];
400
if(!invalidPageChecksumSeen && (sum != 0x55))
402
cerr << "WARNING: Some Supercharger page checksums are invalid...\n";
403
invalidPageChecksumSeen = true;
406
// Copy page to Supercharger RAM
407
memcpy(myImage + (bank * 2048) + (page * 256), src, 256);
410
// Make sure the "dummy" ROM is installed
416
// TODO: Should probably switch to an internal ROM routine to display
417
// this message to the user...
418
cerr << "ERROR: Supercharger load is missing from ROM image...\n";