Update to bsnes v044 release.

This release adds full SA-1 support, with no known issues. All 26 games have been tested by myself and others, and a few have been beaten from start to finish. The latter include Super Mario RPG, Kirby's Dreamland 3, Kirby Super Star and Jikkyou Oshaberi Parodius.
Please understand that the SA-1 is essentially four times faster than the SNES' main CPU, so system requirements will be very high for these games. For example, on an E8400 @ 3.0GHz, I average ~160fps in ordinary games. But for SA-1 emulation, this drops to ~90fps, with the worst case being ~80fps.
The following features are emulated:
    - 5a22 CPU core (bus-cycle accurate)
    - Memory access timing
    - SA-1 -> S-CPU interrupts (IRQ + CHDMA IRQ)
    - S-CPU -> SA-1 interrupts (IRQ + Timer IRQ + DMA IRQ + NMI)
    - SIV / SNV interrupt vector selection
    - Timer unit (linear and H/V)
    - Super MMC unit (ROM + BW-RAM)
    - BS-X flash cart slot mapping
    - Normal DMA
    - Character-conversion 1 DMA (2bpp + 4bpp + 8bpp)
    - Character-conversion 2 DMA (2bpp + 4bpp + 8bpp)
    - BW-RAM virtual bitmap mode (2bpp + 4bpp)
    - Arithmetic unit (multiplication + division + cumulative sum)
    - Variable-length bit processing (fixed and auto increment)
While the following features are not currently emulated, mostly due to lack of information:
    - SA-1 bus conflict delays
    - Write protection (BW-RAM + I-RAM)
    - SA-1 CPU priority for DMA transfers
    - DMA access timing
This commit is contained in:
byuu 2009-04-19 21:34:23 +00:00
parent b0a8de0208
commit 44b5f1bf27
23 changed files with 283 additions and 365 deletions

View File

@ -1,4 +1,4 @@
#define BSNES_VERSION "0.043"
#define BSNES_VERSION "0.044"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus
@ -9,12 +9,7 @@
//S-DSP can be encapsulated into a state machine using #define magic
//this avoids ~2.048m co_switch() calls per second (~5% speedup)
#define USE_STATE_MACHINE
//FAST_FRAMESKIP disables calculation of RTO during frameskip
//frameskip offers near-zero speedup if RTO is calculated
//accuracy is not affected by this define when frameskipping is off
#define FAST_FRAMESKIP
#define DSP_STATE_MACHINE
//game genie + pro action replay code support (~2% speed hit)
#define CHEAT_SYSTEM

View File

@ -1,2 +1,2 @@
#make platform=x compiler=gcc
make platform=x compiler=gcc enable_gzip=true enable_jma=true
make platform=x compiler=gcc
#make platform=x compiler=gcc enable_gzip=true enable_jma=true

View File

@ -1,16 +1,11 @@
#ifdef SA1_CPP
namespace memory {
namespace cpu {
CPUIRAM iram;
CPUBWRAM bwram;
}
namespace sa1 {
SA1IRAM iram;
SA1BWRAM bwram;
SA1BitmapRAM bitmapram;
}
VectorSelectionPage vectorsp;
StaticRAM iram(2048);
MappedRAM &bwram = memory::cartram;
CC1BWRAM cc1bwram;
BitmapRAM bitmapram;
}
void SA1Bus::init() {
@ -22,124 +17,103 @@ void SA1Bus::init() {
memory::mmio.map(i, sa1);
}
map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::sa1::iram);
map(MapLinear, 0x00, 0x3f, 0x0000, 0x07ff, memory::iram);
map(MapDirect, 0x00, 0x3f, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::sa1::iram);
map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1::bwram);
map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bwram);
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::sa1::bwram, 0, 0x040000);
map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::sa1::bitmapram);
map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::sa1::iram);
map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::bwram);
map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bitmapram);
map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::iram);
map(MapDirect, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::sa1::iram);
map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1::bwram);
map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bwram);
map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::cpu::iram);
bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cpu::bwram);
bus.map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram);
bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cpu::bwram, 0, 0x040000);
bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::cpu::iram);
bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cpu::bwram);
bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram);
bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
memory::vectorsp.sync();
}
//=======
//CPUIRAM
//=======
//===================
//VectorSelectionPage
//===================
unsigned CPUIRAM::size() const {
return sizeof(sa1.iram);
//this class maps $00:[ff00-ffff] for the purpose of supporting:
//$2209.d6 IVSW (S-CPU IRQ vector selection) (0 = cart, 1 = SA-1)
//$2209.d4 NVSW (S-CPU NMI vector selection) (0 = cart, 1 = SA-1)
//when set, vector addresses are over-ridden with SA-1 register settings:
//SIV = S-CPU IRQ vector address override
//SNV = S-CPU NMI vector address override
//
//$00:[ffea-ffeb|ffee-ffef] are special cased on read;
//all other addresses return original mapped data.
uint8_t VectorSelectionPage::read(unsigned addr) {
switch(0xff00 | (addr & 0xff)) {
case 0xffea: case 0xffeb: {
if(sa1.mmio.cpu_nvsw == true) return (sa1.mmio.snv >> ((addr & 1) << 3));
} break;
case 0xffee: case 0xffef: {
if(sa1.mmio.cpu_ivsw == true) return (sa1.mmio.siv >> ((addr & 1) << 3));
} break;
}
return access->read(addr);
}
uint8_t CPUIRAM::read(unsigned addr) {
return sa1.iram[addr];
void VectorSelectionPage::write(unsigned addr, uint8_t data) {
return access->write(addr, data);
}
void CPUIRAM::write(unsigned addr, uint8_t data) {
uint8_t wpbit = (addr >> 8) & 7;
if(1 || sa1.mmio.siwp & wpbit) {
//allow only when write-protection is disabled
sa1.iram[addr] = data;
//call this whenever bus is remapped.
//note: S-CPU and SA-1 bus always share $00:[ff00-ffff] as cartridge ROM data;
//the SA-1 MMC does not allow mapping these independently between processors.
//this allows this class to be shared for both, caching only ones' access class.
void VectorSelectionPage::sync() {
if(bus.page[0x00ff00 >> 8].access != this) {
//bus was re-mapped, hook access routine
access = bus.page[0x00ff00 >> 8].access;
bus.page[0x00ff00 >> 8].access = this;
sa1bus.page[0x00ff00 >> 8].access = this;
}
}
//========
//CPUBWRAM
//CC1BWRAM
//========
unsigned CPUBWRAM::size() const {
unsigned CC1BWRAM::size() const {
return memory::cartram.size();
}
uint8_t CPUBWRAM::read(unsigned addr) {
if(cc1dma) return sa1.dma_cc1_read(addr);
uint8_t CC1BWRAM::read(unsigned addr) {
if(dma) return sa1.dma_cc1_read(addr);
return memory::cartram.read(addr);
}
void CPUBWRAM::write(unsigned addr, uint8_t data) {
if(sa1.mmio.swen == false) {
//write-protection enabled
unsigned limit = 0x100 << sa1.mmio.bwp;
//if(addr < limit) return;
}
void CC1BWRAM::write(unsigned addr, uint8_t data) {
memory::cartram.write(addr, data);
}
//=======
//SA1IRAM
//=======
//=========
//BitmapRAM
//=========
unsigned SA1IRAM::size() const {
return sizeof(sa1.iram);
}
uint8_t SA1IRAM::read(unsigned addr) {
return sa1.iram[addr];
}
void SA1IRAM::write(unsigned addr, uint8_t data) {
uint8_t wpbit = (addr >> 8) & 7;
if(1 || sa1.mmio.ciwp & wpbit) {
//allow only when write-protection is disabled
sa1.iram[addr] = data;
}
}
//========
//SA1BWRAM
//========
unsigned SA1BWRAM::size() const {
return memory::cartram.size();
}
uint8_t SA1BWRAM::read(unsigned addr) {
return memory::cartram.read(addr);
}
void SA1BWRAM::write(unsigned addr, uint8_t data) {
if(sa1.mmio.cwen == false) {
//write-protection enabled
unsigned limit = 0x100 << sa1.mmio.bwp;
//if(addr < limit) return;
}
memory::cartram.write(addr, data);
}
//============
//SA1BitmapRAM
//============
unsigned SA1BitmapRAM::size() const {
unsigned BitmapRAM::size() const {
return 0x100000;
}
uint8_t SA1BitmapRAM::read(unsigned addr) {
uint8_t BitmapRAM::read(unsigned addr) {
if(sa1.mmio.bbf == 0) {
//4bpp
unsigned shift = addr & 1;
@ -161,7 +135,7 @@ uint8_t SA1BitmapRAM::read(unsigned addr) {
}
}
void SA1BitmapRAM::write(unsigned addr, uint8_t data) {
void BitmapRAM::write(unsigned addr, uint8_t data) {
if(sa1.mmio.bbf == 0) {
//4bpp
uint8_t shift = addr & 1;

View File

@ -1,49 +1,31 @@
class SA1Bus : public Bus {
public:
struct SA1Bus : Bus {
void init();
};
struct CPUIRAM : Memory {
unsigned size() const;
uint8_t read(unsigned);
void write(unsigned, uint8_t);
struct VectorSelectionPage : Memory {
alwaysinline uint8_t read(unsigned);
alwaysinline void write(unsigned, uint8_t);
void sync();
Memory *access;
};
struct CPUBWRAM : Memory {
bool cc1dma;
struct CC1BWRAM : Memory {
unsigned size() const;
uint8_t read(unsigned);
void write(unsigned, uint8_t);
alwaysinline uint8_t read(unsigned);
alwaysinline void write(unsigned, uint8_t);
bool dma;
};
struct SA1IRAM : Memory {
struct BitmapRAM : Memory {
unsigned size() const;
uint8_t read(unsigned);
void write(unsigned, uint8_t);
};
struct SA1BWRAM : Memory {
unsigned size() const;
uint8_t read(unsigned);
void write(unsigned, uint8_t);
};
struct SA1BitmapRAM : Memory {
unsigned size() const;
uint8_t read(unsigned);
void write(unsigned, uint8_t);
alwaysinline uint8_t read(unsigned);
alwaysinline void write(unsigned, uint8_t);
};
namespace memory {
namespace cpu {
extern CPUIRAM iram;
extern CPUBWRAM bwram;
}
namespace sa1 {
extern SA1IRAM iram;
extern SA1BWRAM bwram;
extern SA1BitmapRAM bitmapram;
}
extern VectorSelectionPage vectorsp;
extern StaticRAM iram;
extern MappedRAM &bwram;
extern CC1BWRAM cc1bwram;
extern BitmapRAM bitmapram;
}

View File

@ -9,9 +9,8 @@ void SA1::dma_normal() {
uint8_t data = regs.mdr;
uint32_t dsa = mmio.dsa++;
uint32_t dda = mmio.dda++;
add_clocks(4);
scheduler.sync_copcpu();
//source and destination cannot be the same
if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestBWRAM) continue;
if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestIRAM ) continue;
@ -29,7 +28,7 @@ void SA1::dma_normal() {
} break;
case DMA::SourceIRAM: {
data = iram[dsa & 0x07ff];
data = memory::iram.read(dsa & 0x07ff);
} break;
}
@ -41,25 +40,26 @@ void SA1::dma_normal() {
} break;
case DMA::DestIRAM: {
iram[dda & 0x07ff] = data;
memory::iram.write(dda & 0x07ff, data);
} break;
}
}
dma.mode = DMA::Inactive;
mmio.dma_irqfl = true;
if(mmio.dma_irqen) mmio.dma_irqcl = 0;
}
//((byte & 6) << 3) + (byte & 1) explanation:
//transforms a byte index (0-7) into a planar index:
//result[] = { 0, 1, 16, 17, 32, 33, 48, 49 };
//works for 2bpp, 4bpp and 8bpp modes
//===========================
//type-1 character conversion
//===========================
void SA1::dma_cc1() {
memory::cpu::bwram.cc1dma = true;
dma.tile = 0;
dma.mode = DMA::Inactive;
memory::cc1bwram.dma = true;
mmio.chdma_irqfl = true;
if(mmio.chdma_irqen) {
mmio.chdma_irqcl = 0;
@ -75,7 +75,7 @@ uint8_t SA1::dma_cc1_read(unsigned addr) {
//buffer next character to I-RAM
unsigned bpp = 2 << (2 - mmio.dmacb);
unsigned bpl = (8 << mmio.dmasize) >> mmio.dmacb;
unsigned bwmask = memory::sa1::bwram.size() - 1;
unsigned bwmask = memory::bwram.size() - 1;
unsigned tile = ((addr - mmio.dsa) & bwmask) >> (6 - mmio.dmacb);
unsigned ty = (tile >> mmio.dmasize);
unsigned tx = tile & ((1 << mmio.dmasize) - 1);
@ -83,8 +83,8 @@ uint8_t SA1::dma_cc1_read(unsigned addr) {
for(unsigned y = 0; y < 8; y++) {
uint64_t data = 0;
for(unsigned n = 0; n < bpp; n++) {
data |= (uint64_t)memory::sa1::bwram.read((bwaddr + n) & bwmask) << (n << 3);
for(unsigned byte = 0; byte < bpp; byte++) {
data |= (uint64_t)memory::bwram.read((bwaddr + byte) & bwmask) << (byte << 3);
}
bwaddr += bpl;
@ -102,15 +102,14 @@ uint8_t SA1::dma_cc1_read(unsigned addr) {
out[7] |= (data & 1) << (7 - x); data >>= 1;
}
for(unsigned n = 0; n < bpp; n++) {
static const unsigned index[] = { 0, 1, 16, 17, 32, 33, 48, 49 };
unsigned p = mmio.dda + (y << 1) + index[n];
iram[p & 0x07ff] = out[n];
for(unsigned byte = 0; byte < bpp; byte++) {
unsigned p = mmio.dda + (y << 1) + ((byte & 6) << 3) + (byte & 1);
memory::iram.write(p & 0x07ff, out[byte]);
}
}
}
return iram[(mmio.dda + (addr & charmask)) & 0x07ff];
return memory::iram.read((mmio.dda + (addr & charmask)) & 0x07ff);
}
//===========================
@ -118,7 +117,7 @@ uint8_t SA1::dma_cc1_read(unsigned addr) {
//===========================
void SA1::dma_cc2() {
//select register file index (0-7 or 8-F)
//select register file index (0-7 or 8-15)
const uint8_t *brf = &mmio.brf[(dma.line & 1) << 3];
unsigned bpp = 2 << (2 - mmio.dmacb);
unsigned addr = mmio.dda & 0x07ff;
@ -131,14 +130,9 @@ void SA1::dma_cc2() {
for(unsigned bit = 0; bit < 8; bit++) {
output |= ((brf[bit] >> byte) & 1) << (7 - bit);
}
static const unsigned index[] = { 0, 1, 16, 17, 32, 33, 48, 49 };
iram[addr + index[byte]] = output;
add_clocks(4);
scheduler.sync_copcpu();
memory::iram.write(addr + ((byte & 6) << 3) + (byte & 1), output);
}
dma.mode = DMA::Inactive;
dma.line = (dma.line + 1) & 15;
}

View File

@ -2,10 +2,6 @@ struct DMA {
enum CDEN { DmaNormal = 0, DmaCharConversion = 1 };
enum SD { SourceROM = 0, SourceBWRAM = 1, SourceIRAM = 2 };
enum DD { DestIRAM = 0, DestBWRAM = 1 };
enum Mode { Inactive, Normal, CC1, CC2 } mode;
unsigned clocks;
bool tile;
unsigned line;
} dma;

View File

@ -5,55 +5,35 @@
//==========================
void SA1::op_io() {
add_clocks(2);
tick();
if(regs.wai) scheduler.sync_copcpu();
cycle_edge();
}
//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks)
//BW-RAM is accessed at ~5.37MHz (4 clock ticks)
//tick() == 2 clock ticks
//note: bus conflict delays are not emulated at this time
#define is_bwram(addr) (\
((addr & 0x40e000) == 0x006000) \
|| ((addr & 0xf00000) == 0x400000) \
|| ((addr & 0xf00000) == 0x600000) \
)
uint8_t SA1::op_read(unsigned addr) {
add_clocks(bus_speed(addr));
tick();
if(is_bwram(addr)) tick();
scheduler.sync_copcpu();
regs.mdr = sa1bus.read(addr);
cycle_edge();
return regs.mdr;
return sa1bus.read(addr);
}
void SA1::op_write(unsigned addr, uint8_t data) {
add_clocks(bus_speed(addr));
tick();
if(is_bwram(addr)) tick();
scheduler.sync_copcpu();
sa1bus.write(addr, regs.mdr = data);
cycle_edge();
sa1bus.write(addr, data);
}
void SA1::cycle_edge() {
switch(dma.mode) {
case DMA::Normal: dma_normal(); break;
case DMA::CC1: dma_cc1(); break;
case DMA::CC2: dma_cc2(); break;
}
}
//$[00-3f:80-bf]:[8000-ffff]
//$[c0-ff] :[0000-ffff]
#define ROM(n) ( \
((n & 0x408000) == 0x008000) \
|| ((n & 0xc00000) == 0xc00000) \
)
//$[00-3f|80-bf]:[0000-07ff]
//$[00-3f|80-bf]:[3000-37ff]
#define IRAM(n) ( \
((n & 0x40f800) == 0x000000) \
|| ((n & 0x40f800) == 0x003000) \
)
unsigned SA1::bus_speed(unsigned addr) {
if(IRAM(addr)) return 2;
if(ROM(addr)) return (ROM(cpu.regs.bus) ? 4 : 2);
return 4; //MMIO, BW-RAM
}
#undef ROM
#undef IRAM
#undef is_bwram
#endif

View File

@ -1,5 +1,4 @@
void op_io();
uint8_t op_read(unsigned addr);
void op_write(unsigned addr, uint8_t data);
void cycle_edge();
unsigned bus_speed(unsigned addr);
alwaysinline void op_io();
alwaysinline uint8_t op_read(unsigned addr);
alwaysinline void op_write(unsigned addr, uint8_t data);
alwaysinline unsigned bus_speed(unsigned addr);

View File

@ -1,5 +1,13 @@
#ifdef SA1_CPP
//BS-X flash carts, when present, are mapped to 0x400000+
Memory& SA1::mmio_access(unsigned &addr) {
if(cartridge.bsx_flash_loaded() == false) return memory::cartrom;
if(addr < 0x400000) return memory::cartrom;
addr &= 0x3fffff;
return bsxflash;
}
//(CCNT) SA-1 control
void SA1::mmio_w2200(uint8_t data) {
if(mmio.sa1_resb && !(data & 0x80)) {
@ -86,17 +94,10 @@ void SA1::mmio_w2209(uint8_t data) {
//(CIE) SA-1 interrupt enable
void SA1::mmio_w220a(uint8_t data) {
if(!mmio.sa1_irqen && (data & 0x80)) {
if(mmio.sa1_irqfl) mmio.sa1_irqcl = 0;
}
if(!mmio.dma_irqen && (data & 0x20)) {
if(mmio.dma_irqfl) mmio.dma_irqcl = 0;
}
if(!mmio.sa1_nmien && (data & 0x10)) {
if(mmio.sa1_nmifl) mmio.sa1_nmicl = 0;
}
if(!mmio.sa1_irqen && (data & 0x80) && mmio.sa1_irqfl ) mmio.sa1_irqcl = 0;
if(!mmio.timer_irqen && (data & 0x40) && mmio.timer_irqfl) mmio.timer_irqcl = 0;
if(!mmio.dma_irqen && (data & 0x20) && mmio.dma_irqfl ) mmio.dma_irqcl = 0;
if(!mmio.sa1_nmien && (data & 0x10) && mmio.sa1_nmifl ) mmio.sa1_nmicl = 0;
mmio.sa1_irqen = (data & 0x80);
mmio.timer_irqen = (data & 0x40);
@ -111,17 +112,18 @@ void SA1::mmio_w220b(uint8_t data) {
mmio.dma_irqcl = (data & 0x20);
mmio.sa1_nmicl = (data & 0x10);
if(mmio.sa1_irqcl) mmio.sa1_irqfl = false;
if(mmio.dma_irqcl) mmio.dma_irqfl = false;
if(mmio.sa1_nmicl) mmio.sa1_nmifl = false;
if(mmio.sa1_irqcl) mmio.sa1_irqfl = false;
if(mmio.timer_irqcl) mmio.timer_irqfl = false;
if(mmio.dma_irqcl) mmio.dma_irqfl = false;
if(mmio.sa1_nmicl) mmio.sa1_nmifl = false;
}
//(SNV) S-CPU NMI vector
void SA1::mmio_w220c(uint8_t data) { mmio.snv = (mmio.snv & 0xff00) | data; }
void SA1::mmio_w220c(uint8_t data) { mmio.snv = (mmio.snv & 0xff00) | data; }
void SA1::mmio_w220d(uint8_t data) { mmio.snv = (data << 8) | (mmio.snv & 0xff); }
//(SIV) S-CPU IRQ vector
void SA1::mmio_w220e(uint8_t data) { mmio.siv = (mmio.siv & 0xff00) | data; }
void SA1::mmio_w220e(uint8_t data) { mmio.siv = (mmio.siv & 0xff00) | data; }
void SA1::mmio_w220f(uint8_t data) { mmio.siv = (data << 8) | (mmio.siv & 0xff); }
//(TMC) H/V timer control
@ -150,10 +152,21 @@ void SA1::mmio_w2220(uint8_t data) {
mmio.cbmode = (data & 0x80);
mmio.cb = (data & 0x07);
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, (mmio.cbmode == 0) ? 0x000000 : (mmio.cb << 20));
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, (mmio.cbmode == 0) ? 0x000000 : (mmio.cb << 20));
bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom, mmio.cb << 20);
sa1bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom, mmio.cb << 20);
unsigned addr = mmio.cb << 20;
Memory &access = mmio_access(addr);
if(mmio.cbmode == 0) {
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000);
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, 0x000000);
} else {
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr);
memory::vectorsp.sync();
}
//(DXB) Super MMC bank D
@ -161,10 +174,19 @@ void SA1::mmio_w2221(uint8_t data) {
mmio.dbmode = (data & 0x80);
mmio.db = (data & 0x07);
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, (mmio.dbmode == 0) ? 0x100000 : (mmio.db << 20));
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, (mmio.dbmode == 0) ? 0x100000 : (mmio.db << 20));
bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, memory::cartrom, mmio.db << 20);
sa1bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, memory::cartrom, mmio.db << 20);
unsigned addr = mmio.db << 20;
Memory &access = mmio_access(addr);
if(mmio.dbmode == 0) {
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000);
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, 0x100000);
} else {
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr);
}
//(EXB) Super MMC bank E
@ -172,10 +194,19 @@ void SA1::mmio_w2222(uint8_t data) {
mmio.ebmode = (data & 0x80);
mmio.eb = (data & 0x07);
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, (mmio.ebmode == 0) ? 0x200000 : (mmio.eb << 20));
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, (mmio.ebmode == 0) ? 0x200000 : (mmio.eb << 20));
bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, memory::cartrom, mmio.eb << 20);
sa1bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, memory::cartrom, mmio.eb << 20);
unsigned addr = mmio.eb << 20;
Memory &access = mmio_access(addr);
if(mmio.ebmode == 0) {
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000);
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, 0x200000);
} else {
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, access, addr);
}
//(FXB) Super MMC bank F
@ -183,18 +214,27 @@ void SA1::mmio_w2223(uint8_t data) {
mmio.fbmode = (data & 0x80);
mmio.fb = (data & 0x07);
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, (mmio.fbmode == 0) ? 0x300000 : (mmio.fb << 20));
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, (mmio.fbmode == 0) ? 0x300000 : (mmio.fb << 20));
bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, memory::cartrom, mmio.fb << 20);
sa1bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, memory::cartrom, mmio.fb << 20);
unsigned addr = mmio.fb << 20;
Memory &access = mmio_access(addr);
if(mmio.fbmode == 0) {
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000);
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, 0x300000);
} else {
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr);
}
bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr);
sa1bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, access, addr);
}
//(BMAPS) S-CPU BW-RAM address mapping
void SA1::mmio_w2224(uint8_t data) {
mmio.sbm = (data & 0x1f);
bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cpu::bwram, mmio.sbm * 0x2000, 0x2000);
bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cpu::bwram, mmio.sbm * 0x2000, 0x2000);
bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000);
bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000);
}
//(BMAP) SA-1 BW-RAM address mapping
@ -204,12 +244,12 @@ void SA1::mmio_w2225(uint8_t data) {
if(mmio.sw46 == 0) {
//$[40-43]:[0000-ffff] x 32 projection
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1::bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1::bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
} else {
//$[60-6f]:[0000-ffff] x 128 projection
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1::bitmapram, mmio.cbm * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1::bitmapram, mmio.cbm * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000);
}
}
@ -256,7 +296,7 @@ void SA1::mmio_w2231(uint8_t data) {
mmio.dmasize = (data >> 2) & 7;
mmio.dmacb = (data & 0x03);
if(mmio.chdend) memory::cpu::bwram.cc1dma = false;
if(mmio.chdend) memory::cc1bwram.dma = false;
if(mmio.dmasize > 5) mmio.dmasize = 5;
if(mmio.dmacb > 2) mmio.dmacb = 2;
}
@ -274,11 +314,11 @@ void SA1::mmio_w2235(uint8_t data) {
void SA1::mmio_w2236(uint8_t data) {
mmio.dda = (mmio.dda & 0xff00ff) | (data << 8);
if(dma.mode == DMA::Inactive) {
if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) {
dma.mode = DMA::Normal;
dma_normal();
} else if(mmio.cden == 1 && mmio.cdsel == 1) {
dma.mode = DMA::CC1;
dma_cc1();
}
}
}
@ -286,9 +326,9 @@ void SA1::mmio_w2236(uint8_t data) {
void SA1::mmio_w2237(uint8_t data) {
mmio.dda = (mmio.dda & 0x00ffff) | (data << 16);
if(dma.mode == DMA::Inactive) {
if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestBWRAM) {
dma.mode = DMA::Normal;
dma_normal();
}
}
}
@ -311,9 +351,9 @@ void SA1::mmio_w2244(uint8_t data) { mmio.brf[ 4] = data; }
void SA1::mmio_w2245(uint8_t data) { mmio.brf[ 5] = data; }
void SA1::mmio_w2246(uint8_t data) { mmio.brf[ 6] = data; }
void SA1::mmio_w2247(uint8_t data) { mmio.brf[ 7] = data;
if(dma.mode == DMA::Inactive) {
if(mmio.dmaen == true) {
if(mmio.cden == 1 && mmio.cdsel == 0) {
dma.mode = DMA::CC2;
dma_cc2();
}
}
}
@ -326,9 +366,9 @@ void SA1::mmio_w224c(uint8_t data) { mmio.brf[12] = data; }
void SA1::mmio_w224d(uint8_t data) { mmio.brf[13] = data; }
void SA1::mmio_w224e(uint8_t data) { mmio.brf[14] = data; }
void SA1::mmio_w224f(uint8_t data) { mmio.brf[15] = data;
if(dma.mode == DMA::Inactive) {
if(mmio.dmaen == true) {
if(mmio.cden == 1 && mmio.cdsel == 0) {
dma.mode = DMA::CC2;
dma_cc2();
}
}
}
@ -384,7 +424,7 @@ void SA1::mmio_w2254(uint8_t data) {
mmio.mr += (int16_t)mmio.ma * (int16_t)mmio.mb;
mmio.overflow = (mmio.mr >= (1ULL << 40));
mmio.mr &= (1ULL << 40) - 1;
mmio.ma = 0;
mmio.mb = 0;
}
}

View File

@ -1,5 +1,6 @@
uint8_t mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8_t data);
Memory& mmio_access(unsigned &addr);
struct MMIO {
//$2200 CCNT

View File

@ -1,5 +1,6 @@
#include <../base.hpp>
#include <../cart/cart.hpp>
#include <../chip/bsx/bsx.hpp>
#define SA1_CPP
#include "sa1.hpp"
@ -12,17 +13,10 @@ void SA1::enter() {
while(true) {
while(mmio.sa1_rdyb || mmio.sa1_resb) {
//SA-1 co-processor is asleep
add_clocks(4);
tick();
scheduler.sync_copcpu();
}
#if 0
static FILE *fp = fopen("/home/byuu/Desktop/sa1log.txt", "wb");
char t[1024];
disassemble_opcode(t);
fprintf(fp, "%s\n", t);
#endif
if(status.interrupt_pending) {
status.interrupt_pending = false;
interrupt(status.interrupt_vector);
@ -39,33 +33,23 @@ void SA1::last_cycle() {
mmio.sa1_nmifl = true;
mmio.sa1_nmicl = 1;
regs.wai = false;
return;
}
if(mmio.timer_irqen && !mmio.timer_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.timer_irqfl = true;
mmio.timer_irqcl = 1;
regs.wai = false;
return;
}
if(mmio.dma_irqen && !mmio.dma_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.dma_irqfl = true;
mmio.dma_irqcl = 1;
regs.wai = false;
return;
}
if(!regs.p.i && mmio.sa1_irq && !mmio.sa1_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.sa1_irqfl = true;
regs.wai = false;
return;
} else if(!regs.p.i) {
if(mmio.timer_irqen && !mmio.timer_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.timer_irqfl = true;
regs.wai = false;
} else if(mmio.dma_irqen && !mmio.dma_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.dma_irqfl = true;
regs.wai = false;
} else if(mmio.sa1_irq && !mmio.sa1_irqcl) {
status.interrupt_pending = true;
status.interrupt_vector = mmio.civ;
mmio.sa1_irqfl = true;
regs.wai = false;
}
}
}
@ -76,7 +60,6 @@ void SA1::interrupt(uint16_t vector) {
op_writestack(regs.pc.h);
op_writestack(regs.pc.l);
op_writestack(regs.e ? (regs.p & ~0x10) : regs.p);
add_clocks(8);
regs.pc.w = vector;
regs.pc.b = 0x00;
regs.p.i = 1;
@ -87,47 +70,37 @@ bool SA1::interrupt_pending() {
return status.interrupt_pending;
}
void SA1::add_clocks(unsigned clocks) {
scheduler.addclocks_cop(clocks);
uint16_t last_hcounter = status.hcounter;
uint16_t last_vcounter = status.vcounter;
void SA1::tick() {
scheduler.addclocks_cop(2);
//adjust counters:
//note that internally, status counters are in clocks;
//whereas MMIO register counters are in dots (4 clocks = 1 dot)
if(mmio.hvselb == 0) {
//HV timer
status.hcounter += clocks;
status.hcounter += 2;
if(status.hcounter >= 1364) {
status.hcounter -= 1364;
status.vcounter++;
if(status.vcounter >= status.scanlines) {
status.vcounter = 0;
}
status.hcounter = 0;
if(++status.vcounter >= status.scanlines) status.vcounter = 0;
}
} else {
//linear timer
status.hcounter += clocks;
status.hcounter += 2;
status.vcounter += (status.hcounter >> 11);
status.hcounter &= 0x07ff;
status.vcounter &= 0x01ff;
}
//test counters for timer IRQ
uint32_t lo = (last_vcounter << 11) + last_hcounter;
uint32_t hi = (status.vcounter << 11) + status.hcounter;
uint32_t trigger = (mmio.vcnt << 11) + (mmio.hcnt << 2);
if(lo > hi) {
if(trigger <= hi) goto trigger_irq;
hi += 1 << 20;
switch((mmio.ven << 1) + (mmio.hen << 0)) {
case 0: break;
case 1: if(status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break;
case 2: if(status.vcounter == mmio.vcnt && status.hcounter == 0) trigger_irq(); break;
case 3: if(status.vcounter == mmio.hcnt && status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break;
}
}
if(lo < trigger && trigger <= hi) goto trigger_irq;
return;
trigger_irq:
void SA1::trigger_irq() {
mmio.timer_irqfl = true;
if(mmio.timer_irqen) mmio.timer_irqcl = 0;
}
@ -146,6 +119,11 @@ void SA1::power() {
}
void SA1::reset() {
memory::vectorsp.access = 0;
memory::cc1bwram.dma = false;
for(unsigned addr = 0; addr < memory::iram.size(); addr++) {
memory::iram.write(addr, 0x00);
}
sa1bus.init();
regs.pc.d = 0x000000;
@ -160,19 +138,14 @@ void SA1::reset() {
regs.wai = false;
update_table();
memset(iram, 0, sizeof iram);
status.interrupt_pending = false;
status.interrupt_vector = 0x0000;
status.scanlines = (snes.region() == SNES::NTSC ? 261 : 311);
status.scanlines = (snes.region() == SNES::NTSC ? 262 : 312);
status.vcounter = 0;
status.hcounter = 0;
dma.mode = DMA::Inactive;
dma.clocks = 4;
dma.tile = 0;
dma.line = 0;
dma.line = 0;
//$2200 CCNT
mmio.sa1_irq = false;

View File

@ -5,7 +5,6 @@ public:
#include "dma/dma.hpp"
#include "memory/memory.hpp"
#include "mmio/mmio.hpp"
uint8_t iram[2048];
struct Status {
bool interrupt_pending;
@ -18,10 +17,11 @@ public:
void enter();
void interrupt(uint16_t vector);
void add_clocks(unsigned);
void tick();
void last_cycle();
bool interrupt_pending();
alwaysinline void trigger_irq();
alwaysinline void last_cycle();
alwaysinline bool interrupt_pending();
void init();
void enable();

View File

@ -11,7 +11,7 @@ void SDD1::enable() {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::read()
for(unsigned i = 0x4300; i <= 0x437f; i++) {
cpu_mmio[i & 0x7f] = memory::mmio.get(i);
cpu_mmio[i & 0x7f] = memory::mmio.mmio[i];
memory::mmio.map(i, *this);
}

View File

@ -71,10 +71,9 @@ struct regs_t {
uint8_t db;
bool e;
bool irq; //IRQ pin (0 = low, 1 = trigger)
bool wai; //raised during wai, cleared after interrupt triggered
uint32_t bus; //address on bus; -1U = I/O cycle
uint8_t mdr; //memory data register
bool irq; //IRQ pin (0 = low, 1 = trigger)
bool wai; //raised during wai, cleared after interrupt triggered
uint8_t mdr; //memory data register
regs_t() : db(0), e(false), irq(false), wai(false), bus(-1U), mdr(0) {}
regs_t() : db(0), e(false), irq(false), wai(false), mdr(0) {}
};

View File

@ -1,7 +1,6 @@
#ifdef SCPU_CPP
void sCPU::op_io() {
regs.bus = -1U;
status.clock_count = 6;
precycle_edge();
add_clocks(6);
@ -10,7 +9,7 @@ void sCPU::op_io() {
}
uint8 sCPU::op_read(uint32 addr) {
status.clock_count = speed(regs.bus = addr);
status.clock_count = speed(addr);
precycle_edge();
add_clocks(status.clock_count - 4);
@ -23,7 +22,7 @@ uint8 sCPU::op_read(uint32 addr) {
}
void sCPU::op_write(uint32 addr, uint8 data) {
status.clock_count = speed(regs.bus = addr);
status.clock_count = speed(addr);
precycle_edge();
add_clocks(status.clock_count);

View File

@ -12,10 +12,6 @@ priority_queue<unsigned> event(512, bind(&sCPU::queue_event, &cpu));
void sCPU::enter() {
regs.pc.l = bus.read(0xfffc);
regs.pc.h = bus.read(0xfffd);
//initial latch values for $213c/$213d
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
add_clocks(186);
while(true) {

View File

@ -26,12 +26,12 @@ void sCPU::scanline() {
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
status.line_clocks = ppu.lineclocks();
//forcefully sync S-CPU and S-SMP, in case chips are not communicating
if((ppu.vcounter() & 7) == 0) scheduler.sync_cpusmp();
if(ppu.vcounter() == 0) {
//hdma init triggers once every frame
event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit);
//forcefully sync S-CPU and S-SMP, in case chips are not communicating
scheduler.sync_cpusmp();
}
//dram refresh occurs once every scanline

View File

@ -12,7 +12,7 @@
#define REG(n) state.regs[r_##n]
#define VREG(n) state.regs[v.vidx + v_##n]
#if !defined(USE_STATE_MACHINE)
#if !defined(DSP_STATE_MACHINE)
#define phase_start() while(true) {
#define phase(n)
#define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp()

View File

@ -24,10 +24,6 @@ void MMIOAccess::map(unsigned addr, MMIO &access) {
mmio[(addr - 0x2000) & 0x3fff] = &access;
}
MMIO* MMIOAccess::get(unsigned addr) {
return mmio[(addr - 0x2000) & 0x3fff];
}
uint8 MMIOAccess::read(unsigned addr) {
return mmio[(addr - 0x2000) & 0x3fff]->mmio_read(addr);
}

View File

@ -56,16 +56,13 @@ private:
struct MMIOAccess : Memory {
void map(unsigned addr, MMIO &access);
MMIO* get(unsigned addr);
uint8 read(unsigned addr);
void write(unsigned addr, uint8 data);
private:
MMIO *mmio[0x4000];
};
class Bus {
public:
struct Bus {
unsigned mirror(unsigned addr, unsigned size);
void map(unsigned addr, Memory &access, unsigned offset);
enum MapMode { MapDirect, MapLinear, MapShadow };
@ -100,7 +97,6 @@ public:
Bus() {}
virtual ~Bus() {}
protected:
struct Page {
Memory *access;
unsigned offset;

View File

@ -16,11 +16,11 @@ void sSMP::enter() {
//forcefully sync S-CPU and S-SMP, in case chips are not communicating
static unsigned counter = 0;
if(++counter & 4096) {
if(++counter >= 128) {
counter = 0;
scheduler.sync_smpcpu();
}
}
}
#endif //ifdef SSMP_CPP
#endif

View File

@ -6,12 +6,6 @@
#include "timing/timing.cpp"
void sSMP::power() {
for(unsigned i = 0; i < memory::apuram.size(); i++) {
//SNES hardware APURAM contains pseudo-random data upon power up (exact formula is unknown.)
//memory::apuram.write(i, (i & 32) ? 0xff : 0x00);
memory::apuram.write(i, 0x00);
}
//targets not initialized/changed upon reset
t0.target = 0;
t1.target = 0;
@ -28,6 +22,10 @@ void sSMP::reset() {
regs.sp = 0xef;
regs.p = 0x02;
for(unsigned i = 0; i < memory::apuram.size(); i++) {
memory::apuram.write(i, 0x00);
}
status.clock_counter = 0;
status.dsp_counter = 0;

View File

@ -2,7 +2,7 @@
void sSMP::add_clocks(unsigned clocks) {
scheduler.addclocks_smp(clocks);
#if !defined(USE_STATE_MACHINE)
#if !defined(DSP_STATE_MACHINE)
scheduler.sync_smpdsp();
#else
while(scheduler.clock.smpdsp < 0) dsp.enter();