A new BIOS has been added to the Supercharger emulation based on code

from Eckhard Stolberg.  This version simulates the progress bar and
leaves the TIA in a state more like the real thing.  This fixes some
display problems with games (e.g., Mindmaster).  In addtion, the bank
switching and RAM writing emulation has been improved.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@63 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
bwmott 2002-04-05 02:18:23 +00:00
parent 5898941bf0
commit 62b7ae6038
4 changed files with 331 additions and 167 deletions

View File

@ -8,12 +8,12 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CartAR.cxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $
// $Id: CartAR.cxx,v 1.2 2002-04-05 02:18:23 bwmott Exp $
//============================================================================
#include <assert.h>
@ -41,11 +41,8 @@ CartridgeAR::CartridgeAR(const uInt8* image, uInt32 size)
myImage[i] = random.next();
}
// Initialize ROM with an invalid 6502 opcode
for(i = 6 * 1024; i < 8 * 1024; ++i)
{
myImage[i] = 0xFF;
}
// Initialize SC BIOS ROM
initializeROM();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -63,20 +60,16 @@ const char* CartridgeAR::name() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeAR::reset()
{
// Try to load the first load into RAM upon reset
loadIntoRAM(0);
myPower = true;
myPowerRomCycle = 0;
myPowerRomCycle = mySystem->cycles();
myWriteEnabled = false;
myLastAccess = 0;
myDataHoldRegister = 0;
myNumberOfDistinctAccesses = 0;
myWritePending = false;
// Set bank configuration upon reset so ROM is selected
myImageOffset[0] = 0 * 2048;
myImageOffset[1] = 3 * 2048;
// Set bank configuration upon reset so ROM is selected and powered up
bankConfiguration(0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -116,49 +109,30 @@ void CartridgeAR::install(System& system)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeAR::peek(uInt16 addr)
{
// Check to see if the Supercharger ROM is being accessed?
if(myImageOffset[1] == 3 * 2048)
// Is the "dummy" SC BIOS hotspot for reading a load being accessed?
if(((addr & 0x1FFF) == 0x1850) && (myImageOffset[1] == (3 * 2048)))
{
Int32 cycles = mySystem->cycles();
// Get load that's being accessed (BIOS places load number at 0x80)
uInt8 load = mySystem->peek(0x0080);
// Is the tape rewind routine being accessed?
if((addr & 0x1FFF) == 0x180A)
{
// See if the ROM has been powered up long enough
if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles)))
{
cerr << "ERROR: Supercharger ROM has not been powered up!\n";
}
else
{
cerr << "ERROR: Supercharger code doesn't handle rewinding tape!\n";
}
}
// Is the multiload routine being accessed?
else if((addr & 0x1FFF) == 0x1800)
{
// See if the ROM has been powered up long enough
if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles)))
{
cerr << "ERROR: Supercharger ROM has not been powered up!\n";
}
else
{
// Get the load they're trying to access
uInt8 load = mySystem->peek(0x00FA);
// Read the specified load into RAM
loadIntoRAM(load);
// Load the specified load into RAM
loadIntoRAM(load);
return myImage[(addr & 0x07FF) + myImageOffset[1]];
}
}
return myImage[(addr & 0x07FF) + myImageOffset[1]];
}
// Are the "value" registers being accessed?
// Cancel any pending write if more than 5 distinct accesses have occurred
// TODO: Modify to handle when the distinct counter wraps around...
if(myWritePending &&
(my6502->distinctAccesses() > myNumberOfDistinctAccesses + 5))
{
myWritePending = false;
}
// Is the data hold register being set?
if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending))
{
myLastAccess = addr;
myDataHoldRegister = addr;
myNumberOfDistinctAccesses = my6502->distinctAccesses();
myWritePending = true;
}
@ -167,22 +141,17 @@ uInt8 CartridgeAR::peek(uInt16 addr)
{
// Yes, so handle bank configuration
myWritePending = false;
bankConfiguration(myLastAccess);
bankConfiguration(myDataHoldRegister);
}
// Handle poke if writing enabled
else if(myWriteEnabled && myWritePending)
else if(myWriteEnabled && myWritePending &&
(my6502->distinctAccesses() == (myNumberOfDistinctAccesses + 5)))
{
if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5)
{
if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5)
{
if((addr & 0x0800) == 0)
myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess;
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess;
}
myWritePending = false;
}
if((addr & 0x0800) == 0)
myImage[(addr & 0x07FF) + myImageOffset[0]] = myDataHoldRegister;
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
myImage[(addr & 0x07FF) + myImageOffset[1]] = myDataHoldRegister;
myWritePending = false;
}
return myImage[(addr & 0x07FF) + myImageOffset[(addr & 0x0800) ? 1 : 0]];
@ -191,10 +160,18 @@ uInt8 CartridgeAR::peek(uInt16 addr)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeAR::poke(uInt16 addr, uInt8)
{
// Are the "value" registers being accessed?
// Cancel any pending write if more than 5 distinct accesses have occurred
// TODO: Modify to handle when the distinct counter wraps around...
if(myWritePending &&
(my6502->distinctAccesses() > myNumberOfDistinctAccesses + 5))
{
myWritePending = false;
}
// Is the data hold register being set?
if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending))
{
myLastAccess = addr;
myDataHoldRegister = addr;
myNumberOfDistinctAccesses = my6502->distinctAccesses();
myWritePending = true;
}
@ -203,22 +180,17 @@ void CartridgeAR::poke(uInt16 addr, uInt8)
{
// Yes, so handle bank configuration
myWritePending = false;
bankConfiguration(myLastAccess);
bankConfiguration(myDataHoldRegister);
}
// Handle poke if writing enabled
else if(myWriteEnabled && myWritePending)
else if(myWriteEnabled && myWritePending &&
(my6502->distinctAccesses() == (myNumberOfDistinctAccesses + 5)))
{
if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5)
{
if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5)
{
if((addr & 0x0800) == 0)
myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess;
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess;
}
myWritePending = false;
}
if((addr & 0x0800) == 0)
myImage[(addr & 0x07FF) + myImageOffset[0]] = myDataHoldRegister;
else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-)
myImage[(addr & 0x07FF) + myImageOffset[1]] = myDataHoldRegister;
myWritePending = false;
}
}
@ -314,45 +286,67 @@ void CartridgeAR::bankConfiguration(uInt8 configuration)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeAR::setupROM()
void CartridgeAR::initializeROM()
{
static uInt8 dummyROMCode[] = {
0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0,
0x80, 0xd0, 0xf9, 0x4c, 0x2b, 0xfa, 0xff, 0xff,
0xa5, 0xfa, 0x85, 0x80, 0x4c, 0x18, 0xf8, 0xff,
0xff, 0xff, 0x78, 0xd8, 0xa0, 0x0, 0xa2, 0x0,
0x94, 0x0, 0xe8, 0xd0, 0xfb, 0x4c, 0x50, 0xf8,
0xa2, 0x0, 0xbd, 0x6, 0xf0, 0xad, 0xf8, 0xff,
0xa2, 0x0, 0xad, 0x0, 0xf0, 0xea, 0xbd, 0x0,
0xf7, 0xca, 0xd0, 0xf6, 0x4c, 0x50, 0xf8, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0,
0x1e, 0xd0, 0xf9, 0xa2, 0x0, 0xbd, 0x45, 0xfa,
0x95, 0xfa, 0xe8, 0xe0, 0x6, 0xd0, 0xf6, 0xa2,
0xff, 0xa0, 0x0, 0xa9, 0x0, 0x85, 0x80, 0xcd,
0x0, 0xf0, 0x4c, 0xfa, 0x0, 0xad, 0xf8, 0xff,
0x4c, 0x0, 0x0
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xa2, 0x3, 0xbc, 0x1d, 0xf9, 0x94, 0xfa, 0xca,
0x10, 0xf8, 0xa0, 0x0, 0xa2, 0x28, 0x94, 0x4,
0xca, 0x10, 0xfb, 0xa2, 0x1c, 0x94, 0x81, 0xca,
0x10, 0xfb, 0xa9, 0x0, 0x85, 0x1b, 0x85, 0x1c,
0x85, 0x1d, 0x85, 0x1e, 0x85, 0x1f, 0x85, 0x19,
0x85, 0x1a, 0x85, 0x8, 0x85, 0x1, 0xa9, 0x10,
0x85, 0x21, 0x85, 0x2, 0xa2, 0x7, 0xca, 0xca,
0xd0, 0xfd, 0xa9, 0x0, 0x85, 0x20, 0x85, 0x10,
0x85, 0x11, 0x85, 0x2, 0x85, 0x2a, 0xa9, 0x5,
0x85, 0xa, 0xa9, 0xff, 0x85, 0xd, 0x85, 0xe,
0x85, 0xf, 0x85, 0x84, 0x85, 0x85, 0xa9, 0xf0,
0x85, 0x83, 0xa9, 0x74, 0x85, 0x9, 0xa9, 0xc,
0x85, 0x15, 0xa9, 0x1f, 0x85, 0x17, 0x85, 0x82,
0xa9, 0x7, 0x85, 0x19, 0xa2, 0x8, 0xa0, 0x0,
0x85, 0x2, 0x88, 0xd0, 0xfb, 0x85, 0x2, 0x85,
0x2, 0xa9, 0x2, 0x85, 0x2, 0x85, 0x0, 0x85,
0x2, 0x85, 0x2, 0x85, 0x2, 0xa9, 0x0, 0x85,
0x0, 0xca, 0x10, 0xe4, 0x6, 0x83, 0x66, 0x84,
0x26, 0x85, 0xa5, 0x83, 0x85, 0xd, 0xa5, 0x84,
0x85, 0xe, 0xa5, 0x85, 0x85, 0xf, 0xa6, 0x82,
0xca, 0x86, 0x82, 0x86, 0x17, 0xe0, 0xa, 0xd0,
0xc3, 0xa9, 0x2, 0x85, 0x1, 0xa2, 0x1c, 0xa0,
0x0, 0x84, 0x19, 0x84, 0x9, 0x94, 0x81, 0xca,
0x10, 0xfb, 0xa6, 0x80, 0xdd, 0x0, 0xf0, 0xa5,
0x80, 0x45, 0xfe, 0x45, 0xff, 0xa2, 0xff, 0xa0,
0x0, 0x9a, 0x4c, 0xfa, 0x0, 0xcd, 0xf8, 0xff,
0x4c
};
int size = sizeof(dummyROMCode);
uInt32 size = sizeof(dummyROMCode);
// Copy the "dummy" ROM code into the ROM area
for(int i = 0; i < size; ++i)
// Initialize ROM with illegal 6502 opcode that causes a real 6502 to jam
for(uInt32 i = 0; i < 2048; ++i)
{
myImage[0x1A00 + i] = dummyROMCode[i];
myImage[3 * 2048 + i] = 0x02;
}
// Put a JMP $FA20 at multiload entry point ($F800)
myImage[0x1800] = 0x4C;
myImage[0x1801] = 0x20;
myImage[0x1802] = 0xFA;
// Copy the "dummy" Supercharger BIOS code into the ROM area
for(uInt32 j = 0; j < size; ++j)
{
myImage[3 * 2048 + j] = dummyROMCode[j];
}
// Update ROM code to have the correct reset address and bank configuration
myImage[0x1A00 + size - 2] = myHeader[0];
myImage[0x1A00 + size - 1] = myHeader[1];
myImage[0x1A00 + size - 11] = myHeader[2];
myImage[0x1A00 + size - 15] = myHeader[2];
// Finally set 6502 vectors to point to this "dummy" code at 0xFA00
myImage[3 * 2048 + 2044] = 0x00;
myImage[3 * 2048 + 2045] = 0xFA;
myImage[3 * 2048 + 2046] = 0x00;
myImage[3 * 2048 + 2047] = 0xFA;
// Finally set 6502 vectors to point to initial load code at 0xF80A of BIOS
myImage[3 * 2048 + 2044] = 0x0A;
myImage[3 * 2048 + 2045] = 0xF8;
myImage[3 * 2048 + 2046] = 0x0A;
myImage[3 * 2048 + 2047] = 0xF8;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -403,12 +397,19 @@ void CartridgeAR::loadIntoRAM(uInt8 load)
invalidPageChecksumSeen = true;
}
// Copy page to Supercharger RAM
memcpy(myImage + (bank * 2048) + (page * 256), src, 256);
// Copy page to Supercharger RAM (don't allow a copy into ROM area)
if(bank < 3)
{
memcpy(myImage + (bank * 2048) + (page * 256), src, 256);
}
}
// Make sure the "dummy" ROM is installed
setupROM();
// Copy the bank switching byte and starting address into the 2600's
// RAM for the "dummy" SC BIOS to access it
mySystem->poke(0xfe, myHeader[0]);
mySystem->poke(0xff, myHeader[1]);
mySystem->poke(0x80, myHeader[2]);
return;
}
}

