BizHawk/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mCM.cs

320 lines
10 KiB
C#

using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
/*
* Spectravideo Compumate Add-on Kevtris Documentation
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 1K of RAM.
Port A on the RIOT is used to run most of the functions on the Compumate:
7 0
---------
ACRE 31BB
A - Audio input from tape player
C - Audio out to tape player and 4017 CLK
R - 4017 RST, and RAM direction. (high = write, low = read)
E - RAM enable. 1 = disable RAM, 0 = enable RAM
3 - Row 3 of keyboard
1 - Row 1 of keyboard
B - 2 bit ROM bank number
All bits are outputs except for the 2 row inputs from the keyboard.
Unlike most things, the Compumate uses all three of the TIA inputs on each
joystick port (paddles and fire).
TIA inputs:
0 - function key
1 - pulled high thru 20K resistor
2 - pulled high thru 20K resistor
3 - shift key
4 - Row 0
5 - Row 2
Memory Map:
-----------
1000-1FFF : selectable 4K ROM bank (selected by D0, D1 on portA)
On powerup, the port is all 1's, so the last bank of ROM is enabled, RAM is
disabled.
when RAM is enabled:
1000-17FF : 2K of RAM. It's mapped into 1000-17FF. Unlike most 2600 carts,
bit 5 of portA controls if the RAM is readable or writable. When it's high,
the RAM is write only. When it's low, it is read only. There's no separate
read and write ports.
Keyboard:
---------
The keyboard's composed of a 4017 1 of 10 counter, driving the 10 columns of
the keyboard. It has 4 rows. The 4 row outputs are buffered by inverters.
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.
*/
/*
* Spectravideo Compumate Add-on Stella Documentation
Cartridge class used for SpectraVideo CompuMate bankswitched games.
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
SWCHA: D7 = Audio input from tape player
D6 = Audio out to tape player and 4017 CLK
1 -> increase key column (0 to 9)
D5 = 4017 RST, and RAM direction. (high = write, low = read)
1 -> reset key column to 0 (if D4 = 0)
0 -> enable RAM writing (if D4 = 1)
D4 = RAM enable: 1 = disable RAM, 0 = enable RAM
D3 = keyboard row 3 input (0 = key pressed)
D2 = keyboard row 1 input (0 = key pressed)
D1 = bank select high bit
D0 = bank select low bit
INPT0: D7 = FUNC key input (0 on startup / 1 = key pressed)
INPT1: D7 = always HIGH input (pulled high thru 20K resistor)
INPT2: D7 = always HIGH input (pulled high thru 20K resistor)
INPT3: D7 = SHIFT key input (0 on startup / 1 = key pressed)
INPT4: D7 = keyboard row 0 input (0 = key pressed)
INPT5: D7 = keyboard row 2 input (0 = key pressed)
The keyboard's composed of a 4017 1 of 10 counter, driving the 10 columns of
the keyboard. It has 4 rows. The 4 row outputs are buffered by inverters.
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.
*/
internal class mCM : MapperBase
{
// TODO: PokeMem
private ByteBuffer _ram = new ByteBuffer(2048);
private int _bank4K = 3; // On Start up, controller port is all 1's, so start on the last bank, flags enabled
private bool _disableRam = true;
private bool _writeMode = false;
private int _column = 0;
public override void Dispose()
{
_ram.Dispose();
base.Dispose();
}
public override void HardReset()
{
_ram = new ByteBuffer(2048);
_bank4K = 3;
_disableRam = true;
_writeMode = true;
_column = 0;
base.HardReset();
}
public override void SyncState(Serializer ser)
{
// TODO
ser.Sync("cartRam", ref _ram);
ser.Sync("bank4k", ref _bank4K);
ser.Sync("column", ref _column);
ser.Sync("disableRam", ref _disableRam);
ser.Sync("writeMode", ref _writeMode);
base.SyncState(ser);
}
public override byte ReadMemory(ushort addr)
{
if (addr < 0x1000)
{
return base.ReadMemory(addr);
}
// Lower 2K is always the first 2K of the ROM bank
// Upper 2K is also Rom if ram is enabled
if (addr < 0x1800 || _disableRam)
{
return Core.Rom[(_bank4K << 12) + (addr & 0xFFF)];
}
// 2K of RAM
if (!_writeMode)
{
return _ram[addr & 0x7FF];
}
// Attempting to read while in write mode
throw new NotTestedException();
}
public override byte PeekMemory(ushort addr)
{
return ReadMemory(addr);
}
public override void WriteMemory(ushort addr, byte value)
{
//Mimicking the 6532 logic for accesing port A, for testing
var isPortA = false;
if ((addr & 0x0200) == 0) // If the RS bit is not set, this is a ram write
{
}
else
{
// If bit 0x0010 is set, and bit 0x0004 is set, this is a timer write
if ((addr & 0x0014) == 0x0014)
{
}
// If bit 0x0004 is not set, bit 0x0010 is ignored and
// these are register writes
else if ((addr & 0x0004) == 0)
{
var registerAddr = (ushort)(addr & 0x0007);
if (registerAddr == 0x00)
{
if (addr != 640)
{
// Write Output reg A
isPortA = true;
}
}
}
}
//if (addr == 0x280) //Stella uses only 280
if (isPortA)
{
var bit5 = value.Bit(5);
var bit4 = value.Bit(4);
//D5 RAM direction. (high = write, low = read)
//0 -> enable RAM writing (if D4 = 1)
//D4 = RAM enable: 1 = disable RAM, 0 = enable RAM
_disableRam = bit4;
//_writeMode = bit5 || bit4; // ?? Am I interpretting this correctly?
_writeMode = (value & 0x30) == 0x20;
_bank4K = value & 0x03;
//D6 = 1 -> increase key column (0 to 9)
//D5 = 1 -> reset key column to 0 (if D4 = 0)
if (bit5 && !bit4)
{
_column = 0;
}
if (value.Bit(6))
{
_column = (_column + 1) % 10;
}
}
if (addr >= 0x1800)
{
if (!_disableRam && _writeMode)
{
_ram[addr & 0x7FF] = value;
}
}
base.WriteMemory(addr, value);
}
}
}