mirror of https://github.com/stella-emu/stella.git
Updated CompuMate emulation. This bankswitching side of things seems to be
working fine. Still TODO is the controller side, so you can actually input some data. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2408 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
d0b8bd57e5
commit
b80a0ecdc9
|
@ -21,6 +21,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
|
#include "M6532.hxx"
|
||||||
#include "CartCM.hxx"
|
#include "CartCM.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -30,6 +31,15 @@ CartridgeCM::CartridgeCM(const uInt8* image, uInt32 size, const Settings& settin
|
||||||
// Copy the ROM image into my buffer
|
// Copy the ROM image into my buffer
|
||||||
memcpy(myImage, image, BSPF_min(16384u, size));
|
memcpy(myImage, image, BSPF_min(16384u, size));
|
||||||
createCodeAccessBase(16384);
|
createCodeAccessBase(16384);
|
||||||
|
|
||||||
|
// This cart contains 2048 bytes extended RAM @ 0x1800
|
||||||
|
// This RAM scheme is unique in that it doesn't require separate read/write ports
|
||||||
|
registerRamArea(0x1800, 2048, 0x00, 0x00);
|
||||||
|
|
||||||
|
// On powerup, portA is all 1's, so the last bank of ROM is enabled and
|
||||||
|
// RAM is disabled
|
||||||
|
myStartBank = 3;
|
||||||
|
myRamState = 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -40,47 +50,144 @@ CartridgeCM::~CartridgeCM()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CartridgeCM::reset()
|
void CartridgeCM::reset()
|
||||||
{
|
{
|
||||||
|
// Initialize RAM
|
||||||
|
if(mySettings.getBool("ramrandom"))
|
||||||
|
for(uInt32 i = 0; i < 2048; ++i)
|
||||||
|
myRAM[i] = mySystem->randGenerator().next();
|
||||||
|
else
|
||||||
|
memset(myRAM, 0, 2048);
|
||||||
|
|
||||||
|
// Upon reset we switch to the startup bank
|
||||||
|
bank(myStartBank);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void CartridgeCM::install(System& system)
|
void CartridgeCM::install(System& system)
|
||||||
{
|
{
|
||||||
|
mySystem = &system;
|
||||||
|
uInt16 mask = mySystem->pageMask();
|
||||||
|
|
||||||
|
// Make sure the system we're being installed in has a page size that'll work
|
||||||
|
assert((0x1000 & mask) == 0);
|
||||||
|
|
||||||
|
// Mirror all access in RIOT; by doing so we're taking responsibility
|
||||||
|
// for that address space in peek and poke below.
|
||||||
|
mySystem->m6532().install(system, *this);
|
||||||
|
|
||||||
|
// Install pages for the startup bank
|
||||||
|
bank(myStartBank);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt8 CartridgeCM::peek(uInt16 address)
|
uInt8 CartridgeCM::peek(uInt16 address)
|
||||||
{
|
{
|
||||||
return 0;
|
// NOTE: This does not handle accessing cart ROM/RAM, however, this function
|
||||||
|
// should never be called for ROM/RAM because of the way page accessing
|
||||||
|
// has been setup
|
||||||
|
// It will only ever be called for RIOT reads
|
||||||
|
return mySystem->m6532().peek(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeCM::poke(uInt16 address, uInt8)
|
bool CartridgeCM::poke(uInt16 address, uInt8 value)
|
||||||
{
|
{
|
||||||
return false;
|
// NOTE: This could be called for RIOT writes or cart ROM writes
|
||||||
|
// In the latter case, the write is ignored
|
||||||
|
if(!(address & 0x1000))
|
||||||
|
{
|
||||||
|
// RIOT mirroring, check bankswitch
|
||||||
|
if(address == 0x280)
|
||||||
|
{
|
||||||
|
myRamState = value;
|
||||||
|
bank(myRamState & 0x3);
|
||||||
|
if(value & 0x20) myColumn = 0;
|
||||||
|
if(value & 0x40) myColumn = (myColumn + 1) % 10;
|
||||||
|
}
|
||||||
|
mySystem->m6532().poke(address, value);
|
||||||
|
}
|
||||||
|
return myBankChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeCM::bank(uInt16 bank)
|
bool CartridgeCM::bank(uInt16 bank)
|
||||||
{
|
{
|
||||||
return false;
|
if(bankLocked()) return false;
|
||||||
|
|
||||||
|
// Remember what bank we're in
|
||||||
|
myCurrentBank = bank;
|
||||||
|
uInt16 offset = myCurrentBank << 12;
|
||||||
|
uInt16 shift = mySystem->pageShift();
|
||||||
|
|
||||||
|
// Although this scheme contains four 4K banks and one 2K bank, it's easier
|
||||||
|
// to think of things in terms of 2K slices, as follows:
|
||||||
|
//
|
||||||
|
// The lower 2K of cart address space always points to the lower 2K of the
|
||||||
|
// current ROM bank
|
||||||
|
// The upper 2K of cart address space can point to either the 2K of RAM or
|
||||||
|
// the upper 2K of the current ROM bank
|
||||||
|
|
||||||
|
System::PageAccess access(0, 0, 0, this, System::PA_READ);
|
||||||
|
|
||||||
|
// Lower 2K (always ROM)
|
||||||
|
for(uInt32 address = 0x1000; address < 0x1800; address += (1 << shift))
|
||||||
|
{
|
||||||
|
access.directPeekBase = &myImage[offset + (address & 0x0FFF)];
|
||||||
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
|
||||||
|
mySystem->setPageAccess(address >> shift, access);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upper 2K (RAM or ROM)
|
||||||
|
for(uInt32 address = 0x1800; address < 0x2000; address += (1 << shift))
|
||||||
|
{
|
||||||
|
access.type = System::PA_READWRITE;
|
||||||
|
|
||||||
|
if(myRamState & 0x10)
|
||||||
|
{
|
||||||
|
access.directPeekBase = &myImage[offset + (address & 0x0FFF)];
|
||||||
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
access.directPeekBase = &myRAM[address & 0x7FF];
|
||||||
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x07FF)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if((myRamState & 0x30) == 0x20)
|
||||||
|
access.directPokeBase = &myRAM[address & 0x7FF];
|
||||||
|
else
|
||||||
|
access.directPokeBase = 0;
|
||||||
|
|
||||||
|
mySystem->setPageAccess(address >> shift, access);
|
||||||
|
}
|
||||||
|
|
||||||
|
return myBankChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt16 CartridgeCM::bank() const
|
uInt16 CartridgeCM::bank() const
|
||||||
{
|
{
|
||||||
return 0;
|
return myCurrentBank;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt16 CartridgeCM::bankCount() const
|
uInt16 CartridgeCM::bankCount() const
|
||||||
{
|
{
|
||||||
|
// We report 4 banks (of ROM), even though RAM can overlap the upper 2K
|
||||||
|
// of cart address space at some times
|
||||||
|
// However, this RAM isn't enabled in the normal way that bankswitching
|
||||||
|
// works, so it is ignored here
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeCM::patch(uInt16 address, uInt8 value)
|
bool CartridgeCM::patch(uInt16 address, uInt8 value)
|
||||||
{
|
{
|
||||||
return false;
|
if((myRamState & 0x30) == 0x20)
|
||||||
|
myRAM[address & 0x7FF] = value;
|
||||||
|
else
|
||||||
|
myImage[(myCurrentBank << 12) + address] = value;
|
||||||
|
|
||||||
|
return myBankChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -93,11 +200,54 @@ const uInt8* CartridgeCM::getImage(int& size) const
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeCM::save(Serializer& out) const
|
bool CartridgeCM::save(Serializer& out) const
|
||||||
{
|
{
|
||||||
return false;
|
try
|
||||||
|
{
|
||||||
|
out.putString(name());
|
||||||
|
out.putInt(myCurrentBank);
|
||||||
|
out.putByte(myRamState);
|
||||||
|
out.putByte(myColumn);
|
||||||
|
|
||||||
|
// The 2048 bytes of RAM
|
||||||
|
out.putInt(2048);
|
||||||
|
for(uInt32 i = 0; i < 2048; ++i)
|
||||||
|
out.putByte((char)myRAM[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(const char* msg)
|
||||||
|
{
|
||||||
|
cerr << "ERROR: CartridgeCM::save" << endl << " " << msg << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeCM::load(Serializer& in)
|
bool CartridgeCM::load(Serializer& in)
|
||||||
{
|
{
|
||||||
return false;
|
try
|
||||||
|
{
|
||||||
|
if(in.getString() != name())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
myCurrentBank = (uInt16) in.getInt();
|
||||||
|
myRamState = (uInt8) in.getByte();
|
||||||
|
myColumn = (uInt8) in.getByte();
|
||||||
|
|
||||||
|
// The 2048 bytes of RAM
|
||||||
|
uInt32 limit = (uInt32) in.getInt();
|
||||||
|
for(uInt32 i = 0; i < limit; ++i)
|
||||||
|
myRAM[i] = (uInt8) in.getByte();
|
||||||
|
}
|
||||||
|
catch(const char* msg)
|
||||||
|
{
|
||||||
|
cerr << "ERROR: CartridgeCM::load" << endl << " " << msg << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember what bank we were in
|
||||||
|
myRamState = (uInt8) in.getByte();
|
||||||
|
bank(myCurrentBank);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,36 +27,76 @@ class System;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Cartridge class used for SpectraVideo CompuMate bankswitched games.
|
Cartridge class used for SpectraVideo CompuMate bankswitched games.
|
||||||
There are 4 4K banks selectable at $1000 - $1FFFF.
|
|
||||||
|
This is more than just a cartridge mapper - it's also a "computer" add-on.
|
||||||
|
There's two 8K EPROMs soldered on top of each other. There's two short
|
||||||
|
wires with DB-9's on them which you plug into the two controller ports.
|
||||||
|
A 42 or so key membrane keyboard with audio in and audio out, and 2K of RAM.
|
||||||
|
|
||||||
|
There are 4 4K banks selectable at $1000 - $1FFF, and 2K RAM at
|
||||||
|
$1800 - $1FFF (R/W 'line' is available at SWCHA D5, so there's no separate
|
||||||
|
read and write ports).
|
||||||
|
|
||||||
Bankswitching is done though the controller ports
|
Bankswitching is done though the controller ports
|
||||||
INPT0: D7 = CTRL key input (0 on startup / 1 = key pressed)
|
SWCHA: D7 = Audio input from tape player
|
||||||
INPT1: D7 = always HIGH input (tested at startup)
|
D6 = Audio out to tape player and 4017 CLK
|
||||||
INPT2: D7 = always HIGH input (tested at startup)
|
1 -> increase key column (0 to 9)
|
||||||
INPT3: D7 = SHIFT key input (0 on startup / 1 = key pressed)
|
D5 = 4017 RST, and RAM direction. (high = write, low = read)
|
||||||
INPT4: D7 = keyboard row 1 input (0 = key pressed)
|
1 -> reset key column to 0 (if D4 = 0)
|
||||||
INPT5: D7 = keyboard row 3 input (0 = key pressed)
|
0 -> enable RAM writing (if D4 = 1)
|
||||||
SWCHA: D7 = tape recorder I/O ?
|
D4 = RAM enable: 1 = disable RAM, 0 = enable RAM
|
||||||
D6 = 1 -> increase key collumn (0 to 9)
|
D3 = keyboard row 1 input (0 = key pressed)
|
||||||
D5 = 1 -> reset key collumn to 0 (if D4 = 0)
|
D2 = keyboard row 1 input (0 = key pressed)
|
||||||
D5 = 0 -> enable RAM writing (if D4 = 1)
|
|
||||||
D4 = 1 -> map 2K of RAM at $1800 - $1fff
|
|
||||||
D3 = keyboard row 4 input (0 = key pressed)
|
|
||||||
D2 = keyboard row 2 input (0 = key pressed)
|
|
||||||
D1 = bank select high bit
|
D1 = bank select high bit
|
||||||
D0 = bank select low bit
|
D0 = bank select low bit
|
||||||
|
|
||||||
Keyboard column numbering:
|
INPT0: D7 = CTRL key input (0 on startup / 1 = key pressed)
|
||||||
column 0 = 7 U J M
|
INPT1: D7 = always HIGH input (pulled high thru 20K resistor)
|
||||||
column 1 = 6 Y H N
|
INPT2: D7 = always HIGH input (pulled high thru 20K resistor)
|
||||||
column 2 = 8 I K ,
|
INPT3: D7 = SHIFT key input (0 on startup / 1 = key pressed)
|
||||||
column 3 = 2 W S X
|
INPT4: D7 = keyboard row 0 input (0 = key pressed)
|
||||||
column 4 = 3 E D C
|
INPT5: D7 = keyboard row 2 input (0 = key pressed)
|
||||||
column 5 = 0 P ENTER SPACE
|
|
||||||
column 6 = 9 O L .
|
The keyboard's composed of a 4017 1 of 10 counter, driving the 10 columns of
|
||||||
column 7 = 5 T G B
|
the keyboard. It has 4 rows. The 4 row outputs are buffered by inverters.
|
||||||
column 8 = 1 Q A Z
|
|
||||||
column 9 = 4 R F V
|
Bit 5 of portA controls the reset line on the 4017. Pulling it high will reset
|
||||||
|
scanning to column 0. Pulling it low will allow the counter to be clocked.
|
||||||
|
|
||||||
|
Bit 6 of portA clocks the 4017. Each rising edge advances the column one
|
||||||
|
count.
|
||||||
|
|
||||||
|
There's 10 columns labelled 0-9, and 4 rows, labelled 0-3.
|
||||||
|
|
||||||
|
Column
|
||||||
|
|
||||||
|
0 1 2 3 4 5 6 7 8 9
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
| 7 | | 6 | | 8 | | 2 | | 3 | | 0 | | 9 | | 5 | | 1 | | 4 | 0
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
| U | | Y | | I | | W | | E | | P | | O | | T | | Q | | R | 1
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ Row
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
| J | | H | | K | | S | | D | |ent| | L | | G | | A | | F | 2
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
| M | | N | | < | | X | | C | |spc| | > | | B | | Z | | V | 3
|
||||||
|
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
|
||||||
|
|
||||||
|
Function and Shift are separate keys that are read by 2 of the paddle inputs.
|
||||||
|
These two buttons pull the specific paddle input low when pressed.
|
||||||
|
|
||||||
|
Because the inputs are inverted, a low indicates a pressed button, and a high
|
||||||
|
is an unpressed one.
|
||||||
|
|
||||||
|
The audio input/output are designed to drive a tape player. The audio output is
|
||||||
|
buffered through an inverter and 2 resistors and a capacitor to reduce the level
|
||||||
|
to feed it into the tape player.
|
||||||
|
|
||||||
|
The audio input is passed through a .1uf capacitor and is pulled to 1/2 supply
|
||||||
|
by two 20K resistors, then it goes through a hex inverting schmitt trigger to
|
||||||
|
square it up. This then runs into bit 7 of portA.
|
||||||
|
|
||||||
@author Stephen Anthony & z26 team
|
@author Stephen Anthony & z26 team
|
||||||
@version $Id$
|
@version $Id$
|
||||||
|
@ -172,6 +212,15 @@ class CartridgeCM : public Cartridge
|
||||||
|
|
||||||
// The 16K ROM image of the cartridge
|
// The 16K ROM image of the cartridge
|
||||||
uInt8 myImage[16384];
|
uInt8 myImage[16384];
|
||||||
|
|
||||||
|
// The 2K of RAM
|
||||||
|
uInt8 myRAM[2048];
|
||||||
|
|
||||||
|
// RAM read/write state
|
||||||
|
uInt8 myRamState;
|
||||||
|
|
||||||
|
// Column currently active
|
||||||
|
uInt8 myColumn;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue