Atari 2600 - start Supercharger emulation (and mapper AR)

This commit is contained in:
adelikat 2014-04-16 01:18:38 +00:00
parent f04210c5c9
commit d1e7ed9d91
3 changed files with 434 additions and 39 deletions

View File

@ -17,8 +17,17 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
public MOS6502X Cpu { get; private set; }
public M6532 M6532 { get; private set; }
public int LastAddress;
public int NumberOfDistinctAddresses;
public byte BaseReadMemory(ushort addr)
{
if (addr != LastAddress)
{
NumberOfDistinctAddresses++;
LastAddress = addr;
}
addr = (ushort)(addr & 0x1FFF);
if ((addr & 0x1080) == 0)
{
@ -51,6 +60,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
public void BaseWriteMemory(ushort addr, byte value)
{
if (addr != LastAddress)
{
NumberOfDistinctAddresses++;
LastAddress = addr;
}
addr = (ushort)(addr & 0x1FFF);
if ((addr & 0x1080) == 0)
{
@ -129,7 +144,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
_mapper = SetMultiCartMapper(Rom.Length, 32);
break;
case "AR":
_mapper = new mAR();
_mapper = new mAR(this); // This mapper has to set up configurations in the contructor.
break;
case "4K":
_mapper = new m4K();

View File

@ -31,17 +31,34 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
internal class mAR : MapperBase
{
public mAR()
// TODO: can we set the defaults instead of do it in the constructor?
// TODO: fastscbios setting
// TODO: var names, savestates, hard reset, dispose, cart ram
public mAR(Atari2600 core)
{
throw new NotImplementedException();
Core = core;
InitializeRom();
BankConfiguration(0);
}
private int _bank2k;
private ByteBuffer _ram = new ByteBuffer(6144);
private IntBuffer _offsets = new IntBuffer(2);
//private ByteBuffer _ram = new ByteBuffer(6144);
ByteBuffer myImage = new ByteBuffer(8192);
private IntBuffer _imageOffsets = new IntBuffer(2);
private bool _writePending = false;
int myNumberOfDistinctAccesses = 0;
bool myWriteEnabled = false;
byte myDataHoldRegister;
private readonly byte[] _RomCode = {
// Indicates if the ROM's power is on or off
private bool myPower;
// Indicates when the power was last turned on
int myPowerRomCycle;
private ulong _elapsedCycles;
private readonly byte[] DummyRomCode = {
0xa5, 0xfa, 0x85, 0x80, 0x4c, 0x18, 0xf8, 0xff,
0xff, 0xff, 0x78, 0xd8, 0xa0, 0x00, 0xa2, 0x00,
0x94, 0x00, 0xe8, 0xd0, 0xfb, 0x4c, 0x50, 0xf8,
@ -81,7 +98,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
0xfa, 0x00, 0xcd, 0xf8, 0xff, 0x4c
};
private readonly byte[] RomHeader = {
private readonly byte[] DefaultHeader = {
0xac, 0xfa, 0x0f, 0x18, 0x62, 0x00, 0x24, 0x02,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
@ -116,34 +133,10 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
public override bool HasCartRam
public override void ClockCpu()
{
get { return true; }
}
public override ByteBuffer CartRam
{
get { return _ram; }
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
ser.Sync("bank4k", ref _bank2k);
ser.Sync("ram", ref _ram);
}
public override void HardReset()
{
_bank2k = 0;
_ram = new ByteBuffer(6144);
base.HardReset();
}
public override void Dispose()
{
base.Dispose();
_ram.Dispose();
_elapsedCycles++;
}
private byte ReadMem(ushort addr, bool peek)
@ -158,22 +151,57 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
return base.ReadMemory(addr);
}
if (addr == 0x1850 && _offsets[1] == (3 << 11))
if (addr == 0x1850 && _imageOffsets[1] == (3 << 11))
{
/// TODO: weird stuff goes here
///
return Core.Rom[(addr & 0x7FF) + _offsets[1]];
return myImage[(addr & 0x7FF) + _imageOffsets[1]];
}
// TODO: cancel pending writes
if (addr == 0x1FF8)
// Cancel any pending write if more than 5 distinct accesses have occurred
// TODO: Modify to handle when the distinct counter wraps around...
if (_writePending &&
(Core.NumberOfDistinctAddresses > myNumberOfDistinctAccesses + 5))
{
_writePending = false;
}
if (!((addr & 0x0F00) > 0) && (!myWriteEnabled || !_writePending))
{
myDataHoldRegister = (byte)addr;
myNumberOfDistinctAccesses = Core.NumberOfDistinctAddresses;
_writePending = true;
}
// Is the bank configuration hotspot being accessed?
else if (addr == 0x1FF8)
{
// Yes, so handle the bank configuration
_writePending = false;
BankConfiguration(myDataHoldRegister);
return Core.Rom[(_bank2k << 12) + (addr & 0xFFF)];
}
else if (myWriteEnabled &&
_writePending &&
Core.NumberOfDistinctAddresses == (myNumberOfDistinctAccesses + 5))
{
if ((addr & 0x800) == 0)
{
// TODO
}
else if (_imageOffsets[1] != (3 << 11)) // Don't poke Rom
{
// TODO
}
_writePending = false;
}
var tempVal = (addr & 0x07FF) + _imageOffsets[((addr & 0x800) > 0) ? 1 : 0];
return myImage[(addr & 0x07FF) + _imageOffsets[((addr & 0x800) > 0) ? 1 : 0]];
}
public override byte ReadMemory(ushort addr)
@ -197,5 +225,105 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
_bank2k = 1;
}
}
private void InitializeRom()
{
/* scrom.asm data borrowed from Stella:
// Note that the following offsets depend on the 'scrom.asm' file
// in src/emucore/misc. If that file is ever recompiled (and its
// contents placed in the ourDummyROMCode array), the offsets will
// almost definitely change
*/
// The scrom.asm code checks a value at offset 109 as follows:
// 0xFF -> do a complete jump over the SC BIOS progress bars code
// 0x00 -> show SC BIOS progress bars as normal
DummyRomCode[109] = 0x00; // TODO: fastscbios setting
// Stella does this, but randomness is bad for determinacy! Hopefully we don't really need it
//ourDummyROMCode[281] = mySystem->randGenerator().next();
// Initialize ROM with illegal 6502 opcode that causes a real 6502 to jam
for (int i = 0; i < 2048; i++)
{
myImage[(3 << 11) + i] = 0x02;
}
// Copy the "dummy" Supercharger BIOS code into the ROM area
for (int i = 0; i < DummyRomCode.Length; i++)
{
myImage[(3 << 11) + i] = DummyRomCode[i];
}
// Finally set 6502 vectors to point to initial load code at 0xF80A of BIOS
myImage[(3 << 11) + 2044] = 0x0A;
myImage[(3 << 11) + 2045] = 0xF8;
myImage[(3 << 11) + 2046] = 0x0A;
myImage[(3 << 11) + 2047] = 0xF8;
}
private void BankConfiguration(byte configuration)
{
// D7-D5 of this byte: Write Pulse Delay (n/a for emulator)
//
// D4-D0: RAM/ROM configuration:
// $F000-F7FF $F800-FFFF Address range that banks map into
// 000wp 2 ROM
// 001wp 0 ROM
// 010wp 2 0 as used in Commie Mutants and many others
// 011wp 0 2 as used in Suicide Mission
// 100wp 2 ROM
// 101wp 1 ROM
// 110wp 2 1 as used in Killer Satellites
// 111wp 1 2 as we use for 2k/4k ROM cloning
//
// w = Write Enable (1 = enabled; accesses to $F000-$F0FF cause writes
// to happen. 0 = disabled, and the cart acts like ROM.)
// p = ROM Power (0 = enabled, 1 = off.) Only power the ROM if you're
// wanting to access the ROM for multiloads. Otherwise set to 1.
_bank2k = configuration & 0x1F; // remember for the bank() method
myPower = !((configuration & 0x01) > 0);
if (myPower)
{
myPowerRomCycle = (int)_elapsedCycles;
}
switch ((configuration >> 2) & 0x07)
{
case 0x00:
_imageOffsets[0] = 2 << 11;
_imageOffsets[1] = 3 << 11;
break;
case 0x01:
_imageOffsets[0] = 0;
_imageOffsets[1] = 3 << 11;
break;
case 0x02:
_imageOffsets[0] = 2 << 11;
_imageOffsets[1] = 0;
break;
case 0x03:
_imageOffsets[0] = 0;
_imageOffsets[1] = 2 << 11;
break;
case 0x04:
_imageOffsets[0] = 2 << 11;
_imageOffsets[1] = 3 << 11;
break;
case 0x05:
_imageOffsets[0] = 1 << 11;
_imageOffsets[1] = 3 << 11;
break;
case 0x06:
_imageOffsets[0] = 2 << 11;
_imageOffsets[1] = 1 << 11;
break;
case 0x07:
_imageOffsets[0] = 1 << 11;
_imageOffsets[1] = 2 << 11;
break;
}
}
}
}

View File

@ -0,0 +1,252 @@
;;============================================================================
;;
;; SSSS tt lll lll
;; SS SS tt ll ll
;; SS tttttt eeee ll ll aaaa
;; SSSS tt ee ee ll ll aa
;; SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
;; SS SS tt ee ll ll aa aa
;; SSSS ttt eeeee llll llll aaaaa
;;
;; Copyright (c) 1995-2012 by Bradford W. Mott, Stephen Anthony
;; and the Stella Team
;;
;; See the file "License.txt" for information on usage and redistribution of
;; this file, and for a DISCLAIMER OF ALL WARRANTIES.
;;
;; $Id: scrom.asm,v 1.3 2005-06-16 01:11:29 stephena Exp $
;;============================================================================
;;
;; This file contains a "dummy" Supercharger BIOS for Stella. It is based
;; on routines developed by Eckhard Stolberg.
;;
;;============================================================================
processor 6502
VSYNC equ $00
VBLANK equ $01
WSYNC equ $02
COLUPF equ $08
COLUBK equ $09
CTRLPF equ $0a
PF0 equ $0d
PF1 equ $0e
PF2 equ $0f
RESP0 equ $10
RESP1 equ $11
AUDC0 equ $15
AUDF0 equ $17
AUDV0 equ $19
AUDV1 equ $1a
GRP0 equ $1b
GRP1 equ $1c
ENAM0 equ $1d
ENAM1 equ $1e
ENABL equ $1f
HMP0 equ $20
HMP1 equ $21
HMOVE equ $2a
;;
;; Entry point for multi-load reading
;;
org $F800
LDA $FA ; Grab the load number and store it in $80 where the
STA $80 ; emulator will grab it when it does the loading
JMP clrp7 ; Go clear page 7 of RAM bank 1 like the real SC
;;
;; Entry point for initial load (invoked when the system is reset)
;;
org $F80A
start SEI
CLD
;;
;; Clear page zero routine for initial load (e.g., RAM and TIA registers)
;;
LDY #$00
LDX #$00
ilclr STY $00,X
INX
BNE ilclr
JMP load
;;
;; Clear page 7 of RAM bank 1 (used for stars in the real SC)
;;
clrp7 LDX #$00
LDA $F006,X
LDA $FFF8
LDX #$00
mlclr3 LDA $F000
NOP
LDA $F700,X
DEX
BNE mlclr3
JMP load
;;
;; NOTE: The emulator does the actual reading of the new load when the
;; next instruction is accessed. The emulator expects the load number to
;; to be stored in location $80. As a side-effect the emulator sets $80
;; to contain the bank selection byte from the load's header and sets
;; $FE and $FF to contain the starting address from the load's header.
;;
load org $F850
;;
;; Copy code into page zero to do the bank switching
;;
LDX #$03
copy LDY code,X
STY $FA,X
DEX
BPL copy
;;
;; Clear some of the 2600's RAM and TIA registers like the real SC BIOS does
;;
LDY #$00
LDX #$28
mlclr1 STY $04,X
DEX
BPL mlclr1
LDX #$1C
mlclr2 STY $81,X
DEX
BPL mlclr2
;;
;; Display the "emulated" Supercharger loading progress bar
;;
;; Check if we should skip the loading progress bar
;; Note that the following code seems to never do a jump
;; However, the comparison value can be patched outside this code
;;
LDA #$FF
CMP #$00 ; patch this value to $FF outside ROM to do a jump
BNE startbars
JMP skipbars
;; Otherwise we display them
startbars:
LDA #$00
STA GRP0
STA GRP1
STA ENAM0
STA ENAM1
STA ENABL
STA AUDV0
STA AUDV1
STA COLUPF
STA VBLANK
LDA #$10
STA HMP1
STA WSYNC
LDX #$07
DEX
pos DEX
BNE pos
LDA #$00
STA HMP0
STA RESP0
STA RESP1
STA WSYNC
STA HMOVE
LDA #%00000101
STA CTRLPF
LDA #$FF
STA PF0
STA PF1
STA PF2
STA $84
STA $85
LDA #$F0
STA $83
LDA #$74
STA COLUBK
LDA #$0C
STA AUDC0
LDA #$1F
STA AUDF0
STA $82
LDA #$07
STA AUDV0
a1
LDX #$08
LDY #$00
a2
STA WSYNC
DEY
BNE a2
STA WSYNC
STA WSYNC
LDA #$02
STA WSYNC
STA VSYNC
STA WSYNC
STA WSYNC
STA WSYNC
LDA #$00
STA VSYNC
DEX
BPL a2
ASL $83
ROR $84
ROL $85
LDA $83
STA PF0
LDA $84
STA PF1
LDA $85
STA PF2
LDX $82
DEX
STX $82
STX AUDF0
CPX #$0A
BNE a1
LDA #%00000010
STA VBLANK
LDX #$1C
LDY #$00
STY AUDV0
STY COLUBK
clear:
STY $81,x
DEX
BPL clear
skipbars:
;;
;; Setup value to be stored in the bank switching control register
;;
LDX $80
CMP $F000,X
;;
;; Initialize A, X, Y, and SP registers
;;
LDA #$9a ;; This is patched outside the ROM to a random value
LDX #$FF
LDY #$00
TXS
;;
;; Execute the code to do bank switch and start running cartridge code
;;
JMP $FA
code dc.b $cd, $f8, $ff ;; CMP $fff8
dc.b $4c ;; JMP $????