View File

@ -8,12 +8,12 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: CartAR.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $
// $Id: CartAR.hxx,v 1.2 2002-04-05 02:18:23 bwmott Exp $
//============================================================================
#ifndef CARTRIDGEAR_HXX
@ -28,13 +28,14 @@ class M6502High;
/**
This is the cartridge class for Arcadia (aka Starpath) Supercharger
games. Christopher Salomon provided most of the technical details
used in creating this class.
used in creating this class. A good description of the Supercharger
is provided in the Cuttle Cart's manual.
The Supercharger has four 2K banks. There are three banks of RAM
and one bank of ROM. All 6K of the RAM can be read and written.
@author Bradford W. Mott
@version $Id: CartAR.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $
@version $Id: CartAR.hxx,v 1.2 2002-04-05 02:18:23 bwmott Exp $
*/
class CartridgeAR : public Cartridge
{
@ -106,8 +107,8 @@ class CartridgeAR : public Cartridge
// Load the specified load into SC RAM
void loadIntoRAM(uInt8 load);
// Sets up a "dummy" bootstrap ROM in the ROM bank of the cartridge
void setupROM();
// Sets up a "dummy" BIOS ROM in the ROM bank of the cartridge
void initializeROM();
private:
// Pointer to the 6502 processor in the system
@ -137,10 +138,10 @@ class CartridgeAR : public Cartridge
// Indicates when the power was last turned on
Int32 myPowerRomCycle;
// Indicates the "value" address which was accessed
uInt16 myLastAccess;
// Data hold register used for writing
uInt8 myDataHoldRegister;
// Indicates the number of distinct accesses when "value" was set
// Indicates number of distinct accesses when data hold register was set
uInt32 myNumberOfDistinctAccesses;
// Indicates if a write is pending or not

View File

@ -2,7 +2,7 @@
Simple program that produces a hex list of a binary object file
@author Bradford W. Mott
@version $Id: romtohex.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
@version $Id: romtohex.cxx,v 1.2 2002-04-05 02:18:23 bwmott Exp $
*/
#include <iomanip.h>
@ -10,7 +10,9 @@
main()
{
ifstream in("rom.o");
ifstream in("scrom.bin");
cout << " ";
for(int t = 0; ; ++t)
{
@ -23,7 +25,7 @@ main()
cout << "0x" << hex << (int)c << ", ";
if((t % 8) == 7)
cout << endl;
cout << endl << " ";
}
cout << endl;
}

View File

@ -8,74 +8,234 @@
;; SS SS tt ee ll ll aa aa
;; SSSS ttt eeeee llll llll aaaaa
;;
;; Copyright (c) 1995-1998 by Bradford W. Mott
;; Copyright (c) 1995-2002 by Bradford W. Mott
;;
;; See the file "license" for information on usage and redistribution of
;; this file, and for a DISCLAIMER OF ALL WARRANTIES.
;;
;; $Id: scrom.asm,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
;; $Id: scrom.asm,v 1.2 2002-04-05 02:18:23 bwmott Exp $
;;============================================================================
;;
;; This file contains a "dummy" Supercharger ROM for Stella. It basically
;; contains some bootstrapping code to get the game up and running.
;; This file contains a "dummy" Supercharger BIOS for Stella. It is based
;; on routines developed by Eckhard Stolberg.
;;
;;============================================================================
processor 6502
org $FA00
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
;;
;; Normal clear page zero routine for initial program load
;; Entry point for initial load (invoked when the system is reset)
;;
LDA #0
LDX #0
clear STA $80,X
INX
CPX #$80
BNE clear
JMP cpcode
org $F80A
start SEI
CLD
;;
;; Clear page zero routine for multi-load
;; Clear page zero routine for initial load (e.g., RAM and TIA registers)
;;
org $FA20
LDA #0
LDX #0
mlclr STA $80,X
INX
CPX #$1E
BNE mlclr
;;
;; Now, copy some code into page zero to do the initial bank switch
;;
cpcode LDX #0
copy LDA code,X
STA $fa,X
INX
CPX #6
BNE copy
;;
;; Initialize X and Y registers
;;
LDX #$ff
LDY #$00
LDX #$00
ilclr STY $00,X
INX
BNE ilclr
JMP load
;;
;; Store the bank configuration in $80 and in CMP instruction
;; Clear page 7 of RAM bank 1 (used for stars in the real SC)
;;
LDA #$00 ;; $00 is changed by emulator to the correct value
STA $80
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
;;
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
;;
;; Setup value to be stored in the bank switching control register
;;
LDX $80
CMP $F000,X
;;
;; Initialize A, X, Y, and SP registers
;;
LDA $80
EOR $FE
EOR $FF
LDX #$FF
LDY #$00
TXS
CMP $f000 ;; $00 is changed by emulator to the correct value
;;
;; Execute the code to do bank switch and start running cartridge code
;;
JMP $fa
JMP $FA
code dc.b $ad, $f8, $ff ;; LDA $fff8
dc.b $4c, $00, $00 ;; JMP $???? ($???? is filled in by emulator)
code dc.b $cd, $f8, $ff ;; CMP $fff8
dc.b $4c ;; JMP $????