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 BSNES_TITLE "bsnes v" BSNES_VERSION
#define BUSCORE sBus #define BUSCORE sBus
@ -9,12 +9,7 @@
//S-DSP can be encapsulated into a state machine using #define magic //S-DSP can be encapsulated into a state machine using #define magic
//this avoids ~2.048m co_switch() calls per second (~5% speedup) //this avoids ~2.048m co_switch() calls per second (~5% speedup)
#define USE_STATE_MACHINE #define DSP_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
//game genie + pro action replay code support (~2% speed hit) //game genie + pro action replay code support (~2% speed hit)
#define CHEAT_SYSTEM #define CHEAT_SYSTEM

View File

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

View File

@ -1,16 +1,11 @@
#ifdef SA1_CPP #ifdef SA1_CPP
namespace memory { namespace memory {
namespace cpu { VectorSelectionPage vectorsp;
CPUIRAM iram; StaticRAM iram(2048);
CPUBWRAM bwram; MappedRAM &bwram = memory::cartram;
} CC1BWRAM cc1bwram;
BitmapRAM bitmapram;
namespace sa1 {
SA1IRAM iram;
SA1BWRAM bwram;
SA1BitmapRAM bitmapram;
}
} }
void SA1Bus::init() { void SA1Bus::init() {
@ -22,124 +17,103 @@ void SA1Bus::init() {
memory::mmio.map(i, sa1); 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(MapDirect, 0x00, 0x3f, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::sa1::iram); map(MapLinear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1::bwram); map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bwram);
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::sa1::bwram, 0, 0x040000); map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::bwram);
map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::sa1::bitmapram); map(MapLinear, 0x60, 0x6f, 0x0000, 0xffff, memory::bitmapram);
map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::sa1::iram); map(MapLinear, 0x80, 0xbf, 0x0000, 0x07ff, memory::iram);
map(MapDirect, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio); map(MapDirect, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio);
map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::sa1::iram); map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram);
map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1::bwram); map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bwram);
map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
map(MapLinear, 0xc0, 0xff, 0x0000, 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, 0x3000, 0x37ff, memory::iram);
bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cpu::bwram); bus.map(MapLinear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); bus.map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cpu::bwram, 0, 0x040000); bus.map(MapLinear, 0x40, 0x4f, 0x0000, 0xffff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::cpu::iram); bus.map(MapLinear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram);
bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cpu::bwram); bus.map(MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram);
bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); bus.map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
bus.map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom); bus.map(MapLinear, 0xc0, 0xff, 0x0000, 0xffff, memory::cartrom);
memory::vectorsp.sync();
} }
//======= //===================
//CPUIRAM //VectorSelectionPage
//======= //===================
unsigned CPUIRAM::size() const { //this class maps $00:[ff00-ffff] for the purpose of supporting:
return sizeof(sa1.iram); //$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) { void VectorSelectionPage::write(unsigned addr, uint8_t data) {
return sa1.iram[addr]; return access->write(addr, data);
} }
void CPUIRAM::write(unsigned addr, uint8_t data) { //call this whenever bus is remapped.
uint8_t wpbit = (addr >> 8) & 7; //note: S-CPU and SA-1 bus always share $00:[ff00-ffff] as cartridge ROM data;
if(1 || sa1.mmio.siwp & wpbit) { //the SA-1 MMC does not allow mapping these independently between processors.
//allow only when write-protection is disabled //this allows this class to be shared for both, caching only ones' access class.
sa1.iram[addr] = data; 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(); return memory::cartram.size();
} }
uint8_t CPUBWRAM::read(unsigned addr) { uint8_t CC1BWRAM::read(unsigned addr) {
if(cc1dma) return sa1.dma_cc1_read(addr); if(dma) return sa1.dma_cc1_read(addr);
return memory::cartram.read(addr); return memory::cartram.read(addr);
} }
void CPUBWRAM::write(unsigned addr, uint8_t data) { void CC1BWRAM::write(unsigned addr, uint8_t data) {
if(sa1.mmio.swen == false) {
//write-protection enabled
unsigned limit = 0x100 << sa1.mmio.bwp;
//if(addr < limit) return;
}
memory::cartram.write(addr, data); memory::cartram.write(addr, data);
} }
//======= //=========
//SA1IRAM //BitmapRAM
//======= //=========
unsigned SA1IRAM::size() const { unsigned BitmapRAM::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 {
return 0x100000; return 0x100000;
} }
uint8_t SA1BitmapRAM::read(unsigned addr) { uint8_t BitmapRAM::read(unsigned addr) {
if(sa1.mmio.bbf == 0) { if(sa1.mmio.bbf == 0) {
//4bpp //4bpp
unsigned shift = addr & 1; 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) { if(sa1.mmio.bbf == 0) {
//4bpp //4bpp
uint8_t shift = addr & 1; uint8_t shift = addr & 1;

View File

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

View File

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

View File

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

View File

@ -5,55 +5,35 @@
//========================== //==========================
void SA1::op_io() { void SA1::op_io() {
add_clocks(2); tick();
if(regs.wai) scheduler.sync_copcpu(); 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) { uint8_t SA1::op_read(unsigned addr) {
add_clocks(bus_speed(addr)); tick();
if(is_bwram(addr)) tick();
scheduler.sync_copcpu(); scheduler.sync_copcpu();
regs.mdr = sa1bus.read(addr); return sa1bus.read(addr);
cycle_edge();
return regs.mdr;
} }
void SA1::op_write(unsigned addr, uint8_t data) { void SA1::op_write(unsigned addr, uint8_t data) {
add_clocks(bus_speed(addr)); tick();
if(is_bwram(addr)) tick();
scheduler.sync_copcpu(); scheduler.sync_copcpu();
sa1bus.write(addr, regs.mdr = data); sa1bus.write(addr, data);
cycle_edge();
} }
void SA1::cycle_edge() { #undef is_bwram
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
#endif #endif

View File

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

View File

@ -1,5 +1,13 @@
#ifdef SA1_CPP #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 //(CCNT) SA-1 control
void SA1::mmio_w2200(uint8_t data) { void SA1::mmio_w2200(uint8_t data) {
if(mmio.sa1_resb && !(data & 0x80)) { if(mmio.sa1_resb && !(data & 0x80)) {
@ -86,17 +94,10 @@ void SA1::mmio_w2209(uint8_t data) {
//(CIE) SA-1 interrupt enable //(CIE) SA-1 interrupt enable
void SA1::mmio_w220a(uint8_t data) { void SA1::mmio_w220a(uint8_t data) {
if(!mmio.sa1_irqen && (data & 0x80)) { if(!mmio.sa1_irqen && (data & 0x80) && mmio.sa1_irqfl ) mmio.sa1_irqcl = 0;
if(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;
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;
}
mmio.sa1_irqen = (data & 0x80); mmio.sa1_irqen = (data & 0x80);
mmio.timer_irqen = (data & 0x40); mmio.timer_irqen = (data & 0x40);
@ -111,17 +112,18 @@ void SA1::mmio_w220b(uint8_t data) {
mmio.dma_irqcl = (data & 0x20); mmio.dma_irqcl = (data & 0x20);
mmio.sa1_nmicl = (data & 0x10); mmio.sa1_nmicl = (data & 0x10);
if(mmio.sa1_irqcl) mmio.sa1_irqfl = false; if(mmio.sa1_irqcl) mmio.sa1_irqfl = false;
if(mmio.dma_irqcl) mmio.dma_irqfl = false; if(mmio.timer_irqcl) mmio.timer_irqfl = false;
if(mmio.sa1_nmicl) mmio.sa1_nmifl = false; if(mmio.dma_irqcl) mmio.dma_irqfl = false;
if(mmio.sa1_nmicl) mmio.sa1_nmifl = false;
} }
//(SNV) S-CPU NMI vector //(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); } void SA1::mmio_w220d(uint8_t data) { mmio.snv = (data << 8) | (mmio.snv & 0xff); }
//(SIV) S-CPU IRQ vector //(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); } void SA1::mmio_w220f(uint8_t data) { mmio.siv = (data << 8) | (mmio.siv & 0xff); }
//(TMC) H/V timer control //(TMC) H/V timer control
@ -150,10 +152,21 @@ void SA1::mmio_w2220(uint8_t data) {
mmio.cbmode = (data & 0x80); mmio.cbmode = (data & 0x80);
mmio.cb = (data & 0x07); mmio.cb = (data & 0x07);
bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, (mmio.cbmode == 0) ? 0x000000 : (mmio.cb << 20)); unsigned addr = mmio.cb << 20;
sa1bus.map(Bus::MapLinear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom, (mmio.cbmode == 0) ? 0x000000 : (mmio.cb << 20)); Memory &access = mmio_access(addr);
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); 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 //(DXB) Super MMC bank D
@ -161,10 +174,19 @@ void SA1::mmio_w2221(uint8_t data) {
mmio.dbmode = (data & 0x80); mmio.dbmode = (data & 0x80);
mmio.db = (data & 0x07); mmio.db = (data & 0x07);
bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, (mmio.dbmode == 0) ? 0x100000 : (mmio.db << 20)); unsigned addr = mmio.db << 20;
sa1bus.map(Bus::MapLinear, 0x20, 0x3f, 0x8000, 0xffff, memory::cartrom, (mmio.dbmode == 0) ? 0x100000 : (mmio.db << 20)); Memory &access = mmio_access(addr);
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); 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 //(EXB) Super MMC bank E
@ -172,10 +194,19 @@ void SA1::mmio_w2222(uint8_t data) {
mmio.ebmode = (data & 0x80); mmio.ebmode = (data & 0x80);
mmio.eb = (data & 0x07); mmio.eb = (data & 0x07);
bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, (mmio.ebmode == 0) ? 0x200000 : (mmio.eb << 20)); unsigned addr = mmio.eb << 20;
sa1bus.map(Bus::MapLinear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom, (mmio.ebmode == 0) ? 0x200000 : (mmio.eb << 20)); Memory &access = mmio_access(addr);
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); 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 //(FXB) Super MMC bank F
@ -183,18 +214,27 @@ void SA1::mmio_w2223(uint8_t data) {
mmio.fbmode = (data & 0x80); mmio.fbmode = (data & 0x80);
mmio.fb = (data & 0x07); mmio.fb = (data & 0x07);
bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, (mmio.fbmode == 0) ? 0x300000 : (mmio.fb << 20)); unsigned addr = mmio.fb << 20;
sa1bus.map(Bus::MapLinear, 0xa0, 0xbf, 0x8000, 0xffff, memory::cartrom, (mmio.fbmode == 0) ? 0x300000 : (mmio.fb << 20)); Memory &access = mmio_access(addr);
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); 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 //(BMAPS) S-CPU BW-RAM address mapping
void SA1::mmio_w2224(uint8_t data) { void SA1::mmio_w2224(uint8_t data) {
mmio.sbm = (data & 0x1f); mmio.sbm = (data & 0x1f);
bus.map(Bus::MapLinear, 0x00, 0x3f, 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::cpu::bwram, 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 //(BMAP) SA-1 BW-RAM address mapping
@ -204,12 +244,12 @@ void SA1::mmio_w2225(uint8_t data) {
if(mmio.sw46 == 0) { if(mmio.sw46 == 0) {
//$[40-43]:[0000-ffff] x 32 projection //$[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, 0x00, 0x3f, 0x6000, 0x7fff, memory::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, 0x80, 0xbf, 0x6000, 0x7fff, memory::bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000);
} else { } else {
//$[60-6f]:[0000-ffff] x 128 projection //$[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, 0x00, 0x3f, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000);
sa1bus.map(Bus::MapLinear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1::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.dmasize = (data >> 2) & 7;
mmio.dmacb = (data & 0x03); 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.dmasize > 5) mmio.dmasize = 5;
if(mmio.dmacb > 2) mmio.dmacb = 2; 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) { void SA1::mmio_w2236(uint8_t data) {
mmio.dda = (mmio.dda & 0xff00ff) | (data << 8); mmio.dda = (mmio.dda & 0xff00ff) | (data << 8);
if(dma.mode == DMA::Inactive) { if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) { if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) {
dma.mode = DMA::Normal; dma_normal();
} else if(mmio.cden == 1 && mmio.cdsel == 1) { } 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) { void SA1::mmio_w2237(uint8_t data) {
mmio.dda = (mmio.dda & 0x00ffff) | (data << 16); mmio.dda = (mmio.dda & 0x00ffff) | (data << 16);
if(dma.mode == DMA::Inactive) { if(mmio.dmaen == true) {
if(mmio.cden == 0 && mmio.dd == DMA::DestBWRAM) { 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_w2245(uint8_t data) { mmio.brf[ 5] = data; }
void SA1::mmio_w2246(uint8_t data) { mmio.brf[ 6] = data; } void SA1::mmio_w2246(uint8_t data) { mmio.brf[ 6] = data; }
void SA1::mmio_w2247(uint8_t data) { mmio.brf[ 7] = 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) { 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_w224d(uint8_t data) { mmio.brf[13] = data; }
void SA1::mmio_w224e(uint8_t data) { mmio.brf[14] = data; } void SA1::mmio_w224e(uint8_t data) { mmio.brf[14] = data; }
void SA1::mmio_w224f(uint8_t data) { mmio.brf[15] = 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) { 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.mr += (int16_t)mmio.ma * (int16_t)mmio.mb;
mmio.overflow = (mmio.mr >= (1ULL << 40)); mmio.overflow = (mmio.mr >= (1ULL << 40));
mmio.mr &= (1ULL << 40) - 1; mmio.mr &= (1ULL << 40) - 1;
mmio.ma = 0; mmio.mb = 0;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -71,10 +71,9 @@ struct regs_t {
uint8_t db; uint8_t db;
bool e; bool e;
bool irq; //IRQ pin (0 = low, 1 = trigger) bool irq; //IRQ pin (0 = low, 1 = trigger)
bool wai; //raised during wai, cleared after interrupt triggered 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
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 #ifdef SCPU_CPP
void sCPU::op_io() { void sCPU::op_io() {
regs.bus = -1U;
status.clock_count = 6; status.clock_count = 6;
precycle_edge(); precycle_edge();
add_clocks(6); add_clocks(6);
@ -10,7 +9,7 @@ void sCPU::op_io() {
} }
uint8 sCPU::op_read(uint32 addr) { uint8 sCPU::op_read(uint32 addr) {
status.clock_count = speed(regs.bus = addr); status.clock_count = speed(addr);
precycle_edge(); precycle_edge();
add_clocks(status.clock_count - 4); add_clocks(status.clock_count - 4);
@ -23,7 +22,7 @@ uint8 sCPU::op_read(uint32 addr) {
} }
void sCPU::op_write(uint32 addr, uint8 data) { void sCPU::op_write(uint32 addr, uint8 data) {
status.clock_count = speed(regs.bus = addr); status.clock_count = speed(addr);
precycle_edge(); precycle_edge();
add_clocks(status.clock_count); add_clocks(status.clock_count);

View File

@ -12,10 +12,6 @@ priority_queue<unsigned> event(512, bind(&sCPU::queue_event, &cpu));
void sCPU::enter() { void sCPU::enter() {
regs.pc.l = bus.read(0xfffc); regs.pc.l = bus.read(0xfffc);
regs.pc.h = bus.read(0xfffd); 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); add_clocks(186);
while(true) { while(true) {

View File

@ -26,12 +26,12 @@ void sCPU::scanline() {
status.dma_counter = (status.dma_counter + status.line_clocks) & 7; status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
status.line_clocks = ppu.lineclocks(); 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) { if(ppu.vcounter() == 0) {
//hdma init triggers once every frame //hdma init triggers once every frame
event.enqueue(cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter(), EventHdmaInit); 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 //dram refresh occurs once every scanline

View File

@ -12,7 +12,7 @@
#define REG(n) state.regs[r_##n] #define REG(n) state.regs[r_##n]
#define VREG(n) state.regs[v.vidx + v_##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_start() while(true) {
#define phase(n) #define phase(n)
#define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp() #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[(addr - 0x2000) & 0x3fff] = &access;
} }
MMIO* MMIOAccess::get(unsigned addr) {
return mmio[(addr - 0x2000) & 0x3fff];
}
uint8 MMIOAccess::read(unsigned addr) { uint8 MMIOAccess::read(unsigned addr) {
return mmio[(addr - 0x2000) & 0x3fff]->mmio_read(addr); return mmio[(addr - 0x2000) & 0x3fff]->mmio_read(addr);
} }

View File

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

View File

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

View File

@ -6,12 +6,6 @@
#include "timing/timing.cpp" #include "timing/timing.cpp"
void sSMP::power() { 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 //targets not initialized/changed upon reset
t0.target = 0; t0.target = 0;
t1.target = 0; t1.target = 0;
@ -28,6 +22,10 @@ void sSMP::reset() {
regs.sp = 0xef; regs.sp = 0xef;
regs.p = 0x02; regs.p = 0x02;
for(unsigned i = 0; i < memory::apuram.size(); i++) {
memory::apuram.write(i, 0x00);
}
status.clock_counter = 0; status.clock_counter = 0;
status.dsp_counter = 0; status.dsp_counter = 0;

View File

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