Merge branch 'master' of https://github.com/finalburnneo/FBNeo into finalburnneo-master

This commit is contained in:
barbudreadmon 2024-11-09 10:13:11 +01:00
commit 1d1fcee97d
51 changed files with 48512 additions and 37 deletions

View File

@ -1,5 +1,5 @@
alldir = burn burn/devices burn/snd burn/drv burn/drv/atari burn/drv/capcom burn/drv/cave burn/drv/channelf burn/drv/coleco burn/drv/cps3 burn/drv/dataeast \
burn/drv/galaxian burn/drv/irem burn/drv/konami burn/drv/megadrive burn/drv/midway burn/drv/pce burn/drv/pst90s burn/drv/pre90s burn/drv/neogeo burn/drv/nes \
burn/drv/galaxian burn/drv/irem burn/drv/konami burn/drv/megadrive burn/drv/midway burn/drv/pce burn/drv/pst90s burn/drv/pre90s burn/drv/neogeo burn/drv/nes burn/drv/snes \
burn/drv/pgm burn/drv/psikyo burn/drv/sega burn/drv/sg1000 burn/drv/sms burn/drv/msx burn/drv/spectrum burn/drv/taito \
burn/drv/toaplan cpu cpu/a68k cpu/arm cpu/arm7 cpu/e132xs cpu/f8 cpu/h6280 cpu/hd6309 cpu/i386 cpu/i8039 cpu/i8x41 cpu/i8051 cpu/adsp2100 cpu/konami cpu/m377 cpu/mips3 cpu/m68k \
cpu/mb88xx cpu/m6502 cpu/m6800 cpu/m6805 cpu/m6809 cpu/nec cpu/pic16c5x cpu/s2650 cpu/tlcs90 cpu/tlcs900 cpu/sh2 cpu/sh4 cpu/tms32010 cpu/tms34 cpu/upd7725 cpu/upd7810 \
@ -40,6 +40,9 @@ drvsrc = d_akkaarrh.o d_arcadecl.o d_atarig1.o d_badlands.o d_batman.o d_blstro
\
d_nes.o \
\
d_snes.o apu.o cart.o cpu.o cx4.o dma.o dsp.o input.o ppu.o snes.o \
snes_other.o spc.o sa1.o sdd1.o cpu_sa1.o statehandler.o \
\
d_pgm.o \
\
d_psikyo.o d_psikyo4.o d_psikyosh.o \

View File

@ -69,7 +69,7 @@ depobj += neocdlist.o \
\
inp_sdl.o aud_sdl.o support_paths.o ips_manager.o scrn.o localise_gamelist.o \
cd_sdl2.o config.o main.o run.o stringset.o bzip.o drv.o media.o romdata.o \
inpdipsw.o vid_sdlfx.o inputbuf.o replay.o vid_sdlopengl.o input.o stated.o
inpdipsw.o vid_sdlfx.o inputbuf.o replay.o vid_sdlopengl.o input_sdl.o stated.o
ifdef INCLUDE_7Z_SUPPORT
depobj += un7z.o \
@ -173,7 +173,7 @@ CFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
$(PLATFLAGS) $(DEF) $(incdir)
CXXFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
-Wall -W -Wno-long-long \
-Wall -W -Wno-long-long -Wno-sign-compare \
-Wunknown-pragmas -Wundef -Wconversion -Wno-missing-braces \
-Wuninitialized -Wpointer-arith -Winline -Wno-multichar \
-Wno-conversion -Wno-attributes \

View File

@ -198,7 +198,7 @@ CFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
$(PLATFLAGS) $(DEF) $(incdir)
CXXFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \
-Wall -W -Wno-long-long \
-Wall -W -Wno-long-long -Wno-sign-compare \
-Wunknown-pragmas -Wundef -Wconversion -Wno-missing-braces \
-Wuninitialized -Wpointer-arith -Winline -Wno-multichar \
-Wno-conversion -Wno-attributes \

View File

@ -526,6 +526,7 @@ void IpsApplyPatches(UINT8* base, char* rom_name, UINT32 rom_crc, bool readonly
#define HARDWARE_PREFIX_FDS (0x1F000000)
#define HARDWARE_PREFIX_NGP (0x20000000)
#define HARDWARE_PREFIX_CHANNELF (0x21000000)
#define HARDWARE_PREFIX_SNES (0x22000000)
#define HARDWARE_SNK_NGP (HARDWARE_PREFIX_NGP | 0x00000000)
#define HARDWARE_SNK_NGPC (HARDWARE_PREFIX_NGP | 0x00000001) // must not be 0x10000
@ -765,6 +766,8 @@ void IpsApplyPatches(UINT8* base, char* rom_name, UINT32 rom_crc, bool readonly
#define HARDWARE_NES (HARDWARE_PREFIX_NES)
#define HARDWARE_FDS (HARDWARE_PREFIX_FDS)
#define HARDWARE_SNES (HARDWARE_PREFIX_SNES)
#define HARDWARE_SNES_ZAPPER (HARDWARE_PREFIX_SNES | 0x0000001)
#define HARDWARE_CHANNELF (HARDWARE_PREFIX_CHANNELF)

View File

@ -33,6 +33,7 @@ static UINT8 *DealerInputMultiplex;
static UINT8 dealer_hw = 0;
static UINT8 game_prot;
static INT32 is_theglob = 0;
static int watchdog;
@ -539,6 +540,7 @@ static void init_prot()
if (!strcmp(BurnDrvGetTextA(DRV_NAME), gamelist[i].set[setnum])) {
bprintf(0, _T("*** found prot for %S\n"), gamelist[i].set[setnum]);
game_prot = gamelist[i].prot;
is_theglob = (game_prot == 0x80);
break;
}
}
@ -643,7 +645,7 @@ static INT32 DrvInit()
ZetSetOutHandler(epos_write_port);
ZetClose();
AY8910Init(0, 687500, 0);
AY8910Init(0, (is_theglob) ? 2750000 : 687500, 0);
AY8910SetAllRoutes(0, 0.35, BURN_SND_ROUTE_BOTH);
AY8910SetBuffered(ZetTotalCycles, 2750000);
@ -718,6 +720,7 @@ static INT32 DrvExit()
BurnFreeMemIndex();
dealer_hw = 0;
is_theglob = 0;
return 0;
}

View File

@ -163,6 +163,7 @@ static void do_rle(INT32 which, INT32 data)
case 0x3000:
// data = framebuffer destination address "x" offset
// c1c = framebuffer destination address "y" offset -dink Nov, 2024
//bprintf(0, _T("do_rle, data/c1c: %x %x\n"), data, c1c[which]);
dstaddress += (data & 0x1ff) | ((c1c[which] & 0x1ff) * 0x200);
break;
}
@ -785,6 +786,7 @@ static INT32 DrvScan(INT32 nAction, INT32 *pnMin)
SCAN_VAR(fbbright1);
SCAN_VAR(fbbright2);
SCAN_VAR(regs1_address);
SCAN_VAR(c1c);
SCAN_VAR(prio_scrollx);
SCAN_VAR(prio_scrolly);
SCAN_VAR(regs1);

242
src/burn/drv/snes/apu.cpp Normal file
View File

@ -0,0 +1,242 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "apu.h"
#include "snes.h"
#include "spc.h"
#include "dsp.h"
#include "statehandler.h"
static uint8_t bootRom[0x40]; // algorithmically constructed
static const uint8_t iplDeltas[0x40] = {
0x18, 0x45, 0xeb, 0x61, 0x1c, 0x9a, 0xdc, 0x06, 0xa1, 0x26, 0x15, 0x07, 0x89, 0x96, 0xb8, 0xe2,
0xf5, 0xe1, 0x1e, 0xf1, 0xeb, 0x0a, 0xd2, 0xc0, 0xf7, 0x83, 0x7e, 0x60, 0x93, 0x40, 0x15, 0x46,
0x18, 0xf4, 0xdc, 0x7a, 0x31, 0x3d, 0x2d, 0x64, 0x30, 0xf8, 0x00, 0xcc, 0xa3, 0x67, 0x67, 0x23,
0xdc, 0xa2, 0x2f, 0xd7, 0xa6, 0x06, 0xd3, 0x84, 0xc4, 0xdc, 0xb8, 0x7f, 0x02, 0x86, 0x47, 0x6a,
};
static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0);
static const double apuCyclesPerMasterPal = (32040 * 32) / (1364 * 312 * 50.0);
static void apu_cycle(Apu* apu);
static uint8_t ipl_lfsr(uint32_t posTo, int32_t arrayPos) {
uint32_t seed = 0xa5; // it's magic! (tm)
uint32_t pos = 0;
do {
if ((pos ^ arrayPos) == posTo) break;
seed = (seed * 1664525 + 1013904223) % 256;
pos++;
} while (1);
return seed;
}
static void ipl_create() {
uint8_t prev = 0;
for (int i = 0; i < 0x40; i++) {
prev = (prev - iplDeltas[i]) & 0xff;
bootRom[i] = ipl_lfsr(prev, i);
prev = iplDeltas[i];
}
}
Apu* apu_init(Snes* snes) {
Apu* apu = (Apu*)BurnMalloc(sizeof(Apu));
apu->snes = snes;
apu->spc = spc_init(apu, apu_spcRead, apu_spcWrite, apu_spcIdle);
apu->dsp = dsp_init(apu);
ipl_create();
return apu;
}
void apu_free(Apu* apu) {
spc_free(apu->spc);
dsp_free(apu->dsp);
BurnFree(apu);
}
uint64_t apu_cycles(Apu* apu) {
return apu->cycles;
}
void apu_reset(Apu* apu) {
// TODO: hard reset for apu
spc_reset(apu->spc, true);
dsp_reset(apu->dsp);
memset(apu->ram, 0, sizeof(apu->ram));
apu->dspAdr = 0;
apu->romReadable = true;
apu->cycles = 0;
memset(apu->inPorts, 0, sizeof(apu->inPorts));
memset(apu->outPorts, 0, sizeof(apu->outPorts));
for(int i = 0; i < 3; i++) {
apu->timer[i].cycles = 0;
apu->timer[i].divider = 0;
apu->timer[i].target = 0;
apu->timer[i].counter = 0;
apu->timer[i].enabled = false;
}
}
void apu_handleState(Apu* apu, StateHandler* sh) {
sh_handleBools(sh, &apu->romReadable, NULL);
sh_handleBytes(sh,
&apu->dspAdr, &apu->inPorts[0], &apu->inPorts[1], &apu->inPorts[2], &apu->inPorts[3], &apu->inPorts[4],
&apu->inPorts[5], &apu->outPorts[0], &apu->outPorts[1], &apu->outPorts[2], &apu->outPorts[3], NULL
);
sh_handleLongLongs(sh, &apu->cycles, NULL);
for(int i = 0; i < 3; i++) {
sh_handleBools(sh, &apu->timer[i].enabled, NULL);
sh_handleBytes(sh, &apu->timer[i].cycles, &apu->timer[i].divider, &apu->timer[i].target, &apu->timer[i].counter, NULL);
}
sh_handleByteArray(sh, apu->ram, 0x10000);
// components
spc_handleState(apu->spc, sh);
dsp_handleState(apu->dsp, sh);
}
void apu_runCycles(Apu* apu) {
uint64_t sync_to = (uint64_t)apu->snes->cycles * (apu->snes->palTiming ? apuCyclesPerMasterPal : apuCyclesPerMaster);
while (apu->cycles < sync_to) {
spc_runOpcode(apu->spc);
}
}
static void apu_cycle(Apu* apu) {
if((apu->cycles & 0x1f) == 0) {
// every 32 cycles
dsp_cycle(apu->dsp);
}
// handle timers
for(int i = 0; i < 3; i++) {
if(apu->timer[i].cycles == 0) {
apu->timer[i].cycles = i == 2 ? 16 : 128;
if(apu->timer[i].enabled) {
apu->timer[i].divider++;
if(apu->timer[i].divider == apu->timer[i].target) {
apu->timer[i].divider = 0;
apu->timer[i].counter++;
apu->timer[i].counter &= 0xf;
}
}
}
apu->timer[i].cycles--;
}
apu->cycles++;
}
uint8_t apu_read(Apu* apu, uint16_t adr) {
switch(adr) {
case 0xf0:
case 0xf1:
case 0xfa:
case 0xfb:
case 0xfc: {
return 0;
}
case 0xf2: {
return apu->dspAdr;
}
case 0xf3: {
return dsp_read(apu->dsp, apu->dspAdr & 0x7f);
}
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9: {
return apu->inPorts[adr - 0xf4];
}
case 0xfd:
case 0xfe:
case 0xff: {
uint8_t ret = apu->timer[adr - 0xfd].counter;
apu->timer[adr - 0xfd].counter = 0;
return ret;
}
}
if(apu->romReadable && adr >= 0xffc0) {
return bootRom[adr - 0xffc0];
}
return apu->ram[adr];
}
void apu_write(Apu* apu, uint16_t adr, uint8_t val) {
switch(adr) {
case 0xf0: {
break; // test register
}
case 0xf1: {
for(int i = 0; i < 3; i++) {
if(!apu->timer[i].enabled && (val & (1 << i))) {
apu->timer[i].divider = 0;
apu->timer[i].counter = 0;
}
apu->timer[i].enabled = val & (1 << i);
}
if(val & 0x10) {
apu->inPorts[0] = 0;
apu->inPorts[1] = 0;
}
if(val & 0x20) {
apu->inPorts[2] = 0;
apu->inPorts[3] = 0;
}
apu->romReadable = val & 0x80;
break;
}
case 0xf2: {
apu->dspAdr = val;
break;
}
case 0xf3: {
if(apu->dspAdr < 0x80) dsp_write(apu->dsp, apu->dspAdr, val);
break;
}
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7: {
apu->outPorts[adr - 0xf4] = val;
break;
}
case 0xf8:
case 0xf9: {
apu->inPorts[adr - 0xf4] = val;
break;
}
case 0xfa:
case 0xfb:
case 0xfc: {
apu->timer[adr - 0xfa].target = val;
break;
}
}
apu->ram[adr] = val;
}
uint8_t apu_spcRead(void* mem, uint16_t adr) {
Apu* apu = (Apu*) mem;
apu_cycle(apu);
return apu_read(apu, adr);
}
void apu_spcWrite(void* mem, uint16_t adr, uint8_t val) {
Apu* apu = (Apu*) mem;
apu_cycle(apu);
apu_write(apu, adr, val);
}
void apu_spcIdle(void* mem, bool waiting) {
Apu* apu = (Apu*) mem;
apu_cycle(apu);
}

49
src/burn/drv/snes/apu.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef APU_H
#define APU_H
#include <stdint.h>
#include <stdbool.h>
#include "burnint.h"
typedef struct Apu Apu;
#include "snes.h"
#include "spc.h"
#include "dsp.h"
#include "statehandler.h"
typedef struct Timer {
uint8_t cycles;
uint8_t divider;
uint8_t target;
uint8_t counter;
bool enabled;
} Timer;
struct Apu {
Snes* snes;
Spc* spc;
Dsp* dsp;
uint8_t ram[0x10000];
bool romReadable;
uint8_t dspAdr;
uint64_t cycles;
uint8_t inPorts[6]; // includes 2 bytes of ram
uint8_t outPorts[4];
Timer timer[3];
};
Apu* apu_init(Snes* snes);
void apu_free(Apu* apu);
void apu_reset(Apu* apu);
uint64_t apu_cycles(Apu* apu);
void apu_handleState(Apu* apu, StateHandler* sh);
void apu_runCycles(Apu* apu);
uint8_t apu_read(Apu* apu, uint16_t adr);
void apu_write(Apu* apu, uint16_t adr, uint8_t val);
uint8_t apu_spcRead(void* mem, uint16_t adr);
void apu_spcWrite(void* mem, uint16_t adr, uint8_t val);
void apu_spcIdle(void* mem, bool waiting);
#endif

604
src/burn/drv/snes/cart.cpp Normal file
View File

@ -0,0 +1,604 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "cart.h"
#include "snes.h"
#include "statehandler.h"
#include "cx4.h"
#include "upd7725.h"
#include "sa1.h"
#include "sdd1.h"
static uint8_t cart_readDummy(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeDummy(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readLorom(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLorom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readExLorom(Cart* cart, uint8_t bank, uint16_t adr);
static uint8_t cart_readLoromSeta(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLoromSeta(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readLoromDSP(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLoromDSP(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readHirom(Cart* cart, uint8_t bank, uint16_t adr);
static uint8_t cart_readExHirom(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeHirom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readHiromDSP(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeHiromDSP(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readCX4(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeCX4(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readLoromSA1(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLoromSA1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readLoromOBC1(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLoromOBC1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
static uint8_t cart_readLoromSDD1(Cart* cart, uint8_t bank, uint16_t adr);
static void cart_writeLoromSDD1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
uint8_t (*cart_read)(Cart* cart, uint8_t bank, uint16_t adr) = NULL;
void (*cart_write)(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) = NULL;
void (*cart_run)() = NULL;
void cart_run_dummy() { }
void cart_mapRun(Cart* cart);
static void cart_mapRwHandlers(Cart* cart) {
switch(cart->type) {
case CART_NONE: cart_read = cart_readDummy; cart_write = cart_writeDummy; break;
case CART_LOROM: cart_read = cart_readLorom; cart_write = cart_writeLorom; break;
case CART_HIROM: cart_read = cart_readHirom; cart_write = cart_writeHirom; break;
case CART_EXLOROM: cart_read = cart_readExLorom; cart_write = cart_writeLorom; break;
case CART_EXHIROM: cart_read = cart_readExHirom; cart_write = cart_writeHirom; break;
case CART_CX4: cart_read = cart_readCX4; cart_write = cart_writeCX4; break;
case CART_LOROMDSP: cart_read = cart_readLoromDSP; cart_write = cart_writeLoromDSP; break;
case CART_HIROMDSP: cart_read = cart_readHiromDSP; cart_write = cart_writeHiromDSP; break;
case CART_LOROMSETA: cart_read = cart_readLoromSeta; cart_write = cart_writeLoromSeta; break;
case CART_LOROMSA1: cart_read = cart_readLoromSA1; cart_write = cart_writeLoromSA1; break;
case CART_LOROMOBC1: cart_read = cart_readLoromOBC1; cart_write = cart_writeLoromOBC1; break;
case CART_LOROMSDD1: cart_read = cart_readLoromSDD1; cart_write = cart_writeLoromSDD1; break;
default:
bprintf(0, _T("cart_mapRwHandlers(): invalid type specified: %x\n"), cart->type); break;
}
}
Cart* cart_init(Snes* snes) {
Cart* cart = (Cart*)BurnMalloc(sizeof(Cart));
cart->snes = snes;
cart->type = CART_NONE;
cart_mapRwHandlers(cart);
cart_mapRun(cart);
cart->hasBattery = 0;
cart->rom = NULL;
cart->romSize = 0;
cart->ram = NULL;
cart->ramSize = 0;
return cart;
}
// upd (dsp 1, 1b, 2, 3, 4) note, 1a and 1 are the same firmware
// Seta st010, st011
// TODO: put upd/dsp stuff into upd.cpp/h
static double upd_CyclesPerMaster;
static uint64_t upd_cycles;
static uint8_t *upd_ram = NULL;
static Snes *upd_snes_ctx;
void cart_free(Cart* cart) {
switch (cart->type) {
case CART_LOROMSA1: snes_sa1_exit(); break;
case CART_LOROMSDD1: snes_sdd1_exit(); break;
}
if(cart->rom != NULL) BurnFree(cart->rom);
if(cart->ram != NULL) BurnFree(cart->ram);
if(cart->bios != NULL) BurnFree(cart->bios);
if(upd_ram != NULL) BurnFree(upd_ram);
BurnFree(cart);
}
void upd_run() {
const uint64_t upd_sync_to = (uint64_t)upd_snes_ctx->cycles * upd_CyclesPerMaster;
int to_run = (int)((uint64_t)upd_sync_to - upd_cycles);
if (to_run > 0) {
upd_cycles += upd96050Run(to_run);
}
}
void cart_mapRun(Cart* cart) {
switch (cart->type) {
case CART_CX4: cart_run = cx4_run; break;
case CART_LOROMSA1: cart_run = snes_sa1_run; cart->heavySync = true; break;
case CART_LOROMDSP:
case CART_HIROMDSP:
case CART_LOROMSETA: cart_run = upd_run; break;
default: cart_run = cart_run_dummy; cart->heavySync = false; break;
}
}
void cart_reset(Cart* cart) {
// do not reset ram, assumed to be battery backed
bprintf(0, _T("cart reset! type %x\n"), cart->type);
switch (cart->type) {
case CART_LOROMSA1:
snes_sa1_init(cart->snes, cart->rom, cart->romSize, cart->ram, cart->ramSize);
snes_sa1_reset();
bprintf(0, _T("init/reset sa-1\n"));
break;
case CART_LOROMSDD1:
snes_sdd1_init(cart->rom, cart->romSize, cart->ram, cart->ramSize);
snes_sdd1_reset();
bprintf(0, _T("init/reset sdd-1\n"));
break;
case CART_CX4: // capcom cx4
cx4_init(cart->snes);
cx4_reset();
bprintf(0, _T("init/reset cx4\n"));
break;
case CART_LOROMDSP: // dsp lorom
case CART_HIROMDSP: // dsp hirom
upd_snes_ctx = cart->snes;
memset(upd_ram, 0, 0x200);
upd96050Init(7725, cart->bios, cart->bios + 0x2000, upd_ram, NULL, NULL);
upd96050Reset();
upd_CyclesPerMaster = (double)8000000 / ((cart->snes->palTiming) ? (1364 * 312 * 50.0) : (1364 * 262 * 60.0));
upd_cycles = 0;
bprintf(0, _T("init/reset dsp\n"));
break;
case CART_LOROMSETA: // Seta ST010/ST011 LoROM
upd_snes_ctx = cart->snes;
memset(upd_ram, 0, 0x1000);
upd96050Init(96050, cart->bios, cart->bios + 0x10000, upd_ram, NULL, NULL);
upd96050Reset();
upd_CyclesPerMaster = (double)11000000 / ((cart->snes->palTiming) ? (1364 * 312 * 50.0) : (1364 * 262 * 60.0));
upd_cycles = 0;
bprintf(0, _T("init/reset seta-dsp\n"));
break;
case CART_LOROMOBC1:
//?
break;
}
}
bool cart_handleTypeState(Cart* cart, StateHandler* sh) {
// when loading, return if values match
if(sh->saving) {
sh_handleBytes(sh, &cart->type, NULL);
sh_handleInts(sh, &cart->romSize, &cart->ramSize, NULL);
return true;
} else {
uint8_t type = 0;
uint32_t romSize = 0;
uint32_t ramSize = 0;
sh_handleBytes(sh, &type, NULL);
sh_handleInts(sh, &romSize, &ramSize, NULL);
return !(type != cart->type || romSize != cart->romSize || ramSize != cart->ramSize);
}
}
void upd_handleState(StateHandler* sh, int cart_type) {
void *upd_data = NULL;
int32_t upd_data_size = upd96050ExportRegs(&upd_data);
sh_handleByteArray(sh, (uint8_t*)upd_data, upd_data_size); // from upd7725 cpu core
sh_handleByteArray(sh, upd_ram, (cart_type == CART_LOROMSETA) ? 0x1000 : 0x200); // from upd7725 cpu core
sh_handleLongLongs(sh, &upd_cycles, NULL);
}
void cart_handleState(Cart* cart, StateHandler* sh) {
if(cart->ram != NULL) sh_handleByteArray(sh, cart->ram, cart->ramSize);
switch(cart->type) {
case CART_CX4: cx4_handleState(sh); break;
case CART_LOROMSA1: snes_sa1_handleState(sh); break;
case CART_LOROMDSP:
case CART_HIROMDSP:
case CART_LOROMSETA: upd_handleState(sh, cart->type); break;
case CART_LOROMSDD1: snes_sdd1_handleState(sh); break;
}
}
static void dsp_bios_reform(uint8_t *ori_bios, uint8_t *new_bios, int is_seta) {
const int bios_sizes[2] = { 0x2000, 0x10000 };
const int data_sizes[2] = { 0x800, 0x1000 };
bprintf(0, _T("bios_reform: seta? %x\n"), is_seta);
int bios_size = bios_sizes[is_seta];
int data_size = data_sizes[is_seta];
bprintf(0, _T("bios size / data size: %x %x\n"), bios_size, data_size);
UINT32 *newbios = (UINT32*)new_bios;
for (int i = 0; i < bios_size; i += 4) {
*newbios = (ori_bios[i + 0] << 24) | (ori_bios[i + 1] << 16) | (ori_bios[i + 2] << 8); // only uses 24bits!
newbios++;
}
UINT16 *newbios_data = (UINT16*)&new_bios[bios_size];
for (int i = 0; i < data_size; i += 2) {
*newbios_data = (ori_bios[bios_size + i + 0] << 8) | (ori_bios[bios_size + i + 1] << 0);
newbios_data++;
}
}
void cart_load(Cart* cart, int type, uint8_t* rom, int romSize, uint8_t* biosrom, int biosromSize, int ramSize, bool hasBattery) {
cart->type = type;
cart_mapRwHandlers(cart);
cart_mapRun(cart);
cart->hasBattery = hasBattery;
if(cart->rom != NULL) BurnFree(cart->rom);
if(cart->ram != NULL) BurnFree(cart->ram);
if(cart->bios != NULL) BurnFree(cart->bios);
cart->rom = BurnMalloc(romSize);
cart->romSize = romSize;
if(ramSize > 0) {
cart->ram = BurnMalloc(ramSize);
} else {
cart->ram = NULL;
}
// dsp[1-4] & seta st010/st011 bios memory init/re-format
if (biosromSize > 0 && (type == CART_LOROMDSP || type == CART_HIROMDSP || type == CART_LOROMSETA)) {
cart->bios = BurnMalloc(0x18000); // dsp 0x2800, Seta st010/st011: 0x11000
cart->biosSize = biosromSize;
dsp_bios_reform(biosrom, cart->bios, (type == CART_LOROMSETA));
upd_ram = (uint8_t*)BurnMalloc(0x2000); // 0x200 dsp, 0x800 seta
}
cart->ramSize = ramSize;
memcpy(cart->rom, rom, romSize);
}
bool cart_handleBattery(Cart* cart, bool save, uint8_t* data, int* size) {
if(cart->hasBattery == false) return false;
if(save) {
*size = cart->ramSize;
if(data == NULL) return true;
// assumes data is correct size
if(cart->ram != NULL) memcpy(data, cart->ram, cart->ramSize);
return true;
} else {
if(*size != cart->ramSize) return false;
if(cart->ram != NULL) memcpy(cart->ram, data, cart->ramSize);
return true;
}
}
static uint8_t cart_readDummy(Cart* cart, uint8_t bank, uint16_t adr) {
return cart->snes->openBus;
}
static void cart_writeDummy(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
}
#if 0
uint8_t cart_read_switched(Cart* cart, uint8_t bank, uint16_t adr) {
switch(cart->type) {
case CART_NONE: return cart->snes->openBus;
case CART_LOROM: return cart_readLorom(cart, bank, adr);
case CART_HIROM: return cart_readHirom(cart, bank, adr);
case CART_EXLOROM: return cart_readExLorom(cart, bank, adr);
case CART_EXHIROM: return cart_readExHirom(cart, bank, adr);
case CART_CX4: return cart_readCX4(cart, bank, adr);
case CART_LOROMDSP: return cart_readLoromDSP(cart, bank, adr);
case CART_HIROMDSP: return cart_readHiromDSP(cart, bank, adr);
case CART_LOROMSETA: return cart_readLoromSeta(cart, bank, adr);
case CART_LOROMSA1: return cart_readLoromSA1(cart, bank, adr);
case CART_LOROMOBC1: return cart_readLoromOBC1(cart, bank, adr);
case CART_LOROMSDD1: return cart_readLoromSDD1(cart, bank, adr);
}
return cart->snes->openBus;
}
void cart_write_switched(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
switch(cart->type) {
case CART_NONE: break;
case CART_LOROM: cart_writeLorom(cart, bank, adr, val); break;
case CART_HIROM: cart_writeHirom(cart, bank, adr, val); break;
case CART_EXLOROM: cart_writeLorom(cart, bank, adr, val); break;
case CART_EXHIROM: cart_writeHirom(cart, bank, adr, val); break;
case CART_CX4: cart_writeCX4(cart, bank, adr, val); break;
case CART_LOROMDSP: cart_writeLoromDSP(cart, bank, adr, val); break;
case CART_HIROMDSP: cart_writeHiromDSP(cart, bank, adr, val); break;
case CART_LOROMSETA: cart_writeLoromSeta(cart, bank, adr, val); break;
case CART_LOROMSA1: cart_writeLoromSA1(cart, bank, adr, val); break;
case CART_LOROMOBC1: cart_writeLoromOBC1(cart, bank, adr, val); break;
case CART_LOROMSDD1: cart_writeLoromSDD1(cart, bank, adr, val); break;
}
}
#endif
static uint8_t cart_readLorom(Cart* cart, uint8_t bank, uint16_t adr) {
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
bank &= 0x7f;
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static void cart_writeLorom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val;
}
}
static uint8_t cart_readExLorom(Cart* cart, uint8_t bank, uint16_t adr) {
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
const bool secondHalf = bank < 0x80;
bank &= 0x7f;
if(adr >= 0x8000) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[((bank << 15) | (secondHalf ? 0x400000 : 0) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static uint8_t cart_readLoromSeta(Cart* cart, uint8_t bank, uint16_t adr) {
if ((bank & 0x78) == 0x60 && (adr & 0xc000) == 0x0000) { // 60-67,0-3fff
cart_run();
return snesdsp_read(~adr & 0x01);
}
if ((bank & 0x78) == 0x68 && (adr & 0x8000) == 0x0000) { // 68-6f,0-7fff
cart_run();
return upd_ram[adr & 0xfff];
}
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
bank &= 0x7f;
if(adr >= 0x8000) {
// adr 8000-ffff in all other banks
return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static void cart_writeLoromSeta(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
if ((bank & 0x78) == 0x60 && (adr & 0xc000) == 0x0000) {
cart_run();
snesdsp_write(~adr & 0x01, val);
return;
}
if ((bank & 0x78) == 0x68 && (adr & 0x8000) == 0x0000) {
cart_run();
upd_ram[adr & 0xfff] = val;
return;
}
if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val;
}
}
static uint8_t cart_readLoromDSP(Cart* cart, uint8_t bank, uint16_t adr) {
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
bank &= 0x7f;
if ( ((bank & 0x70) == 0x30 && adr & 0x8000) || // 30-3f,b0-bf 8000-ffff
((bank & 0x70) == 0x60 && ~adr & 0x8000) ) { // super bases loaded 60-6f,e0-ef,0-7fff
cart_run();
return snesdsp_read(!(adr & 0x4000));
}
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
//bprintf(0, _T("missed.r %x.%x\n"), bank,adr);
return cart->snes->openBus;
}
static void cart_writeLoromDSP(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val;
}
// bprintf(0, _T("missed.w %x.%x %x\n"), bank,adr, val);
if ( ((bank & 0x70) == 0x30 && adr & 0x8000) || // 30-3f,b0-bf 8000-ffff
((bank & 0x70) == 0x60 && ~adr & 0x8000) ) { // super bases loaded 60-6f,e0-ef,0-7fff
cart_run();
snesdsp_write(!(adr & 0x4000), val);
}
}
static uint8_t cart_readCX4(Cart* cart, uint8_t bank, uint16_t adr) {
// cx4 mapper
if((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000) {
// banks 00-3f and 80-bf, adr 6000-7fff
return cx4_read(adr);
}
// save ram
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
bank &= 0x7f;
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static void cart_writeCX4(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
// cx4 mapper
if((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000) {
// banks 00-3f and 80-bf, adr 6000-7fff
cx4_write(adr, val);
}
// save ram
if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && adr < 0x8000 && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff
cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val;
}
}
static uint8_t cart_readHirom(Cart* cart, uint8_t bank, uint16_t adr) {
bank &= 0x7f;
if(bank >= 0x20 && bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) {
// banks 00-3f and 80-bf, adr 6000-7fff
return cart->ram[(((bank & 0x1f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)];
}
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[(((bank & 0x3f) << 16) | adr) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static void cart_writeHirom(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
bank &= 0x7f;
if(bank >= 0x20 && bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) {
// banks 00-3f and 80-bf, adr 6000-7fff
cart->ram[(((bank & 0x1f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)] = val;
}
}
// HiROM w/DSP
static uint8_t cart_readHiromDSP(Cart* cart, uint8_t bank, uint16_t adr) {
bank &= 0x7f;
if (bank < 0x20 && adr >= 0x6000 && adr < 0x8000) {
cart_run();
return snesdsp_read(!(adr & 0x1000));
}
if(bank >= 0x20 && bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) {
// banks 00-3f and 80-bf, adr 6000-7fff
return cart->ram[(((bank & 0x1f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)];
}
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[(((bank & 0x3f) << 16) | adr) & (cart->romSize - 1)];
}
// bprintf(0, _T("missed.r %x.%x\n"), bank,adr);
return cart->snes->openBus;
}
static void cart_writeHiromDSP(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
bank &= 0x7f;
if (bank < 0x20 && adr >= 0x6000 && adr < 0x8000) {
cart_run();
snesdsp_write(!(adr & 0x1000), val);
return;
}
// bprintf(0, _T("missed.w %x.%x %x\n"), bank,adr, val);
if(bank >= 0x20 && bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) {
// banks 00-3f and 80-bf, adr 6000-7fff
cart->ram[(((bank & 0x1f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)] = val;
}
}
static uint8_t cart_readExHirom(Cart* cart, uint8_t bank, uint16_t adr) {
if((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000 && cart->ramSize > 0) {
// banks 00-3f and 80-bf, adr 6000-7fff
return cart->ram[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (cart->ramSize - 1)];
}
bool secondHalf = bank < 0x80;
bank &= 0x7f;
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[(((bank & 0x3f) << 16) | (secondHalf ? 0x400000 : 0) | adr) & (cart->romSize - 1)];
}
return cart->snes->openBus;
}
static uint8_t cart_readLoromSA1(Cart* cart, uint8_t bank, uint16_t adr) {
return snes_sa1_cart_read(bank << 16 | adr);
}
static void cart_writeLoromSA1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
snes_sa1_cart_write(bank << 16 | adr, val);
}
#define obc1_offset (0x1800 | (~cart->ram[0x1ff5] & 0x01) << 10)
#define obc1_offset2 ((cart->ram[0x1ff6] & 0x7f) << 2)
static uint8_t cart_readLoromOBC1(Cart* cart, uint8_t bank, uint16_t adr) {
if(((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
return cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)];
}
bank &= 0x7f;
if(adr >= 0x8000 || bank >= 0x40) {
// adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
return cart->rom[((bank << 15) | (adr & 0x7fff)) & (cart->romSize - 1)];
}
if(bank < 0x40 && adr >= 0x6fff && adr <= 0x7fff) { // 00-3f,80-bf,6fff-7fff
adr &= 0x1fff;
switch (adr) {
case 0x1ff0:
case 0x1ff1:
case 0x1ff2:
case 0x1ff3:
return cart->ram[obc1_offset + obc1_offset2 + (adr & 0x03)];
case 0x1ff4:
return cart->ram[obc1_offset + (obc1_offset2 >> 4) + 0x200];
default:
return cart->ram[adr];
}
}
return cart->snes->openBus;
}
static void cart_writeLoromOBC1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
if(((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && ((cart->romSize >= 0x200000 && adr < 0x8000) || (cart->romSize < 0x200000)) && cart->ramSize > 0) {
// banks 70-7d and f0-ff, adr 0000-7fff & rom >= 2MB || adr 0000-ffff & rom < 2MB
cart->ram[(((bank & 0xf) << 15) | adr) & (cart->ramSize - 1)] = val;
}
bank &= 0x7f;
if(bank < 0x40 && adr >= 0x6fff && adr <= 0x7fff) { // 00-3f,80-bf,6fff-7fff
adr &= 0x1fff;
switch (adr) {
case 0x1ff0:
case 0x1ff1:
case 0x1ff2:
case 0x1ff3:
cart->ram[obc1_offset + obc1_offset2 + (adr & 0x03)] = val;
break;
case 0x1ff4:
cart->ram[obc1_offset + (obc1_offset2 >> 4) + 0x200] = (cart->ram[obc1_offset + (obc1_offset2 >> 4) + 0x200] & ~(3 << ((cart->ram[0x1ff6] & 0x03) << 1))) | ((val & 0x03) << ((cart->ram[0x1ff6] & 0x03) << 1));
break;
default:
cart->ram[adr] = val;
break;
}
}
}
static uint8_t cart_readLoromSDD1(Cart* cart, uint8_t bank, uint16_t adr) {
return snes_sdd1_cart_read(bank << 16 | adr);
}
static void cart_writeLoromSDD1(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val) {
snes_sdd1_cart_write(bank << 16 | adr, val);
}

42
src/burn/drv/snes/cart.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef CART_H
#define CART_H
#include <stdint.h>
#include <stdbool.h>
typedef struct Cart Cart;
#include "snes.h"
#include "statehandler.h"
//const char* typeNames[12] = {"(none)", "LoROM", "HiROM", "ExLoROM", "ExHiROM", "CX4", "LoROM-DSP", "HiROM-DSP", "LoROM-SeTa", "LoROM-SA1", "LoROM-OBC1", "LoROM-SDD1"};
enum { CART_NONE = 0, CART_LOROM, CART_HIROM, CART_EXLOROM, CART_EXHIROM, CART_CX4, CART_LOROMDSP, CART_HIROMDSP, CART_LOROMSETA, CART_LOROMSA1, CART_LOROMOBC1, CART_LOROMSDD1 };
struct Cart {
Snes* snes;
uint8_t type;
bool hasBattery;
bool heavySync;
uint8_t* rom;
uint32_t romSize;
uint8_t* ram;
uint32_t ramSize;
uint8_t* bios;
uint32_t biosSize;
};
// TODO: how to handle reset & load?
Cart* cart_init(Snes* snes);
void cart_free(Cart* cart);
void cart_reset(Cart* cart); // will reset special chips etc, general reading is set up in load
bool cart_handleTypeState(Cart* cart, StateHandler* sh);
void cart_handleState(Cart* cart, StateHandler* sh);
void cart_load(Cart* cart, int type, uint8_t* rom, int romSize, uint8_t* biosrom, int biosromSize, int ramSize, bool hasBattery); // loads rom, sets up ram buffer
bool cart_handleBattery(Cart* cart, bool save, uint8_t* data, int* size); // saves/loads ram
extern uint8_t (*cart_read)(Cart* cart, uint8_t bank, uint16_t adr);
extern void (*cart_write)(Cart* cart, uint8_t bank, uint16_t adr, uint8_t val);
extern void (*cart_run)(); // runs special co-processor chips, if avail
#endif

4255
src/burn/drv/snes/cpu.cpp Normal file

File diff suppressed because it is too large Load Diff

32
src/burn/drv/snes/cpu.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef CPU_H
#define CPU_H
#include <stdint.h>
#include <stdbool.h>
#include "statehandler.h"
#if 0
typedef uint8_t (*CpuReadHandler)(void* mem, uint32_t adr);
typedef void (*CpuWriteHandler)(void* mem, uint32_t adr, uint8_t val);
typedef void (*CpuIdleHandler)(void* mem, bool waiting);
#endif
typedef struct Cpu Cpu;
struct Cpu {
};
void cpu_init(void* mem);
void cpu_free();
void cpu_reset(bool hard);
void cpu_handleState(StateHandler* sh);
void cpu_runOpcode();
void cpu_nmi();
void cpu_setIrq(bool state);
void cpu_setIntDelay();
bool cpu_isVector();
uint32_t cpu_getPC();
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
#ifndef CPU_SA1_H
#define CPU_SA1_H
#include <stdint.h>
#include <stdbool.h>
#include "statehandler.h"
#if 0
typedef uint8_t (*CpuReadHandler)(void* mem, uint32_t adr);
typedef void (*CpuWriteHandler)(void* mem, uint32_t adr, uint8_t val);
typedef void (*CpuIdleHandler)(void* mem, bool waiting);
void cpusa1_init(void* mem, CpuReadHandler read, CpuWriteHandler write, CpuIdleHandler idle);
#endif
void cpusa1_init();
void cpusa1_free();
void cpusa1_reset(bool hard);
void cpusa1_handleState(StateHandler* sh);
void cpusa1_runOpcode();
void cpusa1_nmi();
void cpusa1_setIrq(bool state);
void cpusa1_setIntDelay();
void cpusa1_setHalt(bool haltValue);
uint32_t cpusa1_getPC();
bool cpusa1_isVector();
#endif

859
src/burn/drv/snes/cx4.cpp Normal file
View File

@ -0,0 +1,859 @@
// cx4 simulator for LakeSnes, (c) 2023 dink
// License: MIT
// Big Thanks:
// Ikari, Nocash, Overload, Segher for all the CX4 research and documentation!
// Rupert Carmichael for help with degree interpolation
// Atan for pie recipe
// Notes:
//1: Since neither X2 or X3 relies on: this core doesn't emulate
// 2 of the cache peculiarities documented by ikari: Roll over to
// second prg-cache on end of first & stop at end of second cache
//2: ~224-cycle/frame deficit
// Where does it come from? Possible cpu startup/stop latency?
// Related to cache population (7f48)? Somewhere else?
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>
#include "snes.h"
#include "cx4.h"
#include "burnint.h"
#define DEBUG_STARTSTOP 0
#define DEBUG_CACHE 0
#define DEBUG_DMA 0
// CPU condition codes
enum {
cx4_CC_Z = 1 << 0, // Zero
cx4_CC_C = 1 << 1, // Carry
cx4_CC_N = 1 << 2, // Negative
cx4_CC_V = 1 << 3, // oVerflow
cx4_CC_I = 1 << 4 // Interrupt
};
// BUS state
enum {
B_IDLE = 0,
B_READ = 1,
B_WRITE = 2
};
// IRQ config
enum {
IRQ_ACKNOWLEDGE = 1 << 0
};
#define set_flg(flg, f) do { cx4.cc = (cx4.cc & ~flg) | ((f) ? flg : 0); } while (0)
#define get_flg(flg) (!!(cx4.cc & flg))
#define set_Z(f) set_flg(cx4_CC_Z, f)
#define get_Z() get_flg(cx4_CC_Z)
#define set_C(f) set_flg(cx4_CC_C, f)
#define get_C() get_flg(cx4_CC_C)
#define set_N(f) set_flg(cx4_CC_N, f)
#define get_N() get_flg(cx4_CC_N)
#define set_V(f) set_flg(cx4_CC_V, f)
#define get_V() get_flg(cx4_CC_V)
#define set_I(f) set_flg(cx4_CC_I, f)
#define get_I() get_flg(cx4_CC_I)
#define set_NZ(f) do { set_N(f & 0x800000); set_Z(!f); } while (0)
#define set_A(f) do { cx4.A = (f) & 0xffffff; } while (0)
#define get_A() (cx4.A << ((0x10080100 >> (8 * sub_op)) & 0xff))
#define set_byte(var, data, offset) (var = (var & (~(0xff << (((offset) & 3) * 8)))) | (data << (((offset) & 3) * 8)))
#define get_byte(var, offset) (var >> ((offset) & 3) * 8)
#define sign_extend(f, frombits) ((int32_t)((f) << (32 - (frombits))) >> (32 - (frombits)))
#define struct_sizeto(type, member) offsetof(type, member) + sizeof(((type*)0)->member)
struct Stack {
uint32_t PC;
uint32_t PB;
};
struct CX4 {
uint64_t cycles;
uint64_t cycles_start;
uint64_t suspend_timer;
uint32_t running;
uint32_t prg_base_address;
uint16_t prg_startup_bank;
uint8_t prg_startup_pc;
uint8_t prg_cache_page;
uint8_t prg_cache_lock;
uint32_t prg_cache[2];
uint16_t prg[2][0x100];
int32_t prg_cache_timer;
uint8_t PC;
uint16_t PB;
uint16_t PB_latch;
uint8_t cc;
uint32_t A;
uint32_t SP;
Stack stack[0x08];
uint32_t reg[0x10];
uint8_t vectors[0x20];
uint8_t ram[0x400 * 3];
uint64_t multiplier;
// bus
uint32_t bus_address;
uint32_t bus_mode;
uint32_t bus_data;
int32_t bus_timer;
// cpu registers (bus)
uint32_t bus_address_pointer;
uint32_t ram_address_pointer;
uint32_t rom_data;
uint32_t ram_data;
uint8_t irqcfg;
uint8_t unkcfg;
uint8_t waitstate;
uint32_t dma_source;
uint32_t dma_dest;
uint16_t dma_length;
int32_t dma_timer;
// - calculated @ init -
int32_t struct_data_length;
double CyclesPerMaster;
uint64_t sync_to;
uint32_t rom[0x400];
Snes *snes;
};
static CX4 cx4;
void cx4_init(void *mem)
{
cx4.snes = (Snes *)mem;
cx4.CyclesPerMaster = (double)20000000 / ((cx4.snes->palTiming) ? (1364 * 312 * 50.0) : (1364 * 262 * 60.0));
cx4.struct_data_length = struct_sizeto(CX4, dma_timer);
double pi = atan(1) * 4;
for (int i = 0; i < 0x100; i++) {
cx4.rom[0x000 + i] = (i == 0) ? 0xffffff : (0x800000 / i);
cx4.rom[0x100 + i] = 0x100000 * sqrt(i);
}
for (int i = 0; i < 0x80; i++) {
cx4.rom[0x200 + i] = 0x1000000 * sin(((i * 90.0) / 128.0) * pi / 180.0);
cx4.rom[0x280 + i] = 0x800000 / (90.0 * pi / 180.0) * asin(i / 128.0);
cx4.rom[0x300 + i] = 0x10000 * (tan(((i * 90.0) / 128.0) * pi / 180.0) + 0.00000001); // 0x340 needs a little push
cx4.rom[0x380 + i] = (i == 0) ? 0xffffff : (0x1000000 * cos(((double)(i * 90.0) / 128.0) * pi / 180.0));
}
// test validity of generated rom
int64_t hash = 0;
for (int i = 0; i < 0x400; i++) {
hash += cx4.rom[i];
}
if (hash != 0x169c91535) {
bprintf(PRINT_ERROR, _T("CX4 rom generation failed (bad hash, %I64x)\n"), hash);
}
}
void cx4_reset()
{
memset(&cx4, 0, cx4.struct_data_length);
cx4.A = 0xffffff;
cx4.cc = 0x00;
cx4.running = 0;
cx4.unkcfg = 1;
cx4.waitstate = 0x33;
cx4.bus_mode = B_IDLE;
}
void cx4_handleState(StateHandler* sh)
{
sh_handleByteArray(sh, (uint8_t*)&cx4, cx4.struct_data_length);
}
#define CACHE_PAGE 0x100
static uint32_t resolve_cache_address()
{
return cx4.prg_base_address + cx4.PB * (CACHE_PAGE << 1);
}
static int find_cache(uint32_t address)
{
for (int i = 0; i < 2; i++) {
if (cx4.prg_cache[i] == address) {
return i;
}
}
return -1;
}
static void populate_cache(uint32_t address)
{
cx4.prg_cache_timer = 224; // what is the source of this? (re: note at top of file)
if (cx4.prg_cache[cx4.prg_cache_page] == address) return;
#if 0
int temp = -1;
if ((temp = find_cache(address)) != -1) {
bprintf(0, _T("populate cache is already cached! %x\n"), address);
cx4.prg_cache_page = temp;
return;
}
#endif
#if DEBUG_CACHE
bprintf(0, _T("caching bank %x @ cache pg. %x (prev: %x)\n"), cx4.PB, cx4.prg_cache_page, cx4.prg_cache[cx4.prg_cache_page]);
#endif
cx4.prg_cache[cx4.prg_cache_page] = address;
for (int i = 0; i < CACHE_PAGE; i++) {
cx4.prg[cx4.prg_cache_page][i] = (snes_read(cx4.snes, address++) << 0) | (snes_read(cx4.snes, address++) << 8);
}
cx4.prg_cache_timer += ((cx4.waitstate & 0x07) * CACHE_PAGE) * 2;
#if DEBUG_CACHE
bprintf(0, _T("cache loaded, cycles %d\n"), cx4.prg_cache_timer);
#endif
}
static void do_cache()
{
int new_page;
#if DEBUG_CACHE
bprintf(0, _T("cache list: %x %x\n"), cx4.prg_cache[0], cx4.prg_cache[1]);
#endif
// is our page cached?
if ((new_page = find_cache(resolve_cache_address())) != -1) {
//bprintf(0, _T("our page is already cached, yay.\n"));
cx4.prg_cache_page = new_page;
return;
} else {
// not cached, go to next slot
cx4.prg_cache_page = (cx4.prg_cache_page + 1) & 1;
#if 0
extern int counter;
if (counter) {
// Locked page issue:
// (X2) after boss battle, on the "You got ..." screen: the blue raster box
// flickers in for a frame or 2 before it fades in, because:
// Cache pg 0 is locked with bank 0, pg1 is unlocked
// pg 1 has to swap between pb 2 and 9 about 100 times per frame, and
// this eats a lot of cycles!
// can we use this slot?
if (cx4.prg_cache_lock & (1 << cx4.prg_cache_page)) {
bprintf(0, _T("-> page %x is locked (with %x) ...\n"), cx4.prg_cache_page, cx4.prg_cache[cx4.prg_cache_page]);
cx4.prg_cache_page = (cx4.prg_cache_page + 1) & 1;
// how about the other one?
if (cx4.prg_cache_lock & (1 << cx4.prg_cache_page)) {
bprintf(0, _T("CX4: we can't cache, operations terminated.\n"));
cx4.running = 0;
return; // not cached, can't cache. uhoh!
}
}
}
#endif
}
populate_cache(resolve_cache_address());
}
static void cycle_advance(int32_t cyc)
{
if (cx4.bus_timer) {
cx4.bus_timer -= cyc;
if (cx4.bus_timer < 1) {
switch (cx4.bus_mode) {
case B_READ: cx4.bus_data = snes_read(cx4.snes, cx4.bus_address); break;
case B_WRITE: snes_write(cx4.snes, cx4.bus_address, cx4.bus_data); break;
}
cx4.bus_mode = B_IDLE;
cx4.bus_timer = 0;
}
}
cx4.cycles += cyc;
}
static uint16_t fetch()
{
#if 0
// debug: bypass cache
uint16_t opcode = 0;
uint32_t address = (cx4.prg_base_address + (cx4.PB * (CACHE_PAGE << 1)) + (cx4.PC << 1)) & 0xffffff;
opcode = snes_read(cx4.snes, address++);
opcode |= snes_read(cx4.snes, address++) << 8;
#else
const uint16_t opcode = cx4.prg[cx4.prg_cache_page][cx4.PC];
#endif
cx4.PC++;
if (cx4.PC == 0) {
bprintf(0, _T("PC == 0! PB / Next: %x %x\n"), cx4.PB, cx4.PB_latch);
cx4.PB = cx4.PB_latch;
do_cache();
}
cycle_advance(1);
return opcode;
}
#define is_internal_ram(a) ((a & 0x40e000) == 0x6000)
static uint32_t get_waitstate(uint32_t address)
{
// assumptions: waitstate is always the same for cart ROM and RAM
// .waitstate 0x33 (boot) 0x44 (set by X2/X3)
return (is_internal_ram(address)) ? 0 : (cx4.waitstate & 0x07);
}
static void do_dma()
{
uint32_t dest = cx4.dma_dest;
uint32_t source = cx4.dma_source;
uint32_t dest_cyc = get_waitstate(dest);
uint32_t source_cyc = get_waitstate(source);
#if DEBUG_DMA
bprintf(0, _T("dma\tsrc/dest/len: %x %x %x\n"), source, dest, cx4.dma_length);
#endif
for (int i = 0; i < cx4.dma_length; i++) {
snes_write(cx4.snes, dest++, snes_read(cx4.snes, source++));
}
cx4.dma_timer = cx4.dma_length * (1 + dest_cyc + source_cyc);
#if DEBUG_DMA
bprintf(0, _T("dma end, cycles %d\n"), cx4.dma_timer);
#endif
}
uint8_t cx4_read(uint32_t address)
{
cx4_run(); // get up-to-date
if ((address & 0xfff) < 0xc00) {
return cx4.ram[address & 0xfff];
}
if (address >= 0x7f80 && (address & 0x3f) <= 0x2f) {
address &= 0x3f;
return get_byte(cx4.reg[address / 3], address % 3);
}
switch (address) {
case 0x7f40: return (cx4.dma_source >> 0) & 0xff;
case 0x7f41: return (cx4.dma_source >> 8) & 0xff;
case 0x7f42: return (cx4.dma_source >> 16) & 0xff;
case 0x7f43: return (cx4.dma_length >> 0) & 0xff;
case 0x7f44: return (cx4.dma_length >> 8) & 0xff;
case 0x7f45: return (cx4.dma_dest >> 0) & 0xff;
case 0x7f46: return (cx4.dma_dest >> 8) & 0xff;
case 0x7f47: return (cx4.dma_dest >> 16) & 0xff;
case 0x7f48: return cx4.prg_cache_page;
case 0x7f49: return (cx4.prg_base_address >> 0) & 0xff;
case 0x7f4a: return (cx4.prg_base_address >> 8) & 0xff;
case 0x7f4b: return (cx4.prg_base_address >> 16) & 0xff;
case 0x7f4c: return cx4.prg_cache_lock;
case 0x7f4d: return (cx4.prg_startup_bank >> 0) & 0xff;
case 0x7f4e: return (cx4.prg_startup_bank >> 8) & 0xff;
case 0x7f4f: return cx4.prg_startup_pc;
case 0x7f50: return cx4.waitstate;
case 0x7f51: return cx4.irqcfg;
case 0x7f52: return cx4.unkcfg;
case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56:
case 0x7f57: case 0x7f59: case 0x7f5b: case 0x7f5c:
case 0x7f5d: case 0x7f5e: case 0x7f5f: {
// cx4 status (address 58 & 5a not mapped, returns 0)
// tr.. ..is
// t.transfer in-progress (bus, cache, dma)
// r.running or transfer in-progress
// i.irq flag
// s.processor suspended
const int transfer = (cx4.prg_cache_timer > 0) || (cx4.bus_timer > 0) || (cx4.dma_timer > 0);
const int running = transfer || cx4.running;
const uint8_t res = (transfer << 7) | (running << 6) | (get_I() << 1) | (cx4.suspend_timer != 0);
return res;
}
case 0x7f60: case 0x7f61: case 0x7f62: case 0x7f63:
case 0x7f64: case 0x7f65: case 0x7f66: case 0x7f67:
case 0x7f68: case 0x7f69: case 0x7f6a: case 0x7f6b:
case 0x7f6c: case 0x7f6d: case 0x7f6e: case 0x7f6f:
case 0x7f70: case 0x7f71: case 0x7f72: case 0x7f73:
case 0x7f74: case 0x7f75: case 0x7f76: case 0x7f77:
case 0x7f78: case 0x7f79: case 0x7f7a: case 0x7f7b:
case 0x7f7c: case 0x7f7d: case 0x7f7e: case 0x7f7f:
// this provides the vector table for when the cx4 chip disconnects
// the rom(s) from the bus during cpu/transfer operations
return cx4.vectors[address & 0x1f];
}
return 0;
}
void cx4_write(uint32_t address, uint8_t data)
{
cx4_run();
if ((address & 0xfff) < 0xc00) {
cx4.ram[address & 0xfff] = data;
return;
}
if (address >= 0x7f80 && (address & 0x3f) <= 0x2f) {
address &= 0x3f;
set_byte(cx4.reg[address / 3], data, address % 3);
return;
}
switch (address) {
case 0x7f40: cx4.dma_source = (cx4.dma_source & 0xffff00) | (data << 0); break;
case 0x7f41: cx4.dma_source = (cx4.dma_source & 0xff00ff) | (data << 8); break;
case 0x7f42: cx4.dma_source = (cx4.dma_source & 0x00ffff) | (data << 16); break;
case 0x7f43: cx4.dma_length = (cx4.dma_length & 0xff00) | (data << 0); break;
case 0x7f44: cx4.dma_length = (cx4.dma_length & 0x00ff) | (data << 8); break;
case 0x7f45: cx4.dma_dest = (cx4.dma_dest & 0xffff00) | (data << 0); break;
case 0x7f46: cx4.dma_dest = (cx4.dma_dest & 0xff00ff) | (data << 8); break;
case 0x7f47: cx4.dma_dest = (cx4.dma_dest & 0x00ffff) | (data << 16); do_dma(); break;
case 0x7f48: cx4.prg_cache_page = data & 0x01; populate_cache(resolve_cache_address()); break;
case 0x7f49: cx4.prg_base_address = (cx4.prg_base_address & 0xffff00) | (data << 0); break;
case 0x7f4a: cx4.prg_base_address = (cx4.prg_base_address & 0xff00ff) | (data << 8); break;
case 0x7f4b: cx4.prg_base_address = (cx4.prg_base_address & 0x00ffff) | (data << 16); break;
case 0x7f4c: cx4.prg_cache_lock = data & 0x03; break;
case 0x7f4d: cx4.prg_startup_bank = (cx4.prg_startup_bank & 0xff00) | data; break;
case 0x7f4e: cx4.prg_startup_bank = (cx4.prg_startup_bank & 0x00ff) | ((data & 0x7f) << 8); break;
case 0x7f4f:
cx4.prg_startup_pc = data;
if (cx4.running == 0) {
cx4.PB = cx4.prg_startup_bank;
cx4.PC = cx4.prg_startup_pc;
cx4.running = 1;
cx4.cycles_start = cx4.cycles;
#if DEBUG_STARTSTOP
bprintf(0, _T("cx4 start @ %I64u - "), cx4.cycles);
bprintf(0, _T("cache PB: %x\tPC: %x\tcache: %x\n"), cx4.PB, cx4.PC, resolve_cache_address());
#endif
do_cache();
}
break;
case 0x7f50: cx4.waitstate = data & 0x77; break; // oooo aaaa o.rom, a.ram
case 0x7f51:
cx4.irqcfg = data & 0x01;
if (cx4.irqcfg & IRQ_ACKNOWLEDGE) {
cpu_setIrq(false);
set_I(0);
}
break;
case 0x7f52: cx4.unkcfg = data & 0x01; break; // this is up for debate, previously thought to en/disable 2nd rom chip on certain carts
case 0x7f53: cx4.running = 0; bprintf(0, _T("7f53: CX4 - stop running!\n")); break;
case 0x7f55: case 0x7f56: case 0x7f57: case 0x7f58:
case 0x7f59: case 0x7f5a: case 0x7f5b: case 0x7f5c: {
const int32_t offset = (address - 0x7f55);
cx4.suspend_timer = (offset == 0) ? -1 : (offset << 5);
break;
}
case 0x7f5d: cx4.suspend_timer = 0; break;
case 0x7f5e: set_I(0); break;
case 0x7f60: case 0x7f61: case 0x7f62: case 0x7f63:
case 0x7f64: case 0x7f65: case 0x7f66: case 0x7f67:
case 0x7f68: case 0x7f69: case 0x7f6a: case 0x7f6b:
case 0x7f6c: case 0x7f6d: case 0x7f6e: case 0x7f6f:
case 0x7f70: case 0x7f71: case 0x7f72: case 0x7f73:
case 0x7f74: case 0x7f75: case 0x7f76: case 0x7f77:
case 0x7f78: case 0x7f79: case 0x7f7a: case 0x7f7b:
case 0x7f7c: case 0x7f7d: case 0x7f7e: case 0x7f7f:
cx4.vectors[address & 0x1f] = data; break;
}
}
// special function (purpose?) registers
static uint32_t get_sfr(uint8_t address)
{
switch (address & 0x7f) {
case 0x01: return (cx4.multiplier >> 24) & 0xffffff;
case 0x02: return (cx4.multiplier >> 0) & 0xffffff;
case 0x03: return cx4.bus_data;
case 0x08: return cx4.rom_data;
case 0x0c: return cx4.ram_data;
case 0x13: return cx4.bus_address_pointer;
case 0x1c: return cx4.ram_address_pointer;
case 0x20: return cx4.PC;
case 0x28: return cx4.PB_latch;
case 0x2e: // rom
case 0x2f: // ram
cx4.bus_timer = ((cx4.waitstate >> ((~address & 1) << 2)) & 0x07) + 1;
cx4.bus_address = cx4.bus_address_pointer;
cx4.bus_mode = B_READ;
return 0;
case 0x50: return 0x000000;
case 0x51: return 0xffffff;
case 0x52: return 0x00ff00;
case 0x53: return 0xff0000;
case 0x54: return 0x00ffff;
case 0x55: return 0xffff00;
case 0x56: return 0x800000;
case 0x57: return 0x7fffff;
case 0x58: return 0x008000;
case 0x59: return 0x007fff;
case 0x5a: return 0xff7fff;
case 0x5b: return 0xffff7f;
case 0x5c: return 0x010000;
case 0x5d: return 0xfeffff;
case 0x5e: return 0x000100;
case 0x5f: return 0x00feff;
case 0x60: case 0x61: case 0x62: case 0x63:
case 0x64: case 0x65: case 0x66: case 0x67:
case 0x68: case 0x69: case 0x6a: case 0x6b:
case 0x6c: case 0x6d: case 0x6e: case 0x6f:
return cx4.reg[address & 0x0f];
}
bprintf(0, _T("get_sfr() - invalid %x\n"), address);
return 0;
}
static void set_sfr(uint8_t address, uint32_t data)
{
switch (address & 0x7f) {
case 0x01: cx4.multiplier = (cx4.multiplier & 0x000000ffffff) | ((uint64_t)data << 24); break;
case 0x02: cx4.multiplier = (cx4.multiplier & 0xffffff000000) | ((uint64_t)data << 0); break;
case 0x03: cx4.bus_data = data; break;
case 0x08: cx4.rom_data = data; break;
case 0x0c: cx4.ram_data = data; break;
case 0x13: cx4.bus_address_pointer = data; break;
case 0x1c: cx4.ram_address_pointer = data; break;
case 0x20: cx4.PC = data; break;
case 0x28: cx4.PB_latch = (data & 0x7fff); break;
case 0x2e: // rom
case 0x2f: // ram
cx4.bus_timer = ((cx4.waitstate >> ((~address & 1) << 2)) & 0x07) + 1;
cx4.bus_address = cx4.bus_address_pointer;
cx4.bus_mode = B_WRITE;
break;
case 0x60: case 0x61: case 0x62: case 0x63:
case 0x64: case 0x65: case 0x66: case 0x67:
case 0x68: case 0x69: case 0x6a: case 0x6b:
case 0x6c: case 0x6d: case 0x6e: case 0x6f:
cx4.reg[address & 0x0f] = data; break;
default:
bprintf(0, _T("set_sfr() - invalid %x (%x)\n"), address, data);
break;
}
}
static void jmpjsr(bool is_jsr, bool take, uint8_t page, uint8_t address) {
if (take) {
if (is_jsr) {
cx4.stack[cx4.SP].PC = cx4.PC;
cx4.stack[cx4.SP].PB = cx4.PB;
cx4.SP = (cx4.SP + 1) & 0x07;
}
if (page) {
cx4.PB = cx4.PB_latch;
do_cache();
}
cx4.PC = address;
cycle_advance(2);
}
}
static uint32_t add(uint32_t a1, uint32_t a2)
{
const uint32_t sum = a1 + a2;
set_C(sum & 0xff000000);
set_V(((a1 ^ sum) & (a2 ^ sum)) & 0x800000);
set_N(sum & 0x800000);
set_Z(!(sum & 0xffffff));
return sum & 0xffffff;
}
static uint32_t sub(uint32_t m, uint32_t s)
{
const int32_t diff = m - s;
set_C(!(diff < 0));
set_V(((m ^ diff) & (s ^ diff)) & 0x800000);
set_NZ(diff);
return diff & 0xffffff;
}
#define DIRECT_IMM 0x0400
#define get_immed() ((opcode & DIRECT_IMM) ? immed : get_sfr(immed))
static void run_insn()
{
const uint16_t opcode = fetch();
const uint8_t sub_op = (opcode & 0x0300) >> 8;
const uint8_t immed = (opcode & 0x00ff) >> 0;
uint32_t temp = 0;
switch (opcode & 0xfc00) {
case 0x0000: // nop
break;
case 0x0800: // jmp page,pc
jmpjsr(false, true, sub_op, immed); break;
case 0x0c00: // jmp if flag,page,pc
jmpjsr(false, get_Z(), sub_op, immed); break;
case 0x1000:
jmpjsr(false, get_C(), sub_op, immed); break;
case 0x1400:
jmpjsr(false, get_N(), sub_op, immed); break;
case 0x1800:
jmpjsr(false, get_V(), sub_op, immed); break;
case 0x2800: // jsr page,pc
jmpjsr(true, true, sub_op, immed); break;
case 0x2c00: // jsr if flag,page,pc
jmpjsr(true, get_Z(), sub_op, immed); break;
case 0x3000:
jmpjsr(true, get_C(), sub_op, immed); break;
case 0x3400:
jmpjsr(true, get_N(), sub_op, immed); break;
case 0x3800:
jmpjsr(true, get_V(), sub_op, immed); break;
case 0x3c00: // return
cx4.SP = (cx4.SP - 1) & 0x07;
cx4.PC = cx4.stack[cx4.SP].PC;
cx4.PB = cx4.stack[cx4.SP].PB;
do_cache();
cycle_advance(2);
break;
case 0x1c00: // finish/execute bus transfer
cycle_advance(cx4.bus_timer);
break;
case 0x2400: // skip cc,imm
if (!!(cx4.cc & (1 << ((0x13 >> sub_op) & 3))) == immed) { // note: re-indexes processor flags to match order of sub_op [O,C,Z,N]
fetch();
}
break;
case 0x4000: // inc bus address
cx4.bus_address_pointer = (cx4.bus_address_pointer + 1) & 0xffffff;
break;
case 0x4800: // cmp imm,A
case 0x4c00:
sub(get_immed(), get_A());
break;
case 0x5000: // cmp A,imm
case 0x5400:
sub(get_A(), get_immed());
break;
case 0x5800: // sign_extend A[?,8,16,? bit]
cx4.A = sign_extend(cx4.A, sub_op << 3) & 0xffffff;
set_NZ(cx4.A);
break;
case 0x6000: // mov x,imm
case 0x6400:
switch (sub_op) {
case 0: cx4.A = get_immed(); break;
case 1: cx4.bus_data = get_immed(); break;
case 2: cx4.bus_address_pointer = get_immed(); break;
case 3: cx4.PB_latch = get_immed() & 0x7fff; break;
}
break;
case 0xe000: // mov sfr[imm],x
switch (sub_op) {
case 0: set_sfr(immed, cx4.A); break;
case 1: set_sfr(immed, cx4.bus_data); break;
case 2: set_sfr(immed, cx4.bus_address_pointer); break;
case 3: set_sfr(immed, cx4.PB_latch); break;
}
break;
case 0x6800: // RDRAM subop,A
temp = cx4.A & 0xfff;
if (temp < 0xc00) {
set_byte(cx4.ram_data, cx4.ram[temp], sub_op);
}
break;
case 0x6c00: // RDRAM imm,A
temp = (cx4.ram_address_pointer + immed) & 0xfff;
if (temp < 0xc00) {
set_byte(cx4.ram_data, cx4.ram[temp], sub_op);
}
break;
case 0xe800: // WRRAM subop,A
temp = cx4.A & 0xfff;
if (temp < 0xc00) {
cx4.ram[temp] = get_byte(cx4.ram_data, sub_op);
}
break;
case 0xec00: // WRRAM imm,A
temp = (cx4.ram_address_pointer + immed) & 0xfff;
if (temp < 0xc00) {
cx4.ram[temp] = get_byte(cx4.ram_data, sub_op);
}
break;
case 0x7000: // RDROM
cx4.rom_data = cx4.rom[cx4.A & 0x3ff];
break;
case 0x7400:
cx4.rom_data = cx4.rom[(sub_op << 8) | immed];
break;
case 0x7c00: // mov PB_latch[l/h],imm
set_byte(cx4.PB_latch, immed, sub_op);
cx4.PB_latch &= 0x7fff;
break;
case 0x8000: // ADD A,imm
case 0x8400:
cx4.A = add(get_A(), get_immed());
break;
case 0x8800: // SUB imm,A
case 0x8c00:
cx4.A = sub(get_immed(), get_A());
break;
case 0x9000: // SUB A,imm
case 0x9400:
cx4.A = sub(get_A(), get_immed());
break;
case 0x9800: // MUL imm,A
case 0x9c00:
cx4.multiplier = ((int64_t)sign_extend(get_immed(), 24) * sign_extend(cx4.A, 24)) & 0xffffffffffff;
break;
case 0xa000: // XNOR A,imm
case 0xa400:
set_A(~(get_A()) ^ get_immed());
set_NZ(cx4.A);
break;
case 0xa800: // XOR A,imm
case 0xac00:
set_A((get_A()) ^ get_immed());
set_NZ(cx4.A);
break;
case 0xb000: // AND A,imm
case 0xb400:
set_A((get_A()) & get_immed());
set_NZ(cx4.A);
break;
case 0xb800: // OR A,imm
case 0xbc00:
set_A((get_A()) | get_immed());
set_NZ(cx4.A);
break;
case 0xc000: // SHR A,imm
case 0xc400:
set_A(cx4.A >> (get_immed() & 0x1f));
set_NZ(cx4.A);
break;
case 0xc800: // ASR A,imm
case 0xcc00:
set_A(sign_extend(cx4.A, 24) >> (get_immed() & 0x1f));
set_NZ(cx4.A);
break;
case 0xd000: // ROR A,imm
case 0xd400:
temp = get_immed() & 0x1f;
set_A((cx4.A >> temp) | (cx4.A << (24 - temp)));
set_NZ(cx4.A);
break;
case 0xd800: // SHL A,imm
case 0xdc00:
set_A(cx4.A << (get_immed() & 0x1f));
set_NZ(cx4.A);
break;
case 0xf000: // XCHG A,regs
temp = cx4.A;
cx4.A = cx4.reg[immed & 0xf];
cx4.reg[immed & 0xf] = temp;
break;
case 0xf800: // clear
cx4.A = cx4.ram_address_pointer = cx4.ram_data = cx4.PB_latch = 0x00;
break;
case 0xfc00: // stop
#if DEBUG_STARTSTOP
bprintf(0, _T("cx4 OP-stop, cycles ran %d\n"), (int)((int64_t)cx4.cycles - cx4.cycles_start));
#endif
cx4.running = 0;
if (~cx4.irqcfg & IRQ_ACKNOWLEDGE) {
set_I(1);
cpu_setIrq(true);
}
break;
}
}
static void tally_cycles()
{
cx4.sync_to = (uint64_t)cx4.snes->cycles * cx4.CyclesPerMaster;
}
static inline int64_t cycles_left()
{
return cx4.sync_to - cx4.cycles;
}
void cx4_run()
{
int tcyc = 0;
tally_cycles();
while (cx4.cycles < cx4.sync_to) {
if (cx4.prg_cache_timer) {
tcyc = (cycles_left() > cx4.prg_cache_timer) ? cx4.prg_cache_timer : 1;
cycle_advance(tcyc);
cx4.prg_cache_timer -= tcyc;
} else if (cx4.dma_timer) {
tcyc = (cycles_left() > cx4.dma_timer) ? cx4.dma_timer : 1;
cycle_advance(tcyc);
cx4.dma_timer -= tcyc;
} else if (cx4.suspend_timer) {
tcyc = (cycles_left() > cx4.suspend_timer) ? cx4.suspend_timer : 1;
cycle_advance(tcyc);
cx4.suspend_timer -= tcyc;
} else if (!cx4.running) {
cycle_advance(cycles_left());
} else {
run_insn();
}
}
}

12
src/burn/drv/snes/cx4.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef CX4_H
#define CX4_H
void cx4_init(void *mem);
uint8_t cx4_read(uint32_t addr);
void cx4_write(uint32_t addr, uint8_t value);
void cx4_run();
void cx4_reset();
void cx4_handleState(StateHandler* sh);
#endif

31827
src/burn/drv/snes/d_snes.cpp Normal file

File diff suppressed because it is too large Load Diff

404
src/burn/drv/snes/dma.cpp Normal file
View File

@ -0,0 +1,404 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "dma.h"
#include "snes.h"
#include "statehandler.h"
static const int bAdrOffsets[8][4] = {
{0, 0, 0, 0},
{0, 1, 0, 1},
{0, 0, 0, 0},
{0, 0, 1, 1},
{0, 1, 2, 3},
{0, 1, 0, 1},
{0, 0, 0, 0},
{0, 0, 1, 1}
};
static const int transferLength[8] = {
1, 2, 2, 4, 4, 4, 2, 4
};
static void dma_transferByte(Dma* dma, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB);
static void dma_checkHdma(Dma* dma);
static void dma_doDma(Dma* dma, int cpuCycles);
static void dma_initHdma(Dma* dma, bool doSync, int cpuCycles);
static void dma_doHdma(Dma* dma, bool doSync, int cpuCycles);
Dma* dma_init(Snes* snes) {
Dma* dma = (Dma*)BurnMalloc(sizeof(Dma));
dma->snes = snes;
return dma;
}
void dma_free(Dma* dma) {
BurnFree(dma);
}
void dma_reset(Dma* dma) {
for(int i = 0; i < 8; i++) {
dma->channel[i].bAdr = 0xff;
dma->channel[i].aAdr = 0xffff;
dma->channel[i].aBank = 0xff;
dma->channel[i].size = 0xffff;
dma->channel[i].indBank = 0xff;
dma->channel[i].tableAdr = 0xffff;
dma->channel[i].repCount = 0xff;
dma->channel[i].unusedByte = 0xff;
dma->channel[i].dmaActive = false;
dma->channel[i].hdmaActive = false;
dma->channel[i].mode = 7;
dma->channel[i].fixed = true;
dma->channel[i].decrement = true;
dma->channel[i].indirect = true;
dma->channel[i].fromB = true;
dma->channel[i].unusedBit = true;
dma->channel[i].doTransfer = false;
dma->channel[i].terminated = false;
}
dma->dmaState = 0;
dma->hdmaInitRequested = false;
dma->hdmaRunRequested = false;
}
void dma_handleState(Dma* dma, StateHandler* sh) {
sh_handleBools(sh, &dma->hdmaInitRequested, &dma->hdmaRunRequested, NULL);
sh_handleBytes(sh, &dma->dmaState, NULL);
for(int i = 0; i < 8; i++) {
sh_handleBools(sh,
&dma->channel[i].dmaActive, &dma->channel[i].hdmaActive, &dma->channel[i].fixed, &dma->channel[i].decrement,
&dma->channel[i].indirect, &dma->channel[i].fromB, &dma->channel[i].unusedBit, &dma->channel[i].doTransfer,
&dma->channel[i].terminated, NULL
);
sh_handleBytes(sh,
&dma->channel[i].bAdr, &dma->channel[i].aBank, &dma->channel[i].indBank, &dma->channel[i].repCount,
&dma->channel[i].unusedByte, &dma->channel[i].mode, NULL
);
sh_handleWords(sh, &dma->channel[i].aAdr, &dma->channel[i].size, &dma->channel[i].tableAdr, NULL);
}
}
uint8_t dma_read(Dma* dma, uint16_t adr) {
uint8_t c = (adr & 0x70) >> 4;
switch(adr & 0xf) {
case 0x0: {
uint8_t val = dma->channel[c].mode;
val |= dma->channel[c].fixed << 3;
val |= dma->channel[c].decrement << 4;
val |= dma->channel[c].unusedBit << 5;
val |= dma->channel[c].indirect << 6;
val |= dma->channel[c].fromB << 7;
return val;
}
case 0x1: {
return dma->channel[c].bAdr;
}
case 0x2: {
return dma->channel[c].aAdr & 0xff;
}
case 0x3: {
return dma->channel[c].aAdr >> 8;
}
case 0x4: {
return dma->channel[c].aBank;
}
case 0x5: {
return dma->channel[c].size & 0xff;
}
case 0x6: {
return dma->channel[c].size >> 8;
}
case 0x7: {
return dma->channel[c].indBank;
}
case 0x8: {
return dma->channel[c].tableAdr & 0xff;
}
case 0x9: {
return dma->channel[c].tableAdr >> 8;
}
case 0xa: {
return dma->channel[c].repCount;
}
case 0xb:
case 0xf: {
return dma->channel[c].unusedByte;
}
default: {
return dma->snes->openBus;
}
}
}
void dma_write(Dma* dma, uint16_t adr, uint8_t val) {
uint8_t c = (adr & 0x70) >> 4;
switch(adr & 0xf) {
case 0x0: {
dma->channel[c].mode = val & 0x7;
dma->channel[c].fixed = val & 0x8;
dma->channel[c].decrement = val & 0x10;
dma->channel[c].unusedBit = val & 0x20;
dma->channel[c].indirect = val & 0x40;
dma->channel[c].fromB = val & 0x80;
break;
}
case 0x1: {
dma->channel[c].bAdr = val;
break;
}
case 0x2: {
dma->channel[c].aAdr = (dma->channel[c].aAdr & 0xff00) | val;
break;
}
case 0x3: {
dma->channel[c].aAdr = (dma->channel[c].aAdr & 0xff) | (val << 8);
break;
}
case 0x4: {
dma->channel[c].aBank = val;
break;
}
case 0x5: {
dma->channel[c].size = (dma->channel[c].size & 0xff00) | val;
break;
}
case 0x6: {
dma->channel[c].size = (dma->channel[c].size & 0xff) | (val << 8);
break;
}
case 0x7: {
dma->channel[c].indBank = val;
break;
}
case 0x8: {
dma->channel[c].tableAdr = (dma->channel[c].tableAdr & 0xff00) | val;
break;
}
case 0x9: {
dma->channel[c].tableAdr = (dma->channel[c].tableAdr & 0xff) | (val << 8);
break;
}
case 0xa: {
dma->channel[c].repCount = val;
break;
}
case 0xb:
case 0xf: {
dma->channel[c].unusedByte = val;
break;
}
default: {
break;
}
}
}
static void dma_checkHdma(Dma* dma) {
// run hdma if requested, no sync (already sycned due to dma)
if(dma->hdmaInitRequested) dma_initHdma(dma, false, 0);
if(dma->hdmaRunRequested) dma_doHdma(dma, false, 0);
}
static void dma_doDma(Dma* dma, int cpuCycles) {
// nmi/irq is delayed by 1 opcode if requested during dma/hdma
cpu_setIntDelay();
// align to multiple of 8
snes_syncCycles(dma->snes, true, 8);
dma_checkHdma(dma);
snes_runCycles(dma->snes, 8); // full transfer overhead
for(int i = 0; i < 8; i++) {
if(!dma->channel[i].dmaActive) continue;
//bprintf(0, _T("d%x, x/y %d %d\n"), i, dma->snes->hPos, dma->snes->vPos);
// do channel i
dma_checkHdma(dma);
snes_runCycles(dma->snes, 8); // overhead per channel
int offIndex = 0;
while(dma->channel[i].dmaActive) {
dma_checkHdma(dma);
dma_transferByte(
dma, dma->channel[i].aAdr, dma->channel[i].aBank,
dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][offIndex++], dma->channel[i].fromB
);
offIndex &= 3;
if(!dma->channel[i].fixed) {
dma->channel[i].aAdr += dma->channel[i].decrement ? -1 : 1;
}
dma->channel[i].size--;
if(dma->channel[i].size == 0) {
dma->channel[i].dmaActive = false;
}
}
}
// re-align to cpu cycles
snes_syncCycles(dma->snes, false, cpuCycles);
}
static void dma_initHdma(Dma* dma, bool doSync, int cpuCycles) {
dma->hdmaInitRequested = false;
bool hdmaEnabled = false;
// check if a channel is enabled, and do reset
for(int i = 0; i < 8; i++) {
if(dma->channel[i].hdmaActive) hdmaEnabled = true;
dma->channel[i].doTransfer = false;
dma->channel[i].terminated = false;
}
if(!hdmaEnabled) return;
// nmi/irq is delayed by 1 opcode if requested during dma/hdma
cpu_setIntDelay();
if(doSync) snes_syncCycles(dma->snes, true, 8);
// full transfer overhead
snes_runCycles(dma->snes, 8);
for(int i = 0; i < 8; i++) {
if(dma->channel[i].hdmaActive) {
//bprintf(0, _T("Hdinit%x, x/y %d %d\n"), i, dma->snes->hPos, dma->snes->vPos);
// terminate any dma
dma->channel[i].dmaActive = false;
// load address, repCount, and indirect address if needed
dma->channel[i].tableAdr = dma->channel[i].aAdr;
snes_runCycles(dma->snes, 4);
dma->channel[i].repCount = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++);
snes_runCycles(dma->snes, 4);
if(dma->channel[i].repCount == 0) dma->channel[i].terminated = true;
if(dma->channel[i].indirect) {
snes_runCycles(dma->snes, 4);
dma->channel[i].size = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++);
snes_runCycles(dma->snes, 4);
snes_runCycles(dma->snes, 4);
dma->channel[i].size |= snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++) << 8;
snes_runCycles(dma->snes, 4);
}
dma->channel[i].doTransfer = true;
}
}
if(doSync) snes_syncCycles(dma->snes, false, cpuCycles);
}
static void dma_doHdma(Dma* dma, bool doSync, int cpuCycles) {
dma->hdmaRunRequested = false;
bool hdmaActive = false;
int lastActive = 0;
for(int i = 0; i < 8; i++) {
if(dma->channel[i].hdmaActive) {
hdmaActive = true;
if(!dma->channel[i].terminated) lastActive = i;
}
}
if(!hdmaActive) return;
// nmi/irq is delayed by 1 opcode if requested during dma/hdma
cpu_setIntDelay();
if(doSync) snes_syncCycles(dma->snes, true, 8);
// full transfer overhead
snes_runCycles(dma->snes, 8);
// do all copies
for(int i = 0; i < 8; i++) {
// terminate any dma
if(dma->channel[i].hdmaActive) dma->channel[i].dmaActive = false;
if(dma->channel[i].hdmaActive && !dma->channel[i].terminated) {
// do the hdma
if(dma->channel[i].doTransfer) {
//bprintf(0, _T("???Hdma%x, x/y %d %d\n"), i, dma->snes->hPos, dma->snes->vPos);
for(int j = 0; j < transferLength[dma->channel[i].mode]; j++) {
if(dma->channel[i].indirect) {
dma_transferByte(
dma, dma->channel[i].size++, dma->channel[i].indBank,
dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][j], dma->channel[i].fromB
);
} else {
dma_transferByte(
dma, dma->channel[i].tableAdr++, dma->channel[i].aBank,
dma->channel[i].bAdr + bAdrOffsets[dma->channel[i].mode][j], dma->channel[i].fromB
);
}
}
}
}
}
// do all updates
for(int i = 0; i < 8; i++) {
if(dma->channel[i].hdmaActive && !dma->channel[i].terminated) {
//bprintf(0, _T("Hdma%x, x/y %d %d\n"), i, dma->snes->hPos, dma->snes->vPos);
dma->channel[i].repCount--;
dma->channel[i].doTransfer = dma->channel[i].repCount & 0x80;
snes_runCycles(dma->snes, 4);
uint8_t newRepCount = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr);
snes_runCycles(dma->snes, 4);
if((dma->channel[i].repCount & 0x7f) == 0) {
dma->channel[i].repCount = newRepCount;
dma->channel[i].tableAdr++;
if(dma->channel[i].indirect) {
if(dma->channel[i].repCount == 0 && i == lastActive) {
// if this is the last active channel, only fetch high, and use 0 for low
dma->channel[i].size = 0;
} else {
snes_runCycles(dma->snes, 4);
dma->channel[i].size = snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++);
snes_runCycles(dma->snes, 4);
}
snes_runCycles(dma->snes, 4);
dma->channel[i].size |= snes_read(dma->snes, (dma->channel[i].aBank << 16) | dma->channel[i].tableAdr++) << 8;
snes_runCycles(dma->snes, 4);
}
if(dma->channel[i].repCount == 0) dma->channel[i].terminated = true;
dma->channel[i].doTransfer = true;
}
}
}
if(doSync) snes_syncCycles(dma->snes, false, cpuCycles);
}
static void dma_transferByte(Dma* dma, uint16_t aAdr, uint8_t aBank, uint8_t bAdr, bool fromB) {
// accessing 0x2180 via b-bus while a-bus accesses ram gives open bus
bool validB = !(bAdr == 0x80 && (aBank == 0x7e || aBank == 0x7f || (
(aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) && aAdr < 0x2000
)));
// accesing b-bus, or dma regs via a-bus gives open bus
bool validA = !((aBank < 0x40 || (aBank >= 0x80 && aBank < 0xc0)) && (
aAdr == 0x420b || aAdr == 0x420c || (aAdr >= 0x4300 && aAdr < 0x4380) || (aAdr >= 0x2100 && aAdr < 0x2200)
));
if(fromB) {
snes_runCycles(dma->snes, 4);
uint8_t val = validB ? snes_readBBus(dma->snes, bAdr) : dma->snes->openBus;
snes_runCycles(dma->snes, 4);
if(validA) snes_write(dma->snes, (aBank << 16) | aAdr, val);
} else {
snes_runCycles(dma->snes, 4);
uint8_t val = validA ? snes_read(dma->snes, (aBank << 16) | aAdr) : dma->snes->openBus;
snes_runCycles(dma->snes, 4);
if(validB) snes_writeBBus(dma->snes, bAdr, val);
}
}
void dma_handleDma(Dma* dma, int cpuCycles) {
// if hdma triggered, do it, except if dmastate indicates dma will be done now
// (it will be done as part of the dma in that case)
if(dma->hdmaInitRequested && dma->dmaState != 2) dma_initHdma(dma, true, cpuCycles);
if(dma->hdmaRunRequested && dma->dmaState != 2) dma_doHdma(dma, true, cpuCycles);
if(dma->dmaState == 1) {
dma->dmaState = 2;
return;
}
if(dma->dmaState == 2) {
// do dma
dma_doDma(dma, cpuCycles);
dma->dmaState = 0;
}
}
void dma_startDma(Dma* dma, uint8_t val, bool hdma) {
for(int i = 0; i < 8; i++) {
if(hdma) {
dma->channel[i].hdmaActive = val & (1 << i);
} else {
dma->channel[i].dmaActive = val & (1 << i);
}
}
if(!hdma) {
dma->dmaState = val != 0 ? 1 : 0;
}
}

52
src/burn/drv/snes/dma.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef DMA_H
#define DMA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct Dma Dma;
#include "snes.h"
#include "statehandler.h"
typedef struct DmaChannel {
uint8_t bAdr;
uint16_t aAdr;
uint8_t aBank;
uint16_t size; // also indirect hdma adr
uint8_t indBank; // hdma
uint16_t tableAdr; // hdma
uint8_t repCount; // hdma
uint8_t unusedByte;
bool dmaActive;
bool hdmaActive;
uint8_t mode;
bool fixed;
bool decrement;
bool indirect; // hdma
bool fromB;
bool unusedBit;
bool doTransfer; // hdma
bool terminated; // hdma
} DmaChannel;
struct Dma {
Snes* snes;
DmaChannel channel[8];
uint8_t dmaState;
bool hdmaInitRequested;
bool hdmaRunRequested;
};
Dma* dma_init(Snes* snes);
void dma_free(Dma* dma);
void dma_reset(Dma* dma);
void dma_handleState(Dma* dma, StateHandler* sh);
uint8_t dma_read(Dma* dma, uint16_t adr); // 43x0-43xf
void dma_write(Dma* dma, uint16_t adr, uint8_t val); // 43x0-43xf
void dma_handleDma(Dma* dma, int cpuCycles);
void dma_startDma(Dma* dma, uint8_t val, bool hdma);
#endif

601
src/burn/drv/snes/dsp.cpp Normal file
View File

@ -0,0 +1,601 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "dsp.h"
#include "apu.h"
#include "statehandler.h"
static const int rateValues[32] = {
0, 2048, 1536, 1280, 1024, 768, 640, 512,
384, 320, 256, 192, 160, 128, 96, 80,
64, 48, 40, 32, 24, 20, 16, 12,
10, 8, 6, 5, 4, 3, 2, 1
};
static const int rateOffsets[32] = {
0, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536,
0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0
};
static const int gaussValues[512] = {
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002,
0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005,
0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00a, 0x00a, 0x00a,
0x00b, 0x00b, 0x00b, 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00e, 0x00f, 0x00f, 0x00f, 0x010, 0x010, 0x011, 0x011,
0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x01a, 0x01b, 0x01b,
0x01c, 0x01d, 0x01d, 0x01e, 0x01f, 0x020, 0x020, 0x021, 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028,
0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038,
0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x049, 0x04a, 0x04c, 0x04d,
0x04e, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, 0x05a, 0x05c, 0x05e, 0x05f, 0x061, 0x063, 0x064, 0x066,
0x068, 0x06a, 0x06b, 0x06d, 0x06f, 0x071, 0x073, 0x075, 0x076, 0x078, 0x07a, 0x07c, 0x07e, 0x080, 0x082, 0x084,
0x086, 0x089, 0x08b, 0x08d, 0x08f, 0x091, 0x093, 0x096, 0x098, 0x09a, 0x09c, 0x09f, 0x0a1, 0x0a3, 0x0a6, 0x0a8,
0x0ab, 0x0ad, 0x0af, 0x0b2, 0x0b4, 0x0b7, 0x0ba, 0x0bc, 0x0bf, 0x0c1, 0x0c4, 0x0c7, 0x0c9, 0x0cc, 0x0cf, 0x0d2,
0x0d4, 0x0d7, 0x0da, 0x0dd, 0x0e0, 0x0e3, 0x0e6, 0x0e9, 0x0ec, 0x0ef, 0x0f2, 0x0f5, 0x0f8, 0x0fb, 0x0fe, 0x101,
0x104, 0x107, 0x10b, 0x10e, 0x111, 0x114, 0x118, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, 0x130, 0x133, 0x137,
0x13a, 0x13e, 0x141, 0x145, 0x148, 0x14c, 0x150, 0x153, 0x157, 0x15b, 0x15f, 0x162, 0x166, 0x16a, 0x16e, 0x172,
0x176, 0x17a, 0x17d, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x19a, 0x19e, 0x1a2, 0x1a6, 0x1aa, 0x1ae, 0x1b2,
0x1b7, 0x1bb, 0x1bf, 0x1c3, 0x1c8, 0x1cc, 0x1d0, 0x1d5, 0x1d9, 0x1dd, 0x1e2, 0x1e6, 0x1eb, 0x1ef, 0x1f3, 0x1f8,
0x1fc, 0x201, 0x205, 0x20a, 0x20f, 0x213, 0x218, 0x21c, 0x221, 0x226, 0x22a, 0x22f, 0x233, 0x238, 0x23d, 0x241,
0x246, 0x24b, 0x250, 0x254, 0x259, 0x25e, 0x263, 0x267, 0x26c, 0x271, 0x276, 0x27b, 0x280, 0x284, 0x289, 0x28e,
0x293, 0x298, 0x29d, 0x2a2, 0x2a6, 0x2ab, 0x2b0, 0x2b5, 0x2ba, 0x2bf, 0x2c4, 0x2c9, 0x2ce, 0x2d3, 0x2d8, 0x2dc,
0x2e1, 0x2e6, 0x2eb, 0x2f0, 0x2f5, 0x2fa, 0x2ff, 0x304, 0x309, 0x30e, 0x313, 0x318, 0x31d, 0x322, 0x326, 0x32b,
0x330, 0x335, 0x33a, 0x33f, 0x344, 0x349, 0x34e, 0x353, 0x357, 0x35c, 0x361, 0x366, 0x36b, 0x370, 0x374, 0x379,
0x37e, 0x383, 0x388, 0x38c, 0x391, 0x396, 0x39b, 0x39f, 0x3a4, 0x3a9, 0x3ad, 0x3b2, 0x3b7, 0x3bb, 0x3c0, 0x3c5,
0x3c9, 0x3ce, 0x3d2, 0x3d7, 0x3dc, 0x3e0, 0x3e5, 0x3e9, 0x3ed, 0x3f2, 0x3f6, 0x3fb, 0x3ff, 0x403, 0x408, 0x40c,
0x410, 0x415, 0x419, 0x41d, 0x421, 0x425, 0x42a, 0x42e, 0x432, 0x436, 0x43a, 0x43e, 0x442, 0x446, 0x44a, 0x44e,
0x452, 0x455, 0x459, 0x45d, 0x461, 0x465, 0x468, 0x46c, 0x470, 0x473, 0x477, 0x47a, 0x47e, 0x481, 0x485, 0x488,
0x48c, 0x48f, 0x492, 0x496, 0x499, 0x49c, 0x49f, 0x4a2, 0x4a6, 0x4a9, 0x4ac, 0x4af, 0x4b2, 0x4b5, 0x4b7, 0x4ba,
0x4bd, 0x4c0, 0x4c3, 0x4c5, 0x4c8, 0x4cb, 0x4cd, 0x4d0, 0x4d2, 0x4d5, 0x4d7, 0x4d9, 0x4dc, 0x4de, 0x4e0, 0x4e3,
0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f6, 0x4f8, 0x4fa, 0x4fb, 0x4fd, 0x4ff, 0x500,
0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50a, 0x50b, 0x50c, 0x50d, 0x50e, 0x50f, 0x510, 0x511, 0x511, 0x512,
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519
};
static int clamp16(int val);
static int clip16(int val);
static bool dsp_checkCounter(Dsp* dsp, int rate);
static void dsp_cycleChannel(Dsp* dsp, int ch);
static void dsp_handleEcho(Dsp* dsp);
static void dsp_handleGain(Dsp* dsp, int ch);
static void dsp_decodeBrr(Dsp* dsp, int ch);
static int16_t dsp_getSample(Dsp* dsp, int ch);
static void dsp_handleNoise(Dsp* dsp);
Dsp* dsp_init(Apu* apu) {
Dsp* dsp = (Dsp*)BurnMalloc(sizeof(Dsp));
dsp->apu = apu;
return dsp;
}
void dsp_free(Dsp* dsp) {
BurnFree(dsp);
}
void dsp_reset(Dsp* dsp) {
memset(dsp->ram, 0, sizeof(dsp->ram));
dsp->ram[0x7c] = 0xff; // set ENDx
for(int i = 0; i < 8; i++) {
dsp->channel[i].pitch = 0;
dsp->channel[i].pitchCounter = 0;
dsp->channel[i].pitchModulation = false;
memset(dsp->channel[i].decodeBuffer, 0, sizeof(dsp->channel[i].decodeBuffer));
dsp->channel[i].bufferOffset = 0;
dsp->channel[i].srcn = 0;
dsp->channel[i].decodeOffset = 0;
dsp->channel[i].blockOffset = 0;
dsp->channel[i].brrHeader = 0;
dsp->channel[i].useNoise = false;
dsp->channel[i].startDelay = 0;
memset(dsp->channel[i].adsrRates, 0, sizeof(dsp->channel[i].adsrRates));
dsp->channel[i].adsrState = 0;
dsp->channel[i].sustainLevel = 0;
dsp->channel[i].gainSustainLevel = 0;
dsp->channel[i].useGain = false;
dsp->channel[i].gainMode = 0;
dsp->channel[i].directGain = false;
dsp->channel[i].gainValue = 0;
dsp->channel[i].preclampGain = 0;
dsp->channel[i].gain = 0;
dsp->channel[i].keyOn = false;
dsp->channel[i].keyOff = false;
dsp->channel[i].sampleOut = 0;
dsp->channel[i].volumeL = 0;
dsp->channel[i].volumeR = 0;
dsp->channel[i].echoEnable = false;
}
dsp->counter = 0;
dsp->dirPage = 0;
dsp->evenCycle = true;
dsp->mute = true;
dsp->reset = true;
dsp->masterVolumeL = 0;
dsp->masterVolumeR = 0;
dsp->sampleOutL = 0;
dsp->sampleOutR = 0;
dsp->echoOutL = 0;
dsp->echoOutR = 0;
dsp->noiseSample = 0x4000;
dsp->noiseRate = 0;
dsp->echoWrites = false;
dsp->echoVolumeL = 0;
dsp->echoVolumeR = 0;
dsp->feedbackVolume = 0;
dsp->echoBufferAdr = 0;
dsp->echoDelay = 0;
dsp->echoLength = 0;
dsp->echoBufferIndex = 0;
dsp->firBufferIndex = 0;
memset(dsp->firValues, 0, sizeof(dsp->firValues));
memset(dsp->firBufferL, 0, sizeof(dsp->firBufferL));
memset(dsp->firBufferR, 0, sizeof(dsp->firBufferR));
memset(dsp->sampleBuffer, 0, sizeof(dsp->sampleBuffer));
dsp->sampleOffset = 0;
dsp->lastFrameBoundary = 0;
}
void dsp_newFrame(Dsp* dsp) {
dsp->lastFrameBoundary = dsp->sampleOffset;
}
void dsp_handleState(Dsp* dsp, StateHandler* sh) {
sh_handleBools(sh, &dsp->evenCycle, &dsp->mute, &dsp->reset, &dsp->echoWrites, NULL);
sh_handleBytes(sh, &dsp->noiseRate, &dsp->firBufferIndex, NULL);
sh_handleBytesS(sh,
&dsp->masterVolumeL, &dsp->masterVolumeR, &dsp->echoVolumeL, &dsp->echoVolumeR, &dsp->feedbackVolume,
&dsp->firValues[0], &dsp->firValues[1], &dsp->firValues[2], &dsp->firValues[3], &dsp->firValues[4],
&dsp->firValues[5], &dsp->firValues[6], &dsp->firValues[7], NULL
);
sh_handleWords(sh,
&dsp->counter, &dsp->dirPage, &dsp->echoBufferAdr, &dsp->echoDelay, &dsp->echoLength, &dsp->echoBufferIndex, &dsp->lastFrameBoundary, &dsp->sampleOffset, NULL
);
sh_handleWordsS(sh,
&dsp->sampleOutL, &dsp->sampleOutR, &dsp->echoOutL, &dsp->echoOutR, &dsp->noiseSample,
&dsp->firBufferL[0], &dsp->firBufferL[1], &dsp->firBufferL[2], &dsp->firBufferL[3], &dsp->firBufferL[4],
&dsp->firBufferL[5], &dsp->firBufferL[6], &dsp->firBufferL[7], &dsp->firBufferR[0], &dsp->firBufferR[1],
&dsp->firBufferR[2], &dsp->firBufferR[3], &dsp->firBufferR[4], &dsp->firBufferR[5], &dsp->firBufferR[6],
&dsp->firBufferR[7], NULL
);
for(int i = 0; i < 8; i++) {
sh_handleBools(sh,
&dsp->channel[i].pitchModulation, &dsp->channel[i].useNoise, &dsp->channel[i].useGain, &dsp->channel[i].directGain,
&dsp->channel[i].keyOn, &dsp->channel[i].keyOff, &dsp->channel[i].echoEnable, NULL
);
sh_handleBytes(sh,
&dsp->channel[i].bufferOffset, &dsp->channel[i].srcn, &dsp->channel[i].blockOffset, &dsp->channel[i].brrHeader,
&dsp->channel[i].startDelay, &dsp->channel[i].adsrRates[0], &dsp->channel[i].adsrRates[1],
&dsp->channel[i].adsrRates[2], &dsp->channel[i].adsrRates[3], &dsp->channel[i].adsrState,
&dsp->channel[i].sustainLevel, &dsp->channel[i].gainSustainLevel, &dsp->channel[i].gainMode, NULL
);
sh_handleBytesS(sh, &dsp->channel[i].volumeL, &dsp->channel[i].volumeR, NULL);
sh_handleWords(sh,
&dsp->channel[i].pitch, &dsp->channel[i].pitchCounter, &dsp->channel[i].decodeOffset, &dsp->channel[i].gainValue,
&dsp->channel[i].preclampGain, &dsp->channel[i].gain, NULL
);
sh_handleWordsS(sh,
&dsp->channel[i].decodeBuffer[0], &dsp->channel[i].decodeBuffer[1], &dsp->channel[i].decodeBuffer[2],
&dsp->channel[i].decodeBuffer[3], &dsp->channel[i].decodeBuffer[4], &dsp->channel[i].decodeBuffer[5],
&dsp->channel[i].decodeBuffer[6], &dsp->channel[i].decodeBuffer[7], &dsp->channel[i].decodeBuffer[8],
&dsp->channel[i].decodeBuffer[9], &dsp->channel[i].decodeBuffer[10], &dsp->channel[i].decodeBuffer[11],
&dsp->channel[i].sampleOut, NULL
);
}
sh_handleByteArray(sh, dsp->ram, 0x80);
// sh_handleByteArray(sh, (UINT8*)&dsp->sampleBuffer[0], 0x800*2*2);
}
void dsp_cycle(Dsp* dsp) {
dsp->sampleOutL = 0;
dsp->sampleOutR = 0;
dsp->echoOutL = 0;
dsp->echoOutR = 0;
for(int i = 0; i < 8; i++) {
dsp_cycleChannel(dsp, i);
}
dsp_handleEcho(dsp); // also applies master volume
dsp->counter = dsp->counter == 0 ? 30720 - 1 : dsp->counter - 1;
dsp_handleNoise(dsp);
dsp->evenCycle = !dsp->evenCycle;
// handle mute flag
if(dsp->mute) {
dsp->sampleOutL = 0;
dsp->sampleOutR = 0;
}
if (bBurnRunAheadFrame == 0) {
// put final sample in the samplebuffer
dsp->sampleBuffer[(dsp->sampleOffset & 0x7ff) * 2] = dsp->sampleOutL;
dsp->sampleBuffer[(dsp->sampleOffset++ & 0x7ff) * 2 + 1] = dsp->sampleOutR;
}
}
static int clamp16(int val) {
return val < -0x8000 ? -0x8000 : (val > 0x7fff ? 0x7fff : val);
}
static int clip16(int val) {
return (int16_t) (val & 0xffff);
}
static bool dsp_checkCounter(Dsp* dsp, int rate) {
if(rate == 0) return false;
return ((dsp->counter + rateOffsets[rate]) % rateValues[rate]) == 0;
}
static void dsp_handleEcho(Dsp* dsp) {
// increment fir buffer index
dsp->firBufferIndex++;
dsp->firBufferIndex &= 0x7;
// get value out of ram
uint16_t adr = dsp->echoBufferAdr + dsp->echoBufferIndex;
int16_t ramSample = dsp->apu->ram[adr] | (dsp->apu->ram[(adr + 1) & 0xffff] << 8);
dsp->firBufferL[dsp->firBufferIndex] = ramSample >> 1;
ramSample = dsp->apu->ram[(adr + 2) & 0xffff] | (dsp->apu->ram[(adr + 3) & 0xffff] << 8);
dsp->firBufferR[dsp->firBufferIndex] = ramSample >> 1;
// calculate FIR-sum
int sumL = 0, sumR = 0;
for(int i = 0; i < 8; i++) {
sumL += (dsp->firBufferL[(dsp->firBufferIndex + i + 1) & 0x7] * dsp->firValues[i]) >> 6;
sumR += (dsp->firBufferR[(dsp->firBufferIndex + i + 1) & 0x7] * dsp->firValues[i]) >> 6;
if(i == 6) {
// clip to 16-bit before last addition
sumL = clip16(sumL);
sumR = clip16(sumR);
}
}
sumL = clamp16(sumL) & ~1;
sumR = clamp16(sumR) & ~1;
// apply master volume and modify output with sum
dsp->sampleOutL = clamp16(((dsp->sampleOutL * dsp->masterVolumeL) >> 7) + ((sumL * dsp->echoVolumeL) >> 7));
dsp->sampleOutR = clamp16(((dsp->sampleOutR * dsp->masterVolumeR) >> 7) + ((sumR * dsp->echoVolumeR) >> 7));
// get echo value
int echoL = clamp16(dsp->echoOutL + clip16((sumL * dsp->feedbackVolume) >> 7)) & ~1;
int echoR = clamp16(dsp->echoOutR + clip16((sumR * dsp->feedbackVolume) >> 7)) & ~1;
// write it to ram
if(dsp->echoWrites) {
dsp->apu->ram[adr] = echoL & 0xff;
dsp->apu->ram[(adr + 1) & 0xffff] = echoL >> 8;
dsp->apu->ram[(adr + 2) & 0xffff] = echoR & 0xff;
dsp->apu->ram[(adr + 3) & 0xffff] = echoR >> 8;
}
// handle indexes
if(dsp->echoBufferIndex == 0) {
dsp->echoLength = dsp->echoDelay * 4;
}
dsp->echoBufferIndex += 4;
if(dsp->echoBufferIndex >= dsp->echoLength) {
dsp->echoBufferIndex = 0;
}
}
static void dsp_cycleChannel(Dsp* dsp, int ch) {
// handle pitch counter
int pitch = dsp->channel[ch].pitch;
if(ch > 0 && dsp->channel[ch].pitchModulation) {
pitch += ((dsp->channel[ch - 1].sampleOut >> 5) * pitch) >> 10;
}
// get current brr header and get sample address
dsp->channel[ch].brrHeader = dsp->apu->ram[dsp->channel[ch].decodeOffset];
uint16_t samplePointer = dsp->dirPage + 4 * dsp->channel[ch].srcn;
if(dsp->channel[ch].startDelay == 0) samplePointer += 2;
uint16_t sampleAdr = dsp->apu->ram[samplePointer] | (dsp->apu->ram[(samplePointer + 1) & 0xffff] << 8);
// handle starting of sample
if(dsp->channel[ch].startDelay > 0) {
if(dsp->channel[ch].startDelay == 5) {
// first keyed on
dsp->channel[ch].decodeOffset = sampleAdr;
dsp->channel[ch].blockOffset = 1;
dsp->channel[ch].bufferOffset = 0;
dsp->channel[ch].brrHeader = 0;
}
dsp->channel[ch].gain = 0;
dsp->channel[ch].startDelay--;
dsp->channel[ch].pitchCounter = 0;
if(dsp->channel[ch].startDelay > 0 && dsp->channel[ch].startDelay < 4) {
dsp->channel[ch].pitchCounter = 0x4000;
}
pitch = 0;
}
// get sample
int sample = 0;
if(dsp->channel[ch].useNoise) {
sample = clip16(dsp->noiseSample * 2);
} else {
sample = dsp_getSample(dsp, ch);
}
sample = ((sample * dsp->channel[ch].gain) >> 11) & ~1;
// handle reset and release
if(dsp->reset || (dsp->channel[ch].brrHeader & 0x03) == 1) {
dsp->channel[ch].adsrState = 3; // go to release
dsp->channel[ch].gain = 0;
}
// handle keyon/keyoff
if(dsp->evenCycle) {
if(dsp->channel[ch].keyOff) {
dsp->channel[ch].adsrState = 3; // go to release
}
if(dsp->channel[ch].keyOn) {
dsp->channel[ch].startDelay = 5;
dsp->channel[ch].adsrState = 0; // go to attack
dsp->channel[ch].keyOn = false;
dsp->ram[0x7c] &= ~(1 << ch); // clear ENDx
}
}
// handle envelope
if(dsp->channel[ch].startDelay == 0) {
dsp_handleGain(dsp, ch);
}
// decode new brr samples if needed and update offsets
if(dsp->channel[ch].pitchCounter >= 0x4000) {
dsp_decodeBrr(dsp, ch);
if(dsp->channel[ch].blockOffset >= 7) {
if(dsp->channel[ch].brrHeader & 0x1) {
dsp->channel[ch].decodeOffset = sampleAdr;
dsp->ram[0x7c] |= 1 << ch; // set ENDx
} else {
dsp->channel[ch].decodeOffset += 9;
}
dsp->channel[ch].blockOffset = 1;
} else {
dsp->channel[ch].blockOffset += 2;
}
}
// update pitch counter
dsp->channel[ch].pitchCounter &= 0x3fff;
dsp->channel[ch].pitchCounter += pitch;
if(dsp->channel[ch].pitchCounter > 0x7fff) dsp->channel[ch].pitchCounter = 0x7fff;
// set outputs
dsp->ram[(ch << 4) | 8] = dsp->channel[ch].gain >> 4;
dsp->ram[(ch << 4) | 9] = sample >> 8;
dsp->channel[ch].sampleOut = sample;
dsp->sampleOutL = clamp16(dsp->sampleOutL + ((sample * dsp->channel[ch].volumeL) >> 7));
dsp->sampleOutR = clamp16(dsp->sampleOutR + ((sample * dsp->channel[ch].volumeR) >> 7));
if(dsp->channel[ch].echoEnable) {
dsp->echoOutL = clamp16(dsp->echoOutL + ((sample * dsp->channel[ch].volumeL) >> 7));
dsp->echoOutR = clamp16(dsp->echoOutR + ((sample * dsp->channel[ch].volumeR) >> 7));
}
}
static void dsp_handleGain(Dsp* dsp, int ch) {
int newGain = dsp->channel[ch].gain;
int rate = 0;
// handle gain mode
if(dsp->channel[ch].adsrState == 3) { // release
rate = 31;
newGain -= 8;
} else {
if(!dsp->channel[ch].useGain) {
rate = dsp->channel[ch].adsrRates[dsp->channel[ch].adsrState];
switch(dsp->channel[ch].adsrState) {
case 0: newGain += rate == 31 ? 1024 : 32; break; // attack
case 1: newGain -= ((newGain - 1) >> 8) + 1; break; // decay
case 2: newGain -= ((newGain - 1) >> 8) + 1; break; // sustain
}
} else {
if(!dsp->channel[ch].directGain) {
rate = dsp->channel[ch].adsrRates[3];
switch(dsp->channel[ch].gainMode) {
case 0: newGain -= 32; break; // linear decrease
case 1: newGain -= ((newGain - 1) >> 8) + 1; break; // exponential decrease
case 2: newGain += 32; break; // linear increase
case 3: newGain += (dsp->channel[ch].preclampGain < 0x600) ? 32 : 8; break; // bent increase
}
} else { // direct gain
rate = 31;
newGain = dsp->channel[ch].gainValue;
}
}
}
// use sustain level according to mode
int sustainLevel = dsp->channel[ch].useGain ? dsp->channel[ch].gainSustainLevel : dsp->channel[ch].sustainLevel;
if(dsp->channel[ch].adsrState == 1 && (newGain >> 8) == sustainLevel) {
dsp->channel[ch].adsrState = 2; // go to sustain
}
// store pre-clamped gain (for bent increase)
dsp->channel[ch].preclampGain = newGain & 0xffff;
// clamp gain
if(newGain < 0 || newGain > 0x7ff) {
newGain = newGain < 0 ? 0 : 0x7ff;
if(dsp->channel[ch].adsrState == 0) {
dsp->channel[ch].adsrState = 1; // go to decay
}
}
// store new value
if(dsp_checkCounter(dsp, rate)) dsp->channel[ch].gain = newGain;
}
static int16_t dsp_getSample(Dsp* dsp, int ch) {
int pos = (dsp->channel[ch].pitchCounter >> 12) + dsp->channel[ch].bufferOffset;
int offset = (dsp->channel[ch].pitchCounter >> 4) & 0xff;
int16_t news = dsp->channel[ch].decodeBuffer[(pos + 3) % 12];
int16_t olds = dsp->channel[ch].decodeBuffer[(pos + 2) % 12];
int16_t olders = dsp->channel[ch].decodeBuffer[(pos + 1) % 12];
int16_t oldests = dsp->channel[ch].decodeBuffer[pos % 12];
int out = (gaussValues[0xff - offset] * oldests) >> 11;
out += (gaussValues[0x1ff - offset] * olders) >> 11;
out += (gaussValues[0x100 + offset] * olds) >> 11;
out = clip16(out) + ((gaussValues[offset] * news) >> 11);
return clamp16(out) & ~1;
}
static void dsp_decodeBrr(Dsp* dsp, int ch) {
int shift = dsp->channel[ch].brrHeader >> 4;
int filter = (dsp->channel[ch].brrHeader & 0xc) >> 2;
int bOff = dsp->channel[ch].bufferOffset;
int old = dsp->channel[ch].decodeBuffer[bOff == 0 ? 11 : bOff - 1] >> 1;
int older = dsp->channel[ch].decodeBuffer[bOff == 0 ? 10 : bOff - 2] >> 1;
uint8_t curByte = 0;
for(int i = 0; i < 4; i++) {
int s = 0;
if(i & 1) {
s = curByte & 0xf;
} else {
curByte = dsp->apu->ram[(dsp->channel[ch].decodeOffset + dsp->channel[ch].blockOffset + (i >> 1)) & 0xffff];
s = curByte >> 4;
}
if(s > 7) s -= 16;
if(shift <= 0xc) {
s = (s << shift) >> 1;
} else {
s = (s >> 3) << 12;
}
switch(filter) {
case 1: s += old + (-old >> 4); break;
case 2: s += 2 * old + ((3 * -old) >> 5) - older + (older >> 4); break;
case 3: s += 2 * old + ((13 * -old) >> 6) - older + ((3 * older) >> 4); break;
}
dsp->channel[ch].decodeBuffer[bOff + i] = clamp16(s) * 2; // cuts off bit 15
older = old;
old = dsp->channel[ch].decodeBuffer[bOff + i] >> 1;
}
dsp->channel[ch].bufferOffset += 4;
if(dsp->channel[ch].bufferOffset >= 12) dsp->channel[ch].bufferOffset = 0;
}
static void dsp_handleNoise(Dsp* dsp) {
if(dsp_checkCounter(dsp, dsp->noiseRate)) {
int bit = (dsp->noiseSample & 1) ^ ((dsp->noiseSample >> 1) & 1);
dsp->noiseSample = ((dsp->noiseSample >> 1) & 0x3fff) | (bit << 14);
}
}
uint8_t dsp_read(Dsp* dsp, uint8_t adr) {
return dsp->ram[adr];
}
void dsp_write(Dsp* dsp, uint8_t adr, uint8_t val) {
int ch = adr >> 4;
switch(adr) {
case 0x00: case 0x10: case 0x20: case 0x30: case 0x40: case 0x50: case 0x60: case 0x70: {
dsp->channel[ch].volumeL = val;
break;
}
case 0x01: case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71: {
dsp->channel[ch].volumeR = val;
break;
}
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72: {
dsp->channel[ch].pitch = (dsp->channel[ch].pitch & 0x3f00) | val;
break;
}
case 0x03: case 0x13: case 0x23: case 0x33: case 0x43: case 0x53: case 0x63: case 0x73: {
dsp->channel[ch].pitch = ((dsp->channel[ch].pitch & 0x00ff) | (val << 8)) & 0x3fff;
break;
}
case 0x04: case 0x14: case 0x24: case 0x34: case 0x44: case 0x54: case 0x64: case 0x74: {
dsp->channel[ch].srcn = val;
break;
}
case 0x05: case 0x15: case 0x25: case 0x35: case 0x45: case 0x55: case 0x65: case 0x75: {
dsp->channel[ch].adsrRates[0] = (val & 0xf) * 2 + 1;
dsp->channel[ch].adsrRates[1] = ((val & 0x70) >> 4) * 2 + 16;
dsp->channel[ch].useGain = (val & 0x80) == 0;
break;
}
case 0x06: case 0x16: case 0x26: case 0x36: case 0x46: case 0x56: case 0x66: case 0x76: {
dsp->channel[ch].adsrRates[2] = val & 0x1f;
dsp->channel[ch].sustainLevel = (val & 0xe0) >> 5;
break;
}
case 0x07: case 0x17: case 0x27: case 0x37: case 0x47: case 0x57: case 0x67: case 0x77: {
dsp->channel[ch].directGain = (val & 0x80) == 0;
dsp->channel[ch].gainMode = (val & 0x60) >> 5;
dsp->channel[ch].adsrRates[3] = val & 0x1f;
dsp->channel[ch].gainValue = (val & 0x7f) * 16;
dsp->channel[ch].gainSustainLevel = (val & 0xe0) >> 5;
break;
}
case 0x0c: {
dsp->masterVolumeL = val;
break;
}
case 0x1c: {
dsp->masterVolumeR = val;
break;
}
case 0x2c: {
dsp->echoVolumeL = val;
break;
}
case 0x3c: {
dsp->echoVolumeR = val;
break;
}
case 0x4c: {
for(int i = 0; i < 8; i++) {
dsp->channel[i].keyOn = val & (1 << i);
}
break;
}
case 0x5c: {
for(int i = 0; i < 8; i++) {
dsp->channel[i].keyOff = val & (1 << i);
}
break;
}
case 0x6c: {
dsp->reset = val & 0x80;
dsp->mute = val & 0x40;
dsp->echoWrites = (val & 0x20) == 0;
dsp->noiseRate = val & 0x1f;
break;
}
case 0x7c: {
val = 0; // any write clears ENDx
break;
}
case 0x0d: {
dsp->feedbackVolume = val;
break;
}
case 0x2d: {
for(int i = 0; i < 8; i++) {
dsp->channel[i].pitchModulation = val & (1 << i);
}
break;
}
case 0x3d: {
for(int i = 0; i < 8; i++) {
dsp->channel[i].useNoise = val & (1 << i);
}
break;
}
case 0x4d: {
for(int i = 0; i < 8; i++) {
dsp->channel[i].echoEnable = val & (1 << i);
}
break;
}
case 0x5d: {
dsp->dirPage = val << 8;
break;
}
case 0x6d: {
dsp->echoBufferAdr = val << 8;
break;
}
case 0x7d: {
dsp->echoDelay = (val & 0xf) * 512; // 2048-byte steps, stereo sample is 4 bytes
break;
}
case 0x0f: case 0x1f: case 0x2f: case 0x3f: case 0x4f: case 0x5f: case 0x6f: case 0x7f: {
dsp->firValues[ch] = val;
break;
}
}
dsp->ram[adr] = val;
}
void dsp_getSamples(Dsp* dsp, int16_t* sampleData, int samplesPerFrame) {
// resample from 534 / 641 samples per frame to wanted value
float wantedSamples = (dsp->apu->snes->palTiming ? 641.0 : 534.0);
double adder = wantedSamples / samplesPerFrame;
double location = dsp->lastFrameBoundary - wantedSamples;
for(int i = 0; i < samplesPerFrame; i++) {
sampleData[i * 2] = dsp->sampleBuffer[(((int) location) & 0x7ff) * 2];
sampleData[i * 2 + 1] = dsp->sampleBuffer[(((int) location) & 0x7ff) * 2 + 1];
location += adder;
}
}

99
src/burn/drv/snes/dsp.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef DSP_H
#define DSP_H
#include <stdint.h>
#include <stdbool.h>
typedef struct Dsp Dsp;
#include "apu.h"
#include "statehandler.h"
typedef struct DspChannel {
// pitch
uint16_t pitch;
uint16_t pitchCounter;
bool pitchModulation;
// brr decoding
int16_t decodeBuffer[12];
uint8_t bufferOffset;
uint8_t srcn;
uint16_t decodeOffset;
uint8_t blockOffset; // offset within brr block
uint8_t brrHeader;
bool useNoise;
uint8_t startDelay;
// adsr, envelope, gain
uint8_t adsrRates[4]; // attack, decay, sustain, gain
uint8_t adsrState; // 0: attack, 1: decay, 2: sustain, 3: release
uint8_t sustainLevel;
uint8_t gainSustainLevel;
bool useGain;
uint8_t gainMode;
bool directGain;
uint16_t gainValue; // for direct gain
uint16_t preclampGain; // for bent increase
uint16_t gain;
// keyon/off
bool keyOn;
bool keyOff;
// output
int16_t sampleOut; // final sample, to be multiplied by channel volume
int8_t volumeL;
int8_t volumeR;
bool echoEnable;
} DspChannel;
struct Dsp {
Apu* apu;
// mirror ram
uint8_t ram[0x80];
// 8 channels
DspChannel channel[8];
// overarching
uint16_t counter;
uint16_t dirPage;
bool evenCycle;
bool mute;
bool reset;
int8_t masterVolumeL;
int8_t masterVolumeR;
// accumulation
int16_t sampleOutL;
int16_t sampleOutR;
int16_t echoOutL;
int16_t echoOutR;
// noise
int16_t noiseSample;
uint8_t noiseRate;
// echo
bool echoWrites;
int8_t echoVolumeL;
int8_t echoVolumeR;
int8_t feedbackVolume;
uint16_t echoBufferAdr;
uint16_t echoDelay;
uint16_t echoLength;
uint16_t echoBufferIndex;
uint8_t firBufferIndex;
int8_t firValues[8];
int16_t firBufferL[8];
int16_t firBufferR[8];
// sample ring buffer (2048 samples, *2 for stereo)
int16_t sampleBuffer[0x800 * 2];
uint16_t sampleOffset; // current offset in samplebuffer
uint16_t lastFrameBoundary;
};
Dsp* dsp_init(Apu* apu);
void dsp_free(Dsp* dsp);
void dsp_reset(Dsp* dsp);
void dsp_handleState(Dsp* dsp, StateHandler* sh);
void dsp_cycle(Dsp* dsp);
uint8_t dsp_read(Dsp* dsp, uint8_t adr);
void dsp_write(Dsp* dsp, uint8_t adr, uint8_t val);
void dsp_getSamples(Dsp* dsp, int16_t* sampleData, int samplesPerFrame);
void dsp_newFrame(Dsp* dsp);
#endif

107
src/burn/drv/snes/input.cpp Normal file
View File

@ -0,0 +1,107 @@
\
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "input.h"
#include "snes.h"
#include "statehandler.h"
Input* input_init(Snes* snes, int8_t tag) {
Input* input = (Input*)BurnMalloc(sizeof(Input));
input->snes = snes;
// TODO: handle (where?)
input->portTag = tag; // 1, 2, ...
input->type = DEVICE_GAMEPAD;
input->currentState = 0;
// TODO: handle I/O line (and latching of PPU)
return input;
}
void input_free(Input* input) {
BurnFree(input);
}
void input_reset(Input* input) {
input->latchLine = false;
input->latchedState = 0;
input->currentState = 0;
input->lastX = 0;
input->lastY = 0;
input->mouseSens = 0;
}
void input_handleState(Input* input, StateHandler* sh) {
sh_handleBytes(sh, &input->type, &input->lastX, &input->lastY, &input->mouseSens, NULL);
sh_handleBools(sh, &input->latchLine, NULL);
sh_handleInts(sh, &input->currentState, &input->latchedState, NULL);
}
void input_setType(Input* input, uint8_t type) {
input->type = type;
}
#define d_min(a, b) (((a) < (b)) ? (a) : (b))
#define d_abs(z) (((z) < 0) ? -(z) : (z))
void input_setMouse(Input* input, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB) {
x >>= 8;
y >>= 8;
input->lastX = (x) ? (x < 0) << 7 : input->lastX;
input->lastY = (y) ? (y < 0) << 7 : input->lastY;
x = d_min(d_abs(x), 0x7f);
y = d_min(d_abs(y), 0x7f);
input->currentState = 0;
input->currentState |= (0x01 | (input->mouseSens % 3) << 4 | (buttonA) << 6 | (buttonB) << 7) << 16;
input->currentState |= (y | input->lastY) << 8;
input->currentState |= (x | input->lastX) << 0;
}
static void update_mouse_sensitivity(Input* input) {
input->currentState = (input->currentState & ~0x300000) | ((input->mouseSens % 3) << (4 + 16));
}
void input_latch(Input* input, bool value) {
// if(input->latchLine && !value) {
if(value) {
input->latchedState = input->currentState;
if (input->type == DEVICE_MOUSE) {
update_mouse_sensitivity(input);
}
}
input->latchLine = value;
}
uint8_t input_read(Input* input) {
if(input->latchLine) {
input->latchedState = input->currentState;
}
uint8_t ret = 0;
switch (input->type) {
case DEVICE_NONE:
input_reset(input);
ret = 0x00;
break;
case DEVICE_MOUSE:
if (input->latchLine) {
// fun feature: reading the mouse while latched changes the sensitivty
// setting
update_mouse_sensitivity(input);
input->mouseSens++;
}
ret = (input->latchedState >> 31) & 1;
input->latchedState <<= 1;
input->latchedState |= 1;
break;
case DEVICE_GAMEPAD:
case DEVICE_SUPERSCOPE:
default:
ret = input->latchedState & 1;
input->latchedState >>= 1;
input->latchedState |= 0x8000;
break;
}
return ret;
}

39
src/burn/drv/snes/input.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef INPUT_H
#define INPUT_H
#include <stdint.h>
#include <stdbool.h>
typedef struct Input Input;
#include "snes.h"
#include "statehandler.h"
enum { DEVICE_NONE = 0, DEVICE_GAMEPAD = 1, DEVICE_SUPERSCOPE = 2, DEVICE_MOUSE = 3 };
enum { SCOPE_FIRE = 1 << 0, SCOPE_CURSOR = 1 << 1, SCOPE_TURBO = 1 << 2, SCOPE_PAUSE = 1 << 3, SCOPE_RELOAD = 1 << 6 };
struct Input {
Snes* snes;
uint8_t type;
uint8_t portTag;
// latchline
bool latchLine;
// for controller
uint32_t currentState; // actual state
uint32_t latchedState;
// for mouse
uint8_t lastX, lastY;
uint8_t mouseSens;
};
Input* input_init(Snes* snes, int8_t tag);
void input_free(Input* input);
void input_reset(Input* input);
void input_handleState(Input* input, StateHandler* sh);
void input_latch(Input* input, bool value);
uint8_t input_read(Input* input);
void input_setMouse(Input* input, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB);
void input_setType(Input* input, uint8_t type);
#endif

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-2024 elzo_d, dink
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1371
src/burn/drv/snes/ppu.cpp Normal file

File diff suppressed because it is too large Load Diff

56
src/burn/drv/snes/ppu.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef PPU_H
#define PPU_H
#include <stdint.h>
#include <stdbool.h>
#include "snes.h"
#include "statehandler.h"
typedef struct BgLayer {
uint16_t hScroll;
uint16_t vScroll;
bool tilemapWider;
bool tilemapHigher;
uint16_t tilemapAdr;
uint16_t tileAdr;
bool bigTiles;
bool mosaicEnabled;
} BgLayer;
typedef struct Layer {
bool mainScreenEnabled;
bool subScreenEnabled;
bool mainScreenWindowed;
bool subScreenWindowed;
} Layer;
typedef struct WindowLayer {
bool window1enabled;
bool window2enabled;
bool window1inversed;
bool window2inversed;
uint8_t maskLogic;
} WindowLayer;
void ppu_init(Snes* ssnes);
void ppu_free();
void ppu_reset();
void ppu_handleState(StateHandler* sh);
bool ppu_checkOverscan();
void ppu_handleVblank();
void ppu_handleFrameStart();
void ppu_runLine(int line);
void ppu_latchMode7(int line);
uint8_t ppu_read(uint8_t adr);
void ppu_write(uint8_t adr, uint8_t val);
void ppu_latchHV();
void ppu_latchScope(int x, int y);
void ppu_latchScopeCheck(bool reset);
void ppu_putPixels(uint8_t* pixels, int height);
void ppu_setPixelOutputFormat(int pixelOutputFormat);
bool ppu_frameInterlace();
bool ppu_evenFrame();
#endif

1000
src/burn/drv/snes/sa1.cpp Normal file

File diff suppressed because it is too large Load Diff

15
src/burn/drv/snes/sa1.h Normal file
View File

@ -0,0 +1,15 @@
void snes_sa1_init(void *mem, uint8_t *srom, int32_t sromsize, void *s_ram, int32_t s_ram_size);
void snes_sa1_run();
void snes_sa1_exit();
void snes_sa1_reset();
uint8_t snes_sa1_cart_read(uint32_t address);
void snes_sa1_cart_write(uint32_t address, uint8_t data);
void snes_sa1_handleState(StateHandler* sh);
uint8_t sa1_cpuRead(uint32_t adr);
void sa1_cpuWrite(uint32_t adr, uint8_t val);
void sa1_cpuIdle();
uint64_t sa1_getcycles();
bool sa1_isrom_address(uint32_t address);
bool sa1_snes_rom_conflict();
uint32_t sa1_snes_lastSpeed();

594
src/burn/drv/snes/sdd1.cpp Normal file
View File

@ -0,0 +1,594 @@
// SDD-1 Decoder by Andreas Naive (Public Domain)
// Mapper, everything else by dink
#include "snes.h"
#include "sdd1.h"
static uint8_t *rom;
static int32_t rom_mask;
static uint8_t *sram;
static int32_t sram_mask;
static uint8_t dma_enable[2];
static uint8_t banks[4];
static uint32_t dma_address[8];
static uint16_t dma_size[8];
static bool dma_initxfer;
static uint8_t rom_read(uint32_t address) {
return rom[(banks[(address >> 20) & 3] << 20) + (address & 0x0fffff)];
}
#define set_byte(var, data, offset) (var = (var & (~(0xff << (((offset) & 3) * 8)))) | (data << (((offset) & 3) * 8)))
struct SDD1_IM {
uint32_t byte_address;
uint8_t bit_count;
void init()
{
byte_address = 0;
bit_count = 0;
}
void prepareDecomp(uint32_t in_buf)
{
byte_address = in_buf;
bit_count = 4;
}
uint8_t getCodeword(const uint8_t code_len)
{
uint8_t codeword = rom_read(byte_address) << bit_count;
++bit_count;
if (codeword & 0x80) {
codeword |= rom_read(byte_address + 1) >> (9 - bit_count);
bit_count += code_len;
}
if (bit_count & 0x08) {
byte_address++;
bit_count &= 0x07;
}
return codeword;
}
};
static SDD1_IM IM;
struct SDD1_GCD {
void GCD_getRunCount(uint8_t code_num, uint8_t* MPScount, uint8_t* LPSind)
{
const uint8_t run_count[] =
{
0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00,
0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00,
0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01,
0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00,
0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03,
0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01,
0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02,
0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00,
0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07,
0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03,
0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05,
0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01,
0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06,
0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02,
0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04,
0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00,
0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f,
0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07,
0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b,
0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03,
0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d,
0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05,
0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09,
0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01,
0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e,
0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06,
0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a,
0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02,
0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04,
0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00,
};
uint8_t codeword = IM.getCodeword(code_num);
if (codeword & 0x80) {
*LPSind = 1;
*MPScount = run_count[codeword >> (code_num ^ 0x07)];
} else {
*MPScount = (1 << code_num);
}
}
};
static SDD1_GCD GCD;
struct SDD1_BG {
uint8_t code_num;
uint8_t MPScount;
uint8_t LPSind;
void init(int code)
{
code_num = code;
MPScount = 0;
LPSind = 0;
}
void prepareDecomp()
{
MPScount = 0;
LPSind = 0;
}
uint8_t getBit(uint8_t* endOfRun)
{
uint8_t bit;
if (!(MPScount || LPSind)) {
GCD.GCD_getRunCount(code_num, &MPScount, &LPSind);
}
if (MPScount) {
bit = 0;
MPScount--;
} else {
bit = 1;
LPSind = 0;
}
if (MPScount || LPSind) {
(*endOfRun) = 0;
} else {
(*endOfRun) = 1;
}
return bit;
}
};
static SDD1_BG BG[8];
struct SDD1_PEM_state
{
uint8_t code_num;
uint8_t nextIfMPS;
uint8_t nextIfLPS;
};
static const SDD1_PEM_state PEM_evolution_table[33] =
{
{ 0,25,25},
{ 0, 2, 1},
{ 0, 3, 1},
{ 0, 4, 2},
{ 0, 5, 3},
{ 1, 6, 4},
{ 1, 7, 5},
{ 1, 8, 6},
{ 1, 9, 7},
{ 2,10, 8},
{ 2,11, 9},
{ 2,12,10},
{ 2,13,11},
{ 3,14,12},
{ 3,15,13},
{ 3,16,14},
{ 3,17,15},
{ 4,18,16},
{ 4,19,17},
{ 5,20,18},
{ 5,21,19},
{ 6,22,20},
{ 6,23,21},
{ 7,24,22},
{ 7,24,23},
{ 0,26, 1},
{ 1,27, 2},
{ 2,28, 4},
{ 3,29, 8},
{ 4,30,12},
{ 5,31,16},
{ 6,32,18},
{ 7,24,22}
};
struct SDD1_PEM_ContextInfo
{
uint8_t status;
uint8_t MPS;
};
struct SDD1_PEM {
SDD1_PEM_ContextInfo contextInfo[32];
void prepareDecomp()
{
for (int i = 0; i < 32; i++)
{
contextInfo[i].status = 0;
contextInfo[i].MPS = 0;
}
}
uint8_t getBit(uint8_t context)
{
uint8_t endOfRun;
uint8_t bit;
SDD1_PEM_ContextInfo *pContInfo = &(contextInfo)[context];
uint8_t currStatus = pContInfo->status;
const SDD1_PEM_state* pState = &(PEM_evolution_table[currStatus]);
uint8_t currentMPS = pContInfo->MPS;
bit = BG[pState->code_num].getBit(&endOfRun);
if (endOfRun) {
if (bit) {
if (!(currStatus & 0xfe)) {
(pContInfo->MPS) ^= 0x01;
}
pContInfo->status = pState->nextIfLPS;
} else {
pContInfo->status = pState->nextIfMPS;
}
}
return bit ^ currentMPS;
}
};
static SDD1_PEM PEM;
struct SDD1_CM {
uint8_t bitplanesInfo;
uint8_t contextBitsInfo;
uint8_t bit_number;
uint8_t currBitplane;
uint16_t prevBitplaneBits[8];
void init()
{
bitplanesInfo = 0;
contextBitsInfo = 0;
bit_number = 0;
currBitplane = 0;
for (int i = 0; i < 8; i++) {
prevBitplaneBits[i];
}
}
void prepareDecomp(uint32_t first_byte)
{
bitplanesInfo = rom_read(first_byte) & 0xc0;
contextBitsInfo = rom_read(first_byte) & 0x30;
bit_number = 0;
for (int i = 0; i < 8; i++) {
prevBitplaneBits[i] = 0;
}
switch (bitplanesInfo) {
case 0x00:
currBitplane = 1;
break;
case 0x40:
currBitplane = 7;
break;
case 0x80:
currBitplane = 3;
break;
}
}
uint8_t getBit()
{
uint8_t currContext;
uint16_t *context_bits;
switch (bitplanesInfo) {
case 0x00:
currBitplane ^= 0x01;
break;
case 0x40:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f))
currBitplane = ((currBitplane + 2) & 0x07);
break;
case 0x80:
currBitplane ^= 0x01;
if (!(bit_number & 0x7f))
currBitplane ^= 0x02;
break;
case 0xc0:
currBitplane = bit_number & 0x07;
break;
}
context_bits = &(prevBitplaneBits)[currBitplane];
currContext = (currBitplane & 0x01) << 4;
switch (contextBitsInfo) {
case 0x00:
currContext |= ((*context_bits & 0x01c0) >> 5) | (*context_bits & 0x0001);
break;
case 0x10:
currContext |= ((*context_bits & 0x0180) >> 5) | (*context_bits & 0x0001);
break;
case 0x20:
currContext |= ((*context_bits & 0x00c0) >> 5) | (*context_bits & 0x0001);
break;
case 0x30:
currContext |= ((*context_bits & 0x0180) >> 5) | (*context_bits & 0x0003);
break;
}
uint8_t bit = PEM.getBit(currContext);
*context_bits <<= 1;
*context_bits |= bit;
bit_number++;
return bit;
}
};
static SDD1_CM CM;
struct SDD1_OL {
uint8_t bitplanesInfo;
uint16_t length;
uint8_t i;
uint8_t register1;
uint8_t register2;
void init()
{
bitplanesInfo = 0;
length = 0;
i = 0;
register1 = 0;
register2 = 0;
}
void prepareDecomp(uint32_t first_byte, uint16_t out_len)
{
bitplanesInfo = rom_read(first_byte) & 0xc0;
length = out_len;
i = 1;
register1 = 0;
register2 = 0;
}
uint16_t Get8Bits() // hi byte = status, 0x100 = end of transfer
{
if (length == 0) return 0x100;
switch (bitplanesInfo)
{
case 0x00:
case 0x40:
case 0x80:
if (!i) {
i = ~i;
length--;
return register2 | (length == 0 ? 0x100 : 0);
} else {
for (register1 = register2 = 0, i = 0x80; i; i >>= 1) {
if (CM.getBit())
register1 |= i;
if (CM.getBit())
register2 |= i;
}
length--;
return register1 | (length == 0 ? 0x100 : 0);
}
break;
case 0xc0:
for (register1 = 0, i = 0x01; i; i <<= 1) {
if (CM.getBit())
register1 |= i;
}
length--;
return register1 | (length == 0 ? 0x100 : 0);
}
return 0x100;
}
};
static SDD1_OL OL;
static void SDD1emu_begin_decompress(uint32_t start_address, uint16_t out_len)
{
IM.prepareDecomp(start_address);
BG[0].prepareDecomp();
BG[1].prepareDecomp();
BG[2].prepareDecomp();
BG[3].prepareDecomp();
BG[4].prepareDecomp();
BG[5].prepareDecomp();
BG[6].prepareDecomp();
BG[7].prepareDecomp();
PEM.prepareDecomp();
CM.prepareDecomp(start_address);
OL.prepareDecomp(start_address, out_len);
}
void snes_sdd1_init(uint8_t *s_rom, int32_t rom_size, void *s_ram, int32_t sram_size)
{
rom = s_rom;
rom_mask = rom_size - 1;
bprintf(0, _T("sdd1 rom mask %x\n"), rom_mask);
sram_mask = (sram_mask != 0) ? (sram_size - 1) : 0;
sram = (uint8_t*)s_ram;
}
void snes_sdd1_exit()
{
}
void snes_sdd1_reset()
{
dma_enable[0] = dma_enable[1] = 0;
banks[0] = 0;
banks[1] = 1;
banks[2] = 2;
banks[3] = 3;
IM.init();
CM.init();
OL.init();
for (int i = 0; i < 8; i++) {
BG[i].init(i);
dma_address[i] = 0;
dma_size[i] = 0;
}
}
void snes_sdd1_handleState(StateHandler* sh) {
sh_handleBools(sh, &dma_initxfer, NULL);
sh_handleBytes(sh, &dma_enable[0], &dma_enable[1], &banks[0], &banks[1], &banks[2], &banks[3], NULL);
sh_handleWords(sh, &dma_size[0], &dma_size[1], &dma_size[2], &dma_size[3], &dma_size[4], &dma_size[5], &dma_size[6], &dma_size[7], NULL);
sh_handleInts(sh, &dma_address[0], &dma_address[1], &dma_address[2], &dma_address[3], &dma_address[4], &dma_address[5], &dma_address[6], &dma_address[7], NULL);
sh_handleBytes(sh, &BG[0].code_num, &BG[0].MPScount, &BG[0].LPSind, &BG[1].code_num, &BG[1].MPScount, &BG[1].LPSind, &BG[2].code_num, &BG[2].MPScount, &BG[2].LPSind, &BG[3].code_num, &BG[3].MPScount, &BG[3].LPSind, NULL);
sh_handleBytes(sh, &BG[4].code_num, &BG[4].MPScount, &BG[4].LPSind, &BG[5].code_num, &BG[5].MPScount, &BG[5].LPSind, &BG[6].code_num, &BG[6].MPScount, &BG[6].LPSind, &BG[7].code_num, &BG[7].MPScount, &BG[7].LPSind, NULL);
sh_handleBytes(sh, &IM.bit_count, &CM.bitplanesInfo, &CM.contextBitsInfo, &CM.bit_number, &CM.currBitplane, &OL.bitplanesInfo, &OL.i, &OL.register1, &OL.register2, NULL);
sh_handleWords(sh, &CM.prevBitplaneBits[0], &CM.prevBitplaneBits[1], &CM.prevBitplaneBits[2], &CM.prevBitplaneBits[3], &CM.prevBitplaneBits[4], &CM.prevBitplaneBits[5], &CM.prevBitplaneBits[6], &CM.prevBitplaneBits[7], &OL.length, NULL);
sh_handleInts(sh, &IM.byte_address, NULL);
sh_handleByteArray(sh, (uint8_t*)&PEM.contextInfo, sizeof(PEM.contextInfo));
}
static uint8_t sdd1_mapper_read(uint32_t address)
{
switch (address) {
case 0x4804:
case 0x4805:
case 0x4806:
case 0x4807:
return banks[address & 3];
}
return 0xff;
}
static void sdd1_mapper_write(uint32_t address, uint8_t data)
{
switch (address) {
case 0x4800:
case 0x4801:
dma_enable[address & 1] = data;
break;
case 0x4804:
case 0x4805:
case 0x4806:
case 0x4807:
banks[address & 3] = data & 0xf;
break;
}
if (address >= 0x4300 && address <= 0x437f) {
switch(address & 0xf) {
case 2:
case 3:
case 4:
set_byte(dma_address[(address >> 4) & 7], data, (address & 0xf) - 2);
break;
case 5:
case 6:
set_byte(dma_size[(address >> 4) & 7], data, (address & 0xf) - 5);
break;
}
}
}
static uint8_t sdd1_bankedarea_dma_read(uint32_t address)
{
const uint8_t dma_active = dma_enable[0] & dma_enable[1];
if (dma_active) {
for (int i = 0; i < 8; i++) {
if (dma_active & (1 << i) && address == dma_address[i]) {
if (dma_initxfer == false) {
//bprintf(0, _T("dma, r: %x dma_addr[%x]: %x size: %x\n"), address, i, dma_address[i], dma_size[i]);
SDD1emu_begin_decompress(address, dma_size[i]);
dma_initxfer = true;
}
uint16_t data = OL.Get8Bits();
if (data & 0x100) { // EOT
dma_initxfer = false;
dma_enable[1] &= ~(1 << i);
}
return data & 0xff;
}
}
}
return rom_read(address);
}
uint8_t snes_sdd1_cart_read(uint32_t address)
{
const uint32_t bank = (address & 0xff0000) >> 16;
if ((bank & 0xc0) == 0xc0) {
return sdd1_bankedarea_dma_read(address);
}
if ( (bank & 0x7f) < 0x40 && (address & 0xf000) == 0x4000 ) {
return sdd1_mapper_read(address & 0xffff);
}
if ( (bank & 0x7f) < 0x40 && (address & 0x8000) ) { // LoROM accessor
return rom[(((bank & 0x7f) << 15) + (address & 0x7fff)) & rom_mask];
}
if ( (bank >= 0x70 && bank <= 0x73) || ((bank & 0x7f) <= 0x3f && (address & 0xe000) == 0x6000) ) {
if (sram_mask) {
return sram[address & sram_mask];
}
}
bprintf(0, _T("sdd1 - unmap_read: %x\n"), address);
return 0xff;
}
void snes_sdd1_cart_write(uint32_t address, uint8_t data)
{
const uint32_t bank = (address & 0xff0000) >> 16;
if ( (bank & 0x7f) < 0x40 && (address & 0xf000) == 0x4000 ) {
sdd1_mapper_write(address & 0xffff, data);
return;
}
if ( (bank >= 0x70 && bank <= 0x73) || ((bank & 0x7f) <= 0x3f && (address & 0xe000) == 0x6000) ) {
if (sram_mask) {
sram[address & sram_mask] = data;
}
return;
}
}

6
src/burn/drv/snes/sdd1.h Normal file
View File

@ -0,0 +1,6 @@
void snes_sdd1_init(uint8_t *s_rom, int32_t rom_size, void *s_ram, int32_t sram_size);
void snes_sdd1_exit();
void snes_sdd1_reset();
void snes_sdd1_handleState(StateHandler* sh);
uint8_t snes_sdd1_cart_read(uint32_t address);
void snes_sdd1_cart_write(uint32_t address, uint8_t data);

696
src/burn/drv/snes/snes.cpp Normal file
View File

@ -0,0 +1,696 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "snes.h"
#include "cpu.h"
#include "apu.h"
#include "spc.h"
#include "dma.h"
#include "ppu.h"
#include "cart.h"
#include "cx4.h"
#include "input.h"
#include "statehandler.h"
#include "sa1.h" // debug
static void snes_runCycle(Snes* snes);
static void snes_catchupApu(Snes* snes);
static void snes_doAutoJoypad(Snes* snes);
static uint8_t snes_readReg(Snes* snes, uint16_t adr);
static void snes_writeReg(Snes* snes, uint16_t adr, uint8_t val);
static uint8_t snes_rread(Snes* snes, uint32_t adr); // wrapped by read, to set open bus
static int snes_getAccessTime(Snes* snes, uint32_t adr);
static void build_accesstime(Snes* snes);
static void free_accesstime();
static uint8_t *access_time;
Snes* snes_init(void) {
Snes* snes = (Snes*)BurnMalloc(sizeof(Snes));
cpu_init(snes); //, snes_cpuRead, snes_cpuWrite, snes_cpuIdle);
snes->apu = apu_init(snes);
snes->dma = dma_init(snes);
ppu_init(snes);
snes->cart = cart_init(snes);
snes->input1 = input_init(snes, 1);
snes->input2 = input_init(snes, 2);
snes->palTiming = false;
return snes;
}
void snes_free(Snes* snes) {
cpu_free();
apu_free(snes->apu);
dma_free(snes->dma);
ppu_free();
cart_free(snes->cart);
input_free(snes->input1);
input_free(snes->input2);
free_accesstime();
BurnFree(snes);
}
void snes_reset(Snes* snes, bool hard) {
cpu_reset(hard);
apu_reset(snes->apu);
dma_reset(snes->dma);
ppu_reset();
input_reset(snes->input1);
input_reset(snes->input2);
cart_reset(snes->cart);
if(hard) memset(snes->ram, snes->ramFill, sizeof(snes->ram));
snes->ramAdr = 0;
snes->hPos = 0;
snes->vPos = 0;
snes->frames = 0;
snes->cycles = 0;
snes->syncCycle = 0;
snes->hIrqEnabled = false;
snes->vIrqEnabled = false;
snes->nmiEnabled = false;
snes->hTimer = 0x1ff * 4;
snes->vTimer = 0x1ff;
snes->hvTimer = 0;
snes->inNmi = false;
snes->irqCondition = false;
snes->inIrq = false;
snes->inVblank = false;
snes->inRefresh = false;
memset(snes->portAutoRead, 0, sizeof(snes->portAutoRead));
snes->autoJoyRead = false;
snes->autoJoyTimer = 0;
snes->ppuLatch = true;
snes->multiplyA = 0xff;
snes->multiplyResult = 0xfe01;
snes->divideA = 0xffff;
snes->divideResult = 0x101;
snes->fastMem = false;
snes->openBus = 0;
build_accesstime(snes);
snes->nextHoriEvent = 16;
}
void snes_handleState(Snes* snes, StateHandler* sh) {
sh_handleBools(sh,
&snes->palTiming, &snes->hIrqEnabled, &snes->vIrqEnabled, &snes->nmiEnabled, &snes->inNmi, &snes->irqCondition,
&snes->inIrq, &snes->inVblank, &snes->autoJoyRead, &snes->ppuLatch, &snes->fastMem, NULL
);
sh_handleBytes(sh, &snes->multiplyA, &snes->openBus, NULL);
sh_handleWords(sh,
&snes->hPos, &snes->vPos, &snes->hTimer, &snes->vTimer,
&snes->portAutoRead[0], &snes->portAutoRead[1], &snes->portAutoRead[2], &snes->portAutoRead[3],
&snes->multiplyResult, &snes->divideA, &snes->divideResult, NULL
);
sh_handleInts(sh, &snes->hvTimer, &snes->ramAdr, &snes->frames, &snes->nextHoriEvent, NULL);
sh_handleLongLongs(sh, &snes->cycles, &snes->syncCycle, &snes->autoJoyTimer, NULL);
sh_handleByteArray(sh, snes->ram, 0x20000);
// components
cpu_handleState(sh);
dma_handleState(snes->dma, sh);
ppu_handleState(sh);
apu_handleState(snes->apu, sh);
input_handleState(snes->input1, sh);
input_handleState(snes->input2, sh);
cart_handleState(snes->cart, sh);
}
#define DEBUG_CYC 0
void snes_runFrame(Snes* snes) {
#if DEBUG_CYC
uint32_t apu_cyc_start = apu_cycles(snes->apu);
uint64_t cpu_cyc_start = snes->cycles;
bprintf(0, _T("fr. %d: cycles start frame: %I64u\n"), nCurrentFrame, snes->cycles);
#endif
while(snes->inVblank) {
cpu_runOpcode();
}
// then run until we are at vblank, or we end up at next frame (DMA caused vblank to be skipped)
uint32_t frame = snes->frames;
while(!snes->inVblank && frame == snes->frames) {
cpu_runOpcode();
}
#if DEBUG_CYC
uint32_t apu_cyc_end = apu_cycles(snes->apu);
uint64_t cpu_cyc_end = snes->cycles;
bprintf(0, _T("%04d: apu / cpu cycles ran: %d\t\t%I64u\n"), nCurrentFrame, apu_cyc_end - apu_cyc_start, cpu_cyc_end - cpu_cyc_start);
bprintf(0, _T("fr. %d: cycles -end- frame: %I64u\n"), nCurrentFrame, snes->cycles);
#endif
}
void snes_runCycles(Snes* snes, int cycles) {
for(int i = 0; i < cycles; i += 2) {
snes_runCycle(snes);
}
}
void snes_runCycles4(Snes* snes) {
snes_runCycle(snes);
snes_runCycle(snes);
}
void snes_runCycles6(Snes* snes) {
snes_runCycle(snes);
snes_runCycle(snes);
snes_runCycle(snes);
}
void snes_runCycles8(Snes* snes) {
snes_runCycle(snes);
snes_runCycle(snes);
snes_runCycle(snes);
snes_runCycle(snes);
}
void snes_syncCycles(Snes* snes, bool start, int syncCycles) {
if(start) {
snes->syncCycle = snes->cycles;
int count = syncCycles - (snes->cycles % syncCycles);
snes_runCycles(snes, count);
} else {
int count = syncCycles - ((snes->cycles - snes->syncCycle) % syncCycles);
snes_runCycles(snes, count);
}
}
int snes_verticalLinecount(Snes* snes) {
if(!snes->palTiming) {
// even interlace frame is 263 lines
return (!ppu_frameInterlace() || !ppu_evenFrame()) ? 262 : 263;
} else {
// even interlace frame is 313 lines
return (!ppu_frameInterlace() || !ppu_evenFrame()) ? 312 : 313;
}
}
static void snes_runCycle(Snes* snes) {
snes->cycles += 2;
if(snes->cart->heavySync) {
cart_run(); // run spetzi chippy
}
if ((snes->hPos & 2) == 0) {
// check for h/v timer irq's every 4 cycles
if (snes->hvTimer > 0) {
snes->hvTimer -= 2;
if (snes->hvTimer == 0) {
// bprintf(0, _T("IRQ @ %d,%d\n"),snes->hPos,snes->vPos);
snes->inIrq = true;
cpu_setIrq(true);
}
}
const bool condition = (
(snes->vIrqEnabled || snes->hIrqEnabled) &&
(snes->vPos == snes->vTimer || !snes->vIrqEnabled) &&
(snes->hPos == snes->hTimer || !snes->hIrqEnabled)
);
if(!snes->irqCondition && condition) {
snes->hvTimer = 4;
}
snes->irqCondition = condition;
}
// increment position
snes->hPos += 2; // must come after irq checks! (hagane, cybernator)
// handle positional stuff
if (snes->hPos == snes->nextHoriEvent) {
switch (snes->hPos) {
case 16: {
snes->nextHoriEvent = 22;
if(snes->vPos == 0) snes->dma->hdmaInitRequested = true;
} break;
case 22: {
snes->nextHoriEvent = 512;
if(!snes->inVblank && snes->vPos > 0) ppu_latchMode7(snes->vPos);
} break;
case 512: {
snes->nextHoriEvent = 538;
// render the line halfway of the screen for better compatibility
if(!snes->inVblank && snes->vPos > 0) ppu_runLine(snes->vPos);
} break;
case 538: {
snes->nextHoriEvent = 1104;
// +40cycle dram refresh
snes->inRefresh = true;
snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes);
snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes);
snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes);
snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes); snes_runCycle(snes);
snes->inRefresh = false;
} break;
case 1104: {
if(!snes->inVblank) snes->dma->hdmaRunRequested = true;
if(!snes->palTiming) {
// line 240 of odd frame with no interlace is 4 cycles shorter
// if((snes->hPos == 1360 && snes->vPos == 240 && !ppu_evenFrame() && !ppu_frameInterlace()) || snes->hPos == 1364) {
snes->nextHoriEvent = (snes->vPos == 240 && !ppu_evenFrame() && !ppu_frameInterlace()) ? 1360 : 1364;
//bprintf(0, _T("%d,"),snes->nextHoriEvent);
} else {
// line 311 of odd frame with interlace is 4 cycles longer
// if((snes->hPos == 1364 && (snes->vPos != 311 || ppu_evenFrame() || !ppu_frameInterlace())) || snes->hPos == 1368)
snes->nextHoriEvent = (snes->vPos != 311 || ppu_evenFrame() || !ppu_frameInterlace()) ? 1364 : 1368;
}
} break;
case 1360:
case 1364:
case 1368: { // this is the end (of the h-line)
snes->nextHoriEvent = 16;
snes->hPos = 0;
snes->vPos++;
if(!snes->palTiming) {
// even interlace frame is 263 lines
if((snes->vPos == 262 && (!ppu_frameInterlace() || !ppu_evenFrame())) || snes->vPos == 263) {
cart_run();
snes->vPos = 0;
snes->frames++;
}
} else {
// even interlace frame is 313 lines
if((snes->vPos == 312 && (!ppu_frameInterlace() || !ppu_evenFrame())) || snes->vPos == 313) {
cart_run();
snes->vPos = 0;
snes->frames++;
}
}
// end of hblank, do most vPos-tests
bool startingVblank = false;
if(snes->vPos == 0) {
// end of vblank
snes->inVblank = false;
snes->inNmi = false;
ppu_handleFrameStart();
} else if(snes->vPos == 225) {
// ask the ppu if we start vblank now or at vPos 240 (overscan)
startingVblank = !ppu_checkOverscan();
} else if(snes->vPos == 240){
// if we are not yet in vblank, we had an overscan frame, set startingVblank
if(!snes->inVblank) startingVblank = true;
}
if(startingVblank) {
// catch up the apu at end of emulated frame (we end frame @ start of vblank)
snes_catchupApu(snes);
// notify dsp of frame-end, because sometimes dma will extend much further past vblank (or even into the next frame)
// Megaman X2 (titlescreen animation), Tales of Phantasia (game demo), Actraiser 2 (fade-in @ bootup)
dsp_newFrame(snes->apu->dsp);
// we are starting vblank
ppu_handleVblank();
snes->inVblank = true;
snes->inNmi = true;
if(snes->autoJoyRead) {
// TODO: this starts a little after start of vblank
snes->autoJoyTimer = snes->cycles; // 4224;
snes_doAutoJoypad(snes);
}
if(snes->nmiEnabled) {
cpu_nmi();
}
}
} break;
}
}
}
static void snes_catchupApu(Snes* snes) {
apu_runCycles(snes->apu);
}
static void snes_doAutoJoypad(Snes* snes) {
memset(snes->portAutoRead, 0, sizeof(snes->portAutoRead));
// latch controllers
input_latch(snes->input1, true);
input_latch(snes->input2, true);
input_latch(snes->input1, false);
input_latch(snes->input2, false);
for(int i = 0; i < 16; i++) {
uint8_t val = input_read(snes->input1);
snes->portAutoRead[0] |= ((val & 1) << (15 - i));
snes->portAutoRead[2] |= (((val >> 1) & 1) << (15 - i));
val = input_read(snes->input2);
snes->portAutoRead[1] |= ((val & 1) << (15 - i));
snes->portAutoRead[3] |= (((val >> 1) & 1) << (15 - i));
}
}
uint8_t snes_readBBus(Snes* snes, uint8_t adr) {
if(adr < 0x40) {
return ppu_read(adr);
}
if(adr < 0x80) {
snes_catchupApu(snes); // catch up the apu before reading
// bprintf(0, _T("SR:%x %x\t\t%I64u\t\tpc: %x\n"), adr&3,snes->apu->outPorts[adr & 0x3],snes->apu->cycles,snes->apu->spc->pc);
return snes->apu->outPorts[adr & 0x3];
}
if(adr == 0x80) {
uint8_t ret = snes->ram[snes->ramAdr++];
snes->ramAdr &= 0x1ffff;
return ret;
}
return snes->openBus;
}
void snes_writeBBus(Snes* snes, uint8_t adr, uint8_t val) {
if(adr < 0x40) {
ppu_write(adr, val);
return;
}
if(adr < 0x80) {
snes_catchupApu(snes); // catch up the apu before writing
// bprintf(0, _T("SW:%x %x\t\t%I64u\t\tpc: %x\n"), adr&3,val,snes->apu->cycles,snes->apu->spc->pc);
snes->apu->inPorts[adr & 0x3] = val;
return;
}
switch(adr) {
case 0x80: {
snes->ram[snes->ramAdr++] = val;
snes->ramAdr &= 0x1ffff;
break;
}
case 0x81: {
snes->ramAdr = (snes->ramAdr & 0x1ff00) | val;
break;
}
case 0x82: {
snes->ramAdr = (snes->ramAdr & 0x100ff) | (val << 8);
break;
}
case 0x83: {
snes->ramAdr = (snes->ramAdr & 0x0ffff) | ((val & 1) << 16);
break;
}
}
}
static uint8_t snes_readReg(Snes* snes, uint16_t adr) {
switch(adr) {
case 0x4210: {
uint8_t val = 0x2; // CPU version (4 bit)
val |= snes->inNmi << 7;
snes->inNmi = false;
return val | (snes->openBus & 0x70);
}
case 0x4211: {
// bprintf(0, _T("TIMEUP 0x%x @ %d %d inirq %x\n"), adr, snes->hPos, snes->vPos,snes->inIrq);
uint8_t val = snes->inIrq << 7;
snes->inIrq = false;
cpu_setIrq(false);
return val | (snes->openBus & 0x7f);
}
case 0x4212: {
uint8_t val = ((snes->cycles - snes->autoJoyTimer) <= 4224);
val |= (snes->hPos < 4 || snes->hPos >= 1096) << 6;
val |= snes->inVblank << 7;
return val | (snes->openBus & 0x3e);
}
case 0x4213: {
return snes->ppuLatch << 7; // IO-port
}
case 0x4214: {
return snes->divideResult & 0xff;
}
case 0x4215: {
return snes->divideResult >> 8;
}
case 0x4216: {
return snes->multiplyResult & 0xff;
}
case 0x4217: {
return snes->multiplyResult >> 8;
}
case 0x4218:
case 0x421a:
case 0x421c:
case 0x421e: {
return snes->portAutoRead[(adr - 0x4218) / 2] & 0xff;
}
case 0x4219:
case 0x421b:
case 0x421d:
case 0x421f: {
return snes->portAutoRead[(adr - 0x4219) / 2] >> 8;
}
default: {
return snes->openBus;
}
}
}
static void snes_writeReg(Snes* snes, uint16_t adr, uint8_t val) {
//bprintf(0, _T("write 0x%x %x @ %d %d\n"), adr, val, snes->hPos, snes->vPos);
switch(adr) {
case 0x4200: {
snes->autoJoyRead = val & 0x1;
if(!snes->autoJoyRead) snes->autoJoyTimer = 0;
snes->hIrqEnabled = val & 0x10;
snes->vIrqEnabled = val & 0x20;
if(!snes->hIrqEnabled && !snes->vIrqEnabled) {
snes->inIrq = false;
cpu_setIrq(false);
}
// if nmi is enabled while inNmi is still set, immediately generate nmi
if(!snes->nmiEnabled && (val & 0x80) && snes->inNmi) {
cpu_nmi();
}
snes->nmiEnabled = val & 0x80;
cpu_setIntDelay(); // nmi is delayed by 1 opcode
break;
}
case 0x4201: {
if(!(val & 0x80) && snes->ppuLatch) {
// latch the ppu h/v registers
ppu_latchHV();
}
snes->ppuLatch = val & 0x80;
break;
}
case 0x4202: {
snes->multiplyA = val;
break;
}
case 0x4203: {
snes->multiplyResult = snes->multiplyA * val;
break;
}
case 0x4204: {
snes->divideA = (snes->divideA & 0xff00) | val;
break;
}
case 0x4205: {
snes->divideA = (snes->divideA & 0x00ff) | (val << 8);
break;
}
case 0x4206: {
if(val == 0) {
snes->divideResult = 0xffff;
snes->multiplyResult = snes->divideA;
} else {
snes->divideResult = snes->divideA / val;
snes->multiplyResult = snes->divideA % val;
}
break;
}
case 0x4207: {
//snes->hTimer = (snes->hTimer & 0x100) | val;
snes->hTimer = (snes->hTimer & 0x400) | (val << 2);
//bprintf(0, _T("hTimer.l %x both %x\n"), val<<2, snes->hTimer);
break;
}
case 0x4208: {
//snes->hTimer = (snes->hTimer & 0x0ff) | ((val & 1) << 8);
snes->hTimer = (snes->hTimer & 0x03fc) | ((val & 1) << 10);
//bprintf(0, _T("hTimer.h %x both %x\n"), (val&1)<<10, snes->hTimer);
break;
}
case 0x4209: {
snes->vTimer = (snes->vTimer & 0x100) | val;
//bprintf(0, _T("vTimer.l %x both %x\n"), val, snes->vTimer);
break;
}
case 0x420a: {
snes->vTimer = (snes->vTimer & 0x0ff) | ((val & 1) << 8);
//bprintf(0, _T("vTimer.h %x both %x\n"), ((val & 1) << 8), snes->vTimer);
break;
}
case 0x420b: {
dma_startDma(snes->dma, val, false);
break;
}
case 0x420c: {
dma_startDma(snes->dma, val, true);
break;
}
case 0x420d: {
snes->fastMem = val & 0x1;
//bprintf(0, _T("fastMem %x\n"), val);
break;
}
default: {
break;
}
}
}
static uint8_t snes_rread(Snes* snes, uint32_t adr) {
const uint8_t bank = adr >> 16;
adr &= 0xffff;
if(bank == 0x7e || bank == 0x7f) {
return snes->ram[((bank & 1) << 16) | adr]; // ram
}
if(bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
if(adr < 0x2000) {
return snes->ram[adr]; // ram mirror
}
if(adr >= 0x2100 && adr < 0x2200) {
return snes_readBBus(snes, adr & 0xff); // B-bus
}
if(adr == 0x4016) {
return input_read(snes->input1) | (snes->openBus & 0xfc);
}
if(adr == 0x4017) {
return input_read(snes->input2) | (snes->openBus & 0xe0) | 0x1c;
}
if(adr >= 0x4200 && adr < 0x4220) {
return snes_readReg(snes, adr); // internal registers
}
if(adr >= 0x4300 && adr < 0x4380) {
return dma_read(snes->dma, adr); // dma registers
}
}
// read from cart
return cart_read(snes->cart, bank, adr);
}
void snes_write(Snes* snes, uint32_t adr, uint8_t val) {
snes->openBus = val;
snes->adrBus = adr;
const uint8_t bank = adr >> 16;
adr &= 0xffff;
if(bank == 0x7e || bank == 0x7f) {
snes->ram[((bank & 1) << 16) | adr] = val; // ram
}
if(bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) {
if(adr < 0x2000) {
snes->ram[adr] = val; // ram mirror
}
if(adr >= 0x2100 && adr < 0x2200) {
snes_writeBBus(snes, adr & 0xff, val); // B-bus
}
if(adr == 0x4016) {
input_latch(snes->input1, val & 1); // input latch
input_latch(snes->input2, val & 1);
}
if(adr >= 0x4200 && adr < 0x4220) {
snes_writeReg(snes, adr, val); // internal registers
}
if(adr >= 0x4300 && adr < 0x4380) {
dma_write(snes->dma, adr, val); // dma registers
}
}
// write to cart
cart_write(snes->cart, bank, adr, val);
}
#if 0
static int snes_getAccessTime(Snes* snes, uint32_t adr) {
uint8_t bank = adr >> 16;
adr &= 0xffff;
if((bank < 0x40 || (bank >= 0x80 && bank < 0xc0)) && adr < 0x8000) {
// 00-3f,80-bf:0-7fff
if(adr < 0x2000 || adr >= 0x6000) return 8; // 0-1fff, 6000-7fff
if(adr < 0x4000 || adr >= 0x4200) return 6; // 2000-3fff, 4200-5fff
return 12; // 4000-41ff
}
// 40-7f,co-ff:0000-ffff, 00-3f,80-bf:8000-ffff
return (snes->fastMem && bank >= 0x80) ? 6 : 8; // depends on setting in banks 80+
}
#endif
static inline int snes_getAccessTime(Snes* snes, UINT32 adr)
{
if ((adr & 0x408000) == 0)
{
adr &= 0xffff;
// 00-3f,80-bf:0-7fff
if(adr < 0x2000 || adr >= 0x6000) return 8; // 0-1fff, 6000-7fff
if(adr < 0x4000 || adr >= 0x4200) return 6; // 2000-3fff, 4200-5fff
return 12; // 4000-41ff
}
// 40-7f,co-ff:0000-ffff, 00-3f,80-bf:8000-ffff
return (snes->fastMem && (adr & 0x800000)) ? 6 : 8; // depends on setting in banks 80+
}
static void build_accesstime(Snes* snes) {
if (access_time == NULL) {
access_time = (uint8_t *)BurnMalloc(0x1000000 * 2);
}
snes->fastMem = 0;
for (int i = 0; i < 0x1000000; i++) {
access_time[i] = snes_getAccessTime(snes, i);
}
snes->fastMem = 1;
for (int i = 0x1000000; i < 0x2000000; i++) {
access_time[i] = snes_getAccessTime(snes, i & 0xffffff);
}
snes->fastMem = 0;
}
static void free_accesstime() {
BurnFree(access_time);
}
uint8_t snes_read(Snes* snes, uint32_t adr) {
snes->adrBus = adr;
uint8_t val = snes_rread(snes, adr);
snes->openBus = val;
return val;
}
void snes_cpuIdle(void* mem, bool waiting) {
Snes* snes = (Snes*) mem;
dma_handleDma(snes->dma, 6);
snes_runCycles6(snes);
}
uint8_t snes_cpuRead(void* mem, uint32_t adr) {
Snes* snes = (Snes*) mem;
const int cycles = access_time[adr + (snes->fastMem << 24)];
// const int cycles = snes_getAccessTime(snes, adr);
dma_handleDma(snes->dma, cycles);
snes->adrBus = adr;
snes_runCycles(snes, cycles - 4);
const uint8_t rv = snes_read(snes, adr);
snes_runCycles4(snes);
return rv;
}
void snes_cpuWrite(void* mem, uint32_t adr, uint8_t val) {
Snes* snes = (Snes*) mem;
const int cycles = access_time[adr + (snes->fastMem << 24)];
//const int cycles = snes_getAccessTime(snes, adr);
dma_handleDma(snes->dma, cycles);
snes->adrBus = adr;
snes_runCycles(snes, cycles);
snes_write(snes, adr, val);
}
// debugging
void snes_runCpuCycle(Snes* snes) {
cpu_runOpcode();
}
void snes_runSpcCycle(Snes* snes) {
// TODO: apu catchup is not aware of this, SPC runs extra cycle(s)
spc_runOpcode(snes->apu->spc);
}

102
src/burn/drv/snes/snes.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef SNES_H
#define SNES_H
#include "burnint.h"
typedef struct Snes Snes;
#include "cpu.h"
#include "apu.h"
#include "dma.h"
#include "ppu.h"
#include "cart.h"
#include "input.h"
#include "statehandler.h"
struct Snes {
Cpu* cpu;
Apu* apu;
Dma* dma;
Cart* cart;
bool palTiming;
// input
Input* input1;
Input* input2;
// ram
uint8_t ram[0x20000];
uint32_t ramAdr;
uint8_t ramFill;
// frame timing
int16_t hPos;
uint16_t vPos;
uint32_t frames;
uint64_t cycles;
uint64_t syncCycle;
uint32_t nextHoriEvent;
// cpu handling
// nmi / irq
bool hIrqEnabled;
bool vIrqEnabled;
bool nmiEnabled;
uint16_t hTimer;
uint16_t vTimer;
uint32_t hvTimer;
bool inNmi;
bool irqCondition;
bool inIrq;
bool inVblank;
bool inRefresh;
// joypad handling
uint16_t portAutoRead[4]; // as read by auto-joypad read
bool autoJoyRead;
uint64_t autoJoyTimer; // times how long until reading is done
bool ppuLatch;
// multiplication/division
uint8_t multiplyA;
uint16_t multiplyResult;
uint16_t divideA;
uint16_t divideResult;
// misc
bool fastMem;
uint32_t adrBus;
uint8_t openBus;
};
Snes* snes_init(void);
void snes_free(Snes* snes);
void snes_reset(Snes* snes, bool hard);
void snes_handleState(Snes* snes, StateHandler* sh);
void snes_runFrame(Snes* snes);
// used by dma, cpu
void snes_runCycles(Snes* snes, int cycles);
void snes_runCycles4(Snes* snes);
void snes_runCycles6(Snes* snes);
void snes_runCycles8(Snes* snes);
void snes_syncCycles(Snes* snes, bool start, int syncCycles);
uint8_t snes_readBBus(Snes* snes, uint8_t adr);
void snes_writeBBus(Snes* snes, uint8_t adr, uint8_t val);
uint8_t snes_read(Snes* snes, uint32_t adr);
void snes_write(Snes* snes, uint32_t adr, uint8_t val);
void snes_cpuIdle(void* mem, bool waiting);
uint8_t snes_cpuRead(void* mem, uint32_t adr);
void snes_cpuWrite(void* mem, uint32_t adr, uint8_t val);
int snes_verticalLinecount(Snes* snes);
// debugging
void snes_runCpuCycle(Snes* snes);
void snes_runSpcCycle(Snes* snes);
// snes_other.c functions:
bool snes_loadRom(Snes* snes, const uint8_t* data, int length, uint8_t* biosdata, int bioslength);
void snes_setButtonState(Snes* snes, int player, int button, int pressed, int device);
void snes_setMouseState(Snes* snes, int player, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB);
void snes_setPixels(Snes* snes, uint8_t* pixelData, int height);
void snes_setSamples(Snes* snes, int16_t* sampleData, int samplesPerFrame);
int snes_saveBattery(Snes* snes, uint8_t* data);
bool snes_loadBattery(Snes* snes, uint8_t* data, int size);
int snes_saveState(Snes* snes, uint8_t* data);
bool snes_loadState(Snes* snes, uint8_t* data, int size);
bool snes_isPal(Snes* snes);
#endif

View File

@ -0,0 +1,398 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "snes.h"
#include "cart.h"
#include "ppu.h"
#include "dsp.h"
#include "statehandler.h"
static const int stateVersion = 1;
typedef struct CartHeader {
// normal header
uint8_t headerVersion; // 1, 2, 3
char name[22]; // $ffc0-$ffd4 (max 21 bytes + \0), $ffd4=$00: header V2
uint8_t speed; // $ffd5.7-4 (always 2 or 3)
uint8_t type; // $ffd5.3-0
uint8_t coprocessor; // $ffd6.7-4
uint8_t chips; // $ffd6.3-0
uint32_t romSize; // $ffd7 (0x400 << x)
uint32_t ramSize; // $ffd8 (0x400 << x)
uint8_t region; // $ffd9 (also NTSC/PAL)
uint8_t maker; // $ffda ($33: header V3)
uint8_t version; // $ffdb
uint16_t checksumComplement; // $ffdc,$ffdd
uint16_t checksum; // $ffde,$ffdf
// v2/v3 (v2 only exCoprocessor)
char makerCode[3]; // $ffb0,$ffb1: (2 chars + \0)
char gameCode[5]; // $ffb2-$ffb5: (4 chars + \0)
uint32_t flashSize; // $ffbc (0x400 << x)
uint32_t exRamSize; // $ffbd (0x400 << x) (used for GSU?)
uint8_t specialVersion; // $ffbe
uint8_t exCoprocessor; // $ffbf (if coprocessor = $f)
// calculated stuff
int16_t score; // score for header, to see which mapping is most likely
bool pal; // if this is a rom for PAL regions instead of NTSC
uint8_t cartType; // calculated type
bool hasBattery; // battery
} CartHeader;
static void readHeader(const uint8_t* data, int length, int location, CartHeader* header);
bool snes_loadRom(Snes* snes, const uint8_t* data, int length, uint8_t* biosdata, int bioslength) {
// if smaller than smallest possible, don't load
if(length < 0x8000) {
printf("Failed to load rom: rom to small (%d bytes)\n", length);
return false;
}
// check headers
const int max_headers = 8;
CartHeader headers[max_headers];
memset(headers, 0, sizeof(headers));
for(int i = 0; i < max_headers; i++) {
headers[i].score = -50;
}
if(length >= 0x8000) readHeader(data, length, 0x7fc0, &headers[0]); // lorom
if(length >= 0x8200) readHeader(data, length, 0x81c0, &headers[1]); // lorom + header
if(length >= 0x10000) readHeader(data, length, 0xffc0, &headers[2]); // hirom
if(length >= 0x10200) readHeader(data, length, 0x101c0, &headers[3]); // hirom + header
if(length >= 0x410000) readHeader(data, length, 0x407fc0, &headers[4]); // exlorom
if(length >= 0x410200) readHeader(data, length, 0x4081c0, &headers[5]); // exlorom + header
if(length >= 0x410000) readHeader(data, length, 0x40ffc0, &headers[6]); // exhirom
if(length >= 0x410200) readHeader(data, length, 0x4101c0, &headers[7]); // exhirom + header
// see which it is, go backwards to allow picking ExHiROM over HiROM for roms with headers in both spots
int max = 0;
int used = 0;
for(int i = max_headers-1; i >= 0; i--) {
if(headers[i].score > max) {
max = headers[i].score;
used = i;
}
}
bprintf(0, _T("header used %d\n"), used);
if(used & 1) {
// odd-numbered ones are for headered roms
data += 0x200; // move pointer past header
length -= 0x200; // and subtract from size
}
// check if we can load it
if(headers[used].cartType > 4) {
bprintf(0, _T("Failed to load rom: unsupported type (%d)\n"), headers[used].cartType);
return false;
}
// expand to a power of 2
int newLength = 0x8000;
while(true) {
if(length <= newLength) {
break;
}
newLength *= 2;
}
uint8_t* newData = BurnMalloc(newLength);
memcpy(newData, data, length);
int test = 1;
while(length != newLength) {
if(length & test) {
memcpy(newData + length, newData + length - test, test);
length += test;
}
test *= 2;
}
// coprocessor check
if (headers[used].exCoprocessor == 0x10) {
headers[used].cartType = CART_CX4; // cx4
}
// -- cart specific config --
snes->ramFill = 0x00; // default, 00-fill
if (!strcmp(headers[used].name, "DEATH BRADE") || !strcmp(headers[used].name, "POWERDRIVE")) {
snes->ramFill = 0xff; // games prefer 0xff fill
}
if (!strcmp(headers[used].name, "ASHITANO JOE") || !strcmp(headers[used].name, "SUCCESS JOE")) {
snes->ramFill = 0x3f; // game prefers 0x3f fill
}
if (!strcmp(headers[used].name, "PGA TOUR GOLF")) {
snes->ramFill = 0x3f;
}
if (!strcmp(headers[used].name, "SUPER MARIO KART")) {
snes->ramFill = 0x3f; // start always select 2p if 00-fill
}
if (!strcmp(headers[used].name, "BATMAN--REVENGE JOKER")) {
headers[used].cartType = CART_LOROM; // it's detected as HiROM but actually LoROM (prototype)
}
switch (headers[used].coprocessor) {
case 2:
headers[used].cartType = CART_LOROMOBC1;
break;
case 3:
headers[used].cartType = CART_LOROMSA1;
break;
case 4:
headers[used].cartType = CART_LOROMSDD1;
}
switch (bioslength) {
case 0x2800: // DSP1-4
bprintf(0, _T("-we have dsp bios, lets go!\n"));
switch (headers[used].cartType) {
case CART_LOROM:
headers[used].cartType = CART_LOROMDSP;
break;
case CART_HIROM:
headers[used].cartType = CART_HIROMDSP;
break;
}
break;
case 0x11000: // st010/st011
bprintf(0, _T("-we have st010/st011 bios, lets go!\n"));
headers[used].cartType = CART_LOROMSETA;;
break;
}
// load it
const char* typeNames[12] = {"(none)", "LoROM", "HiROM", "ExLoROM", "ExHiROM", "CX4", "LoROM-DSP", "HiROM-DSP", "LoROM-SeTa", "LoROM-SA1", "LoROM-OBC1", "LoROM-SDD1"};
enum { CART_NONE = 0, CART_LOROM, CART_HIROM, CART_EXLOROM, CART_EXHIROM, CART_CX4, CART_LOROMDSP, CART_HIROMDSP, CART_LOROMSETA, CART_LOROMSA1, CART_LOROMOBC1, CART_LOROMSDD1 };
bprintf(0, _T("Loaded %S rom (%S)\n"), typeNames[headers[used].cartType], headers[used].pal ? "PAL" : "NTSC");
bprintf(0, _T("\"%S\"\n"), headers[used].name);
int bankSize = 0;
switch (headers[used].cartType) {
case CART_HIROM:
case CART_EXHIROM:
case CART_HIROMDSP:
bankSize = 0x10000;
break;
default:
bankSize = 0x8000;
break;
}
bprintf(
0, _T("%dK banks: %d, ramsize: %d%S, coprocessor: %x\n"),
bankSize / 1024, newLength / bankSize, headers[used].chips > 0 ? headers[used].ramSize : 0, (headers[used].hasBattery) ? " (battery-backed)" : "", headers[used].exCoprocessor
);
cart_load(
snes->cart, headers[used].cartType,
newData, newLength, biosdata, bioslength, headers[used].chips > 0 ? headers[used].ramSize : 0,
headers[used].hasBattery
);
snes_reset(snes, true); // reset after loading
snes->palTiming = headers[used].pal; // set region
BurnFree(newData);
return true;
}
bool snes_isPal(Snes* snes) {
return snes->palTiming;
}
void snes_setButtonState(Snes* snes, int player, int button, int pressed, int device) {
// set key in controller
Input* input = (player == 1) ? snes->input1 : snes->input2;
uint32_t *c_state = &input->currentState;
input_setType(input, device);
if(pressed) {
*c_state |= 1 << button;
} else {
*c_state &= ~(1 << button);
}
if (device == DEVICE_SUPERSCOPE) {
static uint8_t button8_9[2] = { 0, 0 };
switch (button) {
case 8:
case 9:
button8_9[button & 1] = pressed;
break;
case 11: // last button
*c_state |= 0xff00;
if (*c_state & SCOPE_FIRE || *c_state & SCOPE_CURSOR) {
ppu_latchScope(button8_9[0], button8_9[1]);
}
break;
}
}
}
void snes_setMouseState(Snes* snes, int player, int16_t x, int16_t y, uint8_t buttonA, uint8_t buttonB) {
Input* input = (player == 1) ? snes->input1 : snes->input2;
input_setType(input, DEVICE_MOUSE);
input_setMouse(input, x, y, buttonA, buttonB);
}
void snes_setPixels(Snes* snes, uint8_t* pixelData, int height) {
// size is 4 (rgba) * 512 (w) * 480 (h)
ppu_putPixels(pixelData, height);
}
void snes_setSamples(Snes* snes, int16_t* sampleData, int samplesPerFrame) {
// size is 2 (int16) * 2 (stereo) * samplesPerFrame
// sets samples in the sampleData
dsp_getSamples(snes->apu->dsp, sampleData, samplesPerFrame);
}
int snes_saveBattery(Snes* snes, uint8_t* data) {
int size = 0;
cart_handleBattery(snes->cart, true, data, &size);
return size;
}
bool snes_loadBattery(Snes* snes, uint8_t* data, int size) {
return cart_handleBattery(snes->cart, false, data, &size);
}
int snes_saveState(Snes* snes, uint8_t* data) {
StateHandler* sh = sh_init(true, NULL, 0);
uint32_t id = 0x4653534c; // 'LSSF' LakeSnes State File
uint32_t version = stateVersion;
sh_handleInts(sh, &id, &version, &version, NULL); // second version to be overridden by length
cart_handleTypeState(snes->cart, sh);
// save data
snes_handleState(snes, sh);
// store
sh_placeInt(sh, 8, sh->offset);
if(data != NULL) memcpy(data, sh->data, sh->offset);
int size = sh->offset;
sh_free(sh);
return size;
}
bool snes_loadState(Snes* snes, uint8_t* data, int size) {
StateHandler* sh = sh_init(false, data, size);
uint32_t id = 0, version = 0, length = 0;
sh_handleInts(sh, &id, &version, &length, NULL);
bool cartMatch = cart_handleTypeState(snes->cart, sh);
if(id != 0x4653534c || version != stateVersion || length != size || !cartMatch) {
sh_free(sh);
return false;
}
// load data
snes_handleState(snes, sh);
// finish
sh_free(sh);
return true;
}
static void readHeader(const uint8_t* data, int length, int location, CartHeader* header) {
// read name, TODO: non-ASCII names?
for(int i = 0; i < 21; i++) {
uint8_t ch = data[location + i];
if(ch >= 0x20 && ch < 0x7f) {
header->name[i] = ch;
} else {
header->name[i] = '.';
}
}
header->name[21] = 0;
// clean name (strip end space)
int slen = strlen(header->name);
while (slen > 0 && header->name[slen-1] == ' ') {
header->name[slen-1] = '\0';
slen--;
}
// read rest
header->speed = data[location + 0x15] >> 4;
header->type = data[location + 0x15] & 0xf;
header->coprocessor = data[location + 0x16] >> 4;
header->chips = data[location + 0x16] & 0xf;
bprintf(0, _T("cart type, copro, chips: %x, %x, %x\n"), header->type, header->coprocessor, header->chips);
header->hasBattery = (header->chips == 0x02 || header->chips == 0x05 || header->chips == 0x06);
header->romSize = 0x400 << data[location + 0x17];
header->ramSize = 0x400 << data[location + 0x18];
header->region = data[location + 0x19];
header->maker = data[location + 0x1a];
header->version = data[location + 0x1b];
header->checksumComplement = (data[location + 0x1d] << 8) + data[location + 0x1c];
header->checksum = (data[location + 0x1f] << 8) + data[location + 0x1e];
// read v3 and/or v2
header->headerVersion = 1;
if(header->maker == 0x33) {
header->headerVersion = 3;
// maker code
for(int i = 0; i < 2; i++) {
uint8_t ch = data[location - 0x10 + i];
if(ch >= 0x20 && ch < 0x7f) {
header->makerCode[i] = ch;
} else {
header->makerCode[i] = '.';
}
}
header->makerCode[2] = 0;
// game code
for(int i = 0; i < 4; i++) {
uint8_t ch = data[location - 0xe + i];
if(ch >= 0x20 && ch < 0x7f) {
header->gameCode[i] = ch;
} else {
header->gameCode[i] = '.';
}
}
header->gameCode[4] = 0;
header->flashSize = 0x400 << data[location - 4];
header->exRamSize = 0x400 << data[location - 3];
header->specialVersion = data[location - 2];
header->exCoprocessor = data[location - 1];
} else if(data[location + 0x14] == 0) {
header->headerVersion = 2;
header->exCoprocessor = data[location - 1];
}
//bprintf(0, _T("ramsize %x exramsize %x\n"),header->ramSize, header->exRamSize);
//bprintf(0, _T("exCoprocessor %x\n"), header->exCoprocessor);
// get region
header->pal = (header->region >= 0x2 && header->region <= 0xc) || header->region == 0x11;
header->cartType = location < 0x9000 ? CART_LOROM : CART_HIROM;
if(location > 0x400000) {
// Ex...
if (location == 0x407fc0 || location == 0x4081c0) {
header->cartType = CART_EXLOROM; // ExLoROM
} else {
header->cartType = CART_EXHIROM; // ExHiROM
}
}
// get score
// TODO: check name, maker/game-codes (if V3) for ASCII, more vectors,
// more first opcode, rom-sizes (matches?), type (matches header location?)
int score = 0;
score += (header->speed == 2 || header->speed == 3) ? 5 : -4;
score += (header->type <= 3 || header->type == 5) ? 5 : -2;
score += (header->coprocessor <= 5 || header->coprocessor >= 0xe) ? 5 : -2;
score += (header->chips <= 6 || header->chips == 9 || header->chips == 0xa) ? 5 : -2;
score += (header->region <= 0x14) ? 5 : -2;
score += (header->checksum + header->checksumComplement == 0xffff) ? 8 : -6;
uint16_t resetVector = data[location + 0x3c] | (data[location + 0x3d] << 8);
score += (resetVector >= 0x8000) ? 8 : -20;
// check first opcode after reset
int opcodeLoc = location + 0x40 - 0x8000 + (resetVector & 0x7fff);
uint8_t opcode = 0xff;
if(opcodeLoc < length) {
opcode = data[opcodeLoc];
} else {
score -= 14;
}
if(opcode == 0x78 || opcode == 0x18) {
// sei, clc (for clc:xce)
score += 6;
}
if(opcode == 0x4c || opcode == 0x5c || opcode == 0x9c) {
// jmp abs, jml abl, stz abs
score += 3;
}
if(opcode == 0x00 || opcode == 0xff || opcode == 0xdb) {
// brk, sbc alx, stp
score -= 6;
}
header->score = score;
}

1937
src/burn/drv/snes/spc.cpp Normal file

File diff suppressed because it is too large Load Diff

59
src/burn/drv/snes/spc.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef SPC_H
#define SPC_H
#include <stdint.h>
#include <stdbool.h>
#include "burnint.h"
#include "statehandler.h"
typedef uint8_t (*SpcReadHandler)(void* mem, uint16_t adr);
typedef void (*SpcWriteHandler)(void* mem, uint16_t adr, uint8_t val);
typedef void (*SpcIdleHandler)(void* mem, bool waiting);
typedef struct Spc Spc;
struct Spc {
// reference to memory handler, pointers to read/write/idle handlers
void* mem;
SpcReadHandler read;
SpcWriteHandler write;
SpcIdleHandler idle;
// registers
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t sp;
uint16_t pc;
// flags
bool c;
bool z;
bool v;
bool n;
bool i;
bool h;
bool p;
bool b;
// stopping
bool stopped;
// reset
bool resetWanted;
// single-cycle
uint8_t opcode;
uint32_t step;
uint32_t bstep;
uint16_t adr;
uint16_t adr1;
uint8_t dat;
uint16_t dat16;
uint8_t param;
};
Spc* spc_init(void* mem, SpcReadHandler read, SpcWriteHandler write, SpcIdleHandler idle);
void spc_free(Spc* spc);
void spc_reset(Spc* spc, bool hard);
void spc_handleState(Spc* spc, StateHandler* sh);
void spc_runOpcode(Spc* spc);
#endif

View File

@ -0,0 +1,259 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "statehandler.h"
static void sh_writeByte(StateHandler* sh, uint8_t val);
static uint8_t sh_readByte(StateHandler* sh);
StateHandler* sh_init(bool saving, const uint8_t* data, int size) {
StateHandler* sh = (StateHandler*)BurnMalloc(sizeof(StateHandler));
sh->saving = saving;
sh->offset = 0;
if(!saving) {
sh->data = BurnMalloc(size);
memcpy(sh->data, data, size);
sh->allocSize = size;
} else {
sh->data = BurnMalloc(512 * 1024);
sh->allocSize = 512 * 1024;
}
return sh;
}
void sh_free(StateHandler* sh) {
BurnFree(sh->data);
BurnFree(sh);
}
static void sh_writeByte(StateHandler* sh, uint8_t val) {
if(sh->offset >= sh->allocSize) {
// realloc
sh->data = BurnRealloc(sh->data, sh->allocSize * 2);
sh->allocSize *= 2;
}
sh->data[sh->offset++] = val;
}
static uint8_t sh_readByte(StateHandler* sh) {
if(sh->offset >= sh->allocSize) {
bprintf(0, _T("offset >= allocSize! %x %x\n"), sh->offset, sh->allocSize);
// reading above data (should never happen)
return 0;
}
return sh->data[sh->offset++];
}
void sh_handleBools(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
bool* v = va_arg(args, bool*);
if(v == NULL) break;
if(sh->saving) {
sh_writeByte(sh, *v ? 1 : 0);
} else {
*v = sh_readByte(sh) > 0 ? true : false;
}
}
va_end(args);
}
void sh_handleBytes(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
uint8_t* v = va_arg(args, uint8_t*);
if(v == NULL) break;
if(sh->saving) {
sh_writeByte(sh, *v);
} else {
*v = sh_readByte(sh);
}
}
va_end(args);
}
void sh_handleBytesS(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
int8_t* v = va_arg(args, int8_t*);
if(v == NULL) break;
if(sh->saving) {
sh_writeByte(sh, (uint8_t) *v);
} else {
*v = (int8_t) sh_readByte(sh);
}
}
va_end(args);
}
void sh_handleWords(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
uint16_t* v = va_arg(args, uint16_t*);
if(v == NULL) break;
if(sh->saving) {
uint16_t val = *v;
for(int i = 0; i < 16; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint16_t val = 0;
for(int i = 0; i < 16; i += 8) val |= sh_readByte(sh) << i;
*v = val;
}
}
va_end(args);
}
void sh_handleWordsS(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
int16_t* v = va_arg(args, int16_t*);
if(v == NULL) break;
if(sh->saving) {
uint16_t val = (uint16_t) *v;
for(int i = 0; i < 16; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint16_t val = 0;
for(int i = 0; i < 16; i += 8) val |= sh_readByte(sh) << i;
*v = (int16_t) val;
}
}
va_end(args);
}
void sh_handleLongLongs(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
uint64_t* v = va_arg(args, uint64_t*);
if(v == NULL) break;
if(sh->saving) {
uint64_t val = *v;
for(int i = 0; i < 64; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint64_t val = 0;
for(int i = 0; i < 64; i += 8) val |= (uint64_t)sh_readByte(sh) << i;
*v = val;
}
}
va_end(args);
}
void sh_handleInts(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
uint32_t* v = va_arg(args, uint32_t*);
if(v == NULL) break;
if(sh->saving) {
uint32_t val = *v;
for(int i = 0; i < 32; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint32_t val = 0;
for(int i = 0; i < 32; i += 8) val |= sh_readByte(sh) << i;
*v = val;
}
}
va_end(args);
}
void sh_handleIntsS(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
int32_t* v = va_arg(args, int32_t*);
if(v == NULL) break;
if(sh->saving) {
uint32_t val = (uint32_t) *v;
for(int i = 0; i < 32; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint32_t val = 0;
for(int i = 0; i < 32; i += 8) val |= sh_readByte(sh) << i;
*v = (int32_t) val;
}
}
va_end(args);
}
void sh_handleFloats(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
float* v = va_arg(args, float*);
if(v == NULL) break;
if(sh->saving) {
uint8_t valData[4] = {};
*((float*) valData) = *v;
uint32_t val = *((uint32_t*) valData);
for(int i = 0; i < 32; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint32_t val = 0;
for(int i = 0; i < 32; i += 8) val |= sh_readByte(sh) << i;
uint8_t valData[4] = {};
*((uint32_t*) valData) = val;
*v = *((float*) valData);
}
}
va_end(args);
}
void sh_handleDoubles(StateHandler* sh, ...) {
va_list args;
va_start(args, sh);
while(true) {
double* v = va_arg(args, double*);
if(v == NULL) break;
if(sh->saving) {
uint8_t valData[8] = {};
*((double*) valData) = *v;
uint64_t val = *((uint64_t*) valData);
for(int i = 0; i < 64; i += 8) sh_writeByte(sh, (val >> i) & 0xff);
} else {
uint64_t val = 0;
for(int i = 0; i < 64; i += 8) val |= (uint64_t)sh_readByte(sh) << i;
uint8_t valData[8] = {};
*((uint64_t*) valData) = val;
*v = *((double*) valData);
}
}
va_end(args);
}
void sh_handleByteArray(StateHandler* sh, uint8_t* data, int size) {
for(int i = 0; i < size; i++) {
if(sh->saving) {
sh_writeByte(sh, data[i]);
} else {
data[i] = sh_readByte(sh);
}
}
}
void sh_handleWordArray(StateHandler* sh, uint16_t* data, int size) {
for(int i = 0; i < size; i++) {
if(sh->saving) {
sh_writeByte(sh, data[i] & 0xff);
sh_writeByte(sh, (data[i] >> 8) & 0xff);
} else {
data[i] = sh_readByte(sh);
data[i] |= sh_readByte(sh) << 8;
}
}
}
void sh_placeInt(StateHandler* sh, int location, uint32_t value) {
while(sh->offset < location + 4) sh_writeByte(sh, 0);
sh->data[location] = value & 0xff;
sh->data[location + 1] = (value >> 8) & 0xff;
sh->data[location + 2] = (value >> 16) & 0xff;
sh->data[location + 3] = (value >> 24) & 0xff;
}

View File

@ -0,0 +1,33 @@
#ifndef STATEHANDLER_H
#define STATEHANDLER_H
#include <stdint.h>
#include <stdbool.h>
#include "burnint.h"
typedef struct StateHandler {
bool saving;
int offset;
uint8_t* data;
int allocSize;
} StateHandler;
StateHandler* sh_init(bool saving, const uint8_t* data, int size);
void sh_free(StateHandler* sh);
void sh_handleBools(StateHandler* sh, ...);
void sh_handleBytes(StateHandler* sh, ...);
void sh_handleBytesS(StateHandler* sh, ...);
void sh_handleWords(StateHandler* sh, ...);
void sh_handleWordsS(StateHandler* sh, ...);
void sh_handleLongLongs(StateHandler* sh, ...);
void sh_handleInts(StateHandler* sh, ...);
void sh_handleIntsS(StateHandler* sh, ...);
void sh_handleFloats(StateHandler* sh, ...);
void sh_handleDoubles(StateHandler* sh, ...);
void sh_handleByteArray(StateHandler* sh, uint8_t* data, int size);
void sh_handleWordArray(StateHandler* sh, uint16_t* data, int size);
void sh_placeInt(StateHandler* sh, int location, uint32_t value);
#endif

View File

@ -134,6 +134,8 @@ struct tIniStruct {
extern tIniStruct gamehw_cfg[];
UINT32 GameInputGetHWFlag();
void GetHistoryDatHardwareToken(char *to_string);
// inp_interface.cpp
@ -223,8 +225,9 @@ void ComputeGammaLUT();
#define DAT_NEOGEO_ONLY 11
#define DAT_NES_ONLY 12
#define DAT_FDS_ONLY 13
#define DAT_NGP_ONLY 14
#define DAT_CHANNELF_ONLY 15
#define DAT_SNES_ONLY 14
#define DAT_NGP_ONLY 15
#define DAT_CHANNELF_ONLY 16
INT32 write_datfile(INT32 bType, FILE* fDat);
INT32 create_datfile(TCHAR* szFilename, INT32 bType);

View File

@ -123,6 +123,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
if (bType == DAT_NEOGEO_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - Neo Geo Games</name>\n");
if (bType == DAT_NES_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - NES Games</name>\n");
if (bType == DAT_FDS_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - FDS Games</name>\n");
if (bType == DAT_SNES_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - SNES Games</name>\n");
if (bType == DAT_NGP_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - Neo Geo Pocket Games</name>\n");
if (bType == DAT_CHANNELF_ONLY) fprintf(fDat, "\t\t<name>" APP_TITLE " - Fairchild Channel F Games</name>\n");
@ -140,6 +141,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
if (bType == DAT_NEOGEO_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" Neo Geo Games</description>\n"), szAppBurnVer);
if (bType == DAT_NES_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" NES Games</description>\n"), szAppBurnVer);
if (bType == DAT_FDS_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" FDS Games</description>\n"), szAppBurnVer);
if (bType == DAT_SNES_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" SNES Games</description>\n"), szAppBurnVer);
if (bType == DAT_NGP_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" Neo Geo Pocket Games</description>\n"), szAppBurnVer);
if (bType == DAT_CHANNELF_ONLY) _ftprintf(fDat, _T("\t\t<description>") _T(APP_TITLE) _T(" v%s") _T(" Fairchild Channel F Games</description>\n"), szAppBurnVer);
fprintf(fDat, "\t\t<category>Standard DatFile</category>\n");
@ -182,6 +184,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SPECTRUM)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NGP)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_CHANNELF)
) && (bType == DAT_ARCADE_ONLY)) {
@ -236,6 +239,10 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
continue;
}
if (((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) != HARDWARE_SNES) && (bType == DAT_SNES_ONLY)) {
continue;
}
if (((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) != HARDWARE_SNK_NGP) && (bType == DAT_NGP_ONLY)) {
continue;
}
@ -334,6 +341,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
remove_driver_leader(HARDWARE_SPECTRUM, 5, 1)
remove_driver_leader(HARDWARE_NES, 4, 1)
remove_driver_leader(HARDWARE_FDS, 4, 1)
remove_driver_leader(HARDWARE_SNES, 5, 1)
remove_driver_leader(HARDWARE_SNK_NGP, 4, 1)
remove_driver_leader(HARDWARE_CHANNELF, 4, 1)
@ -640,6 +648,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SPECTRUM)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NGP)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_CHANNELF)
) && (bType == DAT_ARCADE_ONLY)) {
@ -694,6 +703,10 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
continue;
}
if (((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) != HARDWARE_SNES) && (bType == DAT_SNES_ONLY)) {
continue;
}
if (((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) != HARDWARE_SNK_NGP) && (bType == DAT_NGP_ONLY)) {
continue;
}
@ -725,6 +738,7 @@ INT32 write_datfile(INT32 bType, FILE* fDat)
remove_driver_leader(HARDWARE_SPECTRUM, 5, 0)
remove_driver_leader(HARDWARE_NES, 4, 0)
remove_driver_leader(HARDWARE_FDS, 4, 0)
remove_driver_leader(HARDWARE_SNES, 5, 0)
remove_driver_leader(HARDWARE_SNK_NGP, 4, 0)
remove_driver_leader(HARDWARE_CHANNELF, 4, 0)

View File

@ -2165,6 +2165,8 @@ tIniStruct gamehw_cfg[] = {
{_T("Neo Geo Pocket C hardware"), _T("config/presets/ngp.ini"), { HARDWARE_SNK_NGPC, 0 }, "\t\t\t<item list=\"ngpc\" name=\"" },
{_T("NES hardware"), _T("config/presets/nes.ini"), { HARDWARE_NES, 0 }, "\t\t\t<item list=\"nes\" name=\"" },
{_T("FDS hardware"), _T("config/presets/fds.ini"), { HARDWARE_FDS, 0 }, "\t\t\t<item list=\"famicom_flop\" name=\"" },
{_T("SNES hardware"), _T("config/presets/snes.ini"), { HARDWARE_SNES, 0 }, "\t\t\t<item list=\"snes\" name=\"" },
{_T("SNES w/Scope hardware"), _T("config/presets/snes_scope.ini"),{ HARDWARE_SNES_ZAPPER, 0 }, "\t\t\t<item list=\"snes\" name=\"" },
{_T("PGM hardware"), _T("config/presets/pgm.ini"), { HARDWARE_IGS_PGM, 0 }, "\t\t\t<system name=\"" },
{_T("MegaDrive hardware"), _T("config/presets/megadrive.ini"), { HARDWARE_SEGA_MEGADRIVE, 0 }, "\t\t\t<item list=\"megadriv\" name=\"" },
{_T("PCE/SGX hardware"), _T("config/presets/pce.ini"), { HARDWARE_PCENGINE_PCENGINE, 0 }, "\t\t\t<item list=\"pce\" name=\"" },
@ -2181,7 +2183,7 @@ tIniStruct gamehw_cfg[] = {
void GetHistoryDatHardwareToken(char *to_string)
{
INT32 nHardwareFlag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);
UINT32 nHardwareFlag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);
// See if nHardwareFlag belongs to any systems in gamehw_config
for (INT32 i = 0; gamehw_cfg[i].ini[0] != '\0'; i++) {
@ -2198,6 +2200,18 @@ void GetHistoryDatHardwareToken(char *to_string)
strcpy(to_string, "\t\t\t<system name=\"");
}
UINT32 GameInputGetHWFlag()
{
UINT32 nHardwareFlag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);
if (nHardwareFlag == HARDWARE_SNES) {
// for this(these?) systems, get the unmasked HW flag
nHardwareFlag = BurnDrvGetHardwareCode();
}
return nHardwareFlag;
}
INT32 ConfigGameLoadHardwareDefaults()
{
#if defined(BUILD_SDL2) && !defined(SDL_WINDOWS)
@ -2209,7 +2223,7 @@ INT32 ConfigGameLoadHardwareDefaults()
#endif
INT32 nApplyHardwareDefaults = 0;
INT32 nHardwareFlag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);
UINT32 nHardwareFlag = GameInputGetHWFlag();
// See if nHardwareFlag belongs to any systems in gamehw_config
for (INT32 i = 0; gamehw_cfg[i].ini[0] != '\0'; i++) {

View File

@ -1168,6 +1168,7 @@ BEGIN
MENUITEM "Generate dat (ZX Spectrum Games only)...",MENU_CLRMAME_PRO_XML_SPECTRUM_ONLY
MENUITEM "Generate dat (NES Games only)...", MENU_CLRMAME_PRO_XML_NES_ONLY
MENUITEM "Generate dat (FDS Games only)...", MENU_CLRMAME_PRO_XML_FDS_ONLY
MENUITEM "Generate dat (SNES Games only)...", MENU_CLRMAME_PRO_XML_SNES_ONLY
MENUITEM "Generate dat (Neo Geo Pocket Games only)...", MENU_CLRMAME_PRO_XML_NGP_ONLY
MENUITEM "Generate dat (Fairchild Channel F Games only)...", MENU_CLRMAME_PRO_XML_CHANNELF_ONLY
MENUITEM SEPARATOR

View File

@ -5,7 +5,6 @@
int bDrvOkay = 0; // 1 if the Driver has been initted okay, and it's okay to use the BurnDrv functions
TCHAR szAppRomPaths[DIRS_MAX][MAX_PATH] = {
{ _T("") },
{ _T("") },
{ _T("") },
{ _T("roms/romdata/") },
@ -13,6 +12,7 @@ TCHAR szAppRomPaths[DIRS_MAX][MAX_PATH] = {
{ _T("roms/ngp/") },
{ _T("roms/nes/") },
{ _T("roms/fds/") },
{ _T("roms/snes/") },
{ _T("roms/spectrum/") },
{ _T("roms/msx/") },
{ _T("roms/sms/") },

View File

@ -658,7 +658,7 @@ static void SaveHardwarePreset()
TCHAR *szFileName = _T("config\\presets\\preset.ini");
TCHAR *szHardwareString = _T("Generic hardware");
int nHardwareFlag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);
int nHardwareFlag = GameInputGetHWFlag();
// See if nHardwareFlag belongs to any systems (nes.ini, neogeo.ini, etc) in gamehw_config (see: burner/gami.cpp)
for (INT32 i = 0; gamehw_cfg[i].ini[0] != '\0'; i++) {

View File

@ -976,6 +976,11 @@ int ProcessCmdLine()
return 1;
}
if (_tcscmp(szName, _T("-listinfosnesonly")) == 0) {
write_datfile(DAT_SNES_ONLY, stdout);
return 1;
}
if (_tcscmp(szName, _T("-listinfongponly")) == 0) {
write_datfile(DAT_NGP_ONLY, stdout);
return 1;
@ -1251,6 +1256,7 @@ static void CreateSupportFolders()
{_T("roms/spectrum/")},
{_T("roms/nes/")},
{_T("roms/fds/")},
{_T("roms/snes/")},
{_T("roms/ngp/")},
{_T("roms/channelf/")},
{_T("roms/romdata/")},

View File

@ -137,6 +137,8 @@ struct NGCDGAME games[] =
{ _T("flappychick") , _T("Flappy Chicken") , _T("2023") , _T("Blastar") , 0x2022 }, //
{ _T("hypernoid") , _T("Hypernoid") , _T("2022") , _T("NeoHomeBrew.com") , 0x0600 }, //
{ _T("timesup") , _T("Time's Up") , _T("2012") , _T("NGF DEV. INC.") , 0x0276 }, //
{ _T("pow") , _T("P.O.W. - Prisoners of War") , _T("2024") , _T("SNK (iq_132 conversion)") , 0x1324 }, //
{ _T("karnov") , _T("Karnov") , _T("2024") , _T("Data East (iq_132 conversion)") , 0x0283 }, //
};
static char szVolumeID[64];

View File

@ -735,8 +735,9 @@
#define MENU_AUTOSCANGAMELIST 10735
#define MENU_CLRMAME_PRO_XML_NES_ONLY 10736
#define MENU_CLRMAME_PRO_XML_FDS_ONLY 10737
#define MENU_CLRMAME_PRO_XML_NGP_ONLY 10738
#define MENU_CLRMAME_PRO_XML_CHANNELF_ONLY 10739
#define MENU_CLRMAME_PRO_XML_SNES_ONLY 10738
#define MENU_CLRMAME_PRO_XML_NGP_ONLY 10739
#define MENU_CLRMAME_PRO_XML_CHANNELF_ONLY 10740
#define MENU_BASIC_NORMAL 11001
#define MENU_BASIC_SCAN 11002

View File

@ -211,8 +211,8 @@
#define IDS_SEL_SEGA_GRP (IDS_STRING + 644)
#define IDS_SEL_NES (IDS_STRING + 646)
#define IDS_SEL_FDS (IDS_STRING + 648)
#define IDS_SEL_NGP (IDS_STRING + 556)
#define IDS_SEL_CHANNELF (IDS_STRING + 558)
#define IDS_SEL_NGP (IDS_STRING + 556) // maybe we should renumber these?
#define IDS_SEL_CHANNELF (IDS_STRING + 558) // ""
#define IDS_GENRE (IDS_STRING + 650)
#define IDS_GENRE_HORSHOOT (IDS_STRING + 652)

View File

@ -385,6 +385,7 @@ int CreateDatfileWindows(int bType)
if (bType == DAT_SPECTRUM_ONLY) _sntprintf(szConsoleString, 64, _T(", ZX Spectrum Games only"));
if (bType == DAT_NES_ONLY) _sntprintf(szConsoleString, 64, _T(", NES Games only"));
if (bType == DAT_FDS_ONLY) _sntprintf(szConsoleString, 64, _T(", FDS Games only"));
if (bType == DAT_SNES_ONLY) _sntprintf(szConsoleString, 64, _T(", SNES Games only"));
if (bType == DAT_NGP_ONLY) _sntprintf(szConsoleString, 64, _T(", NeoGeo Pocket Games only"));
if (bType == DAT_CHANNELF_ONLY) _sntprintf(szConsoleString, 64, _T(", Fairchild Channel F Games only"));
@ -502,6 +503,9 @@ int CreateAllDatfilesWindows()
_sntprintf(szFilename, MAX_PATH, _T("%s") _T(APP_TITLE) _T(" v%.20s (%s%s).dat"), buffer, szAppBurnVer, szProgramString, _T(", FDS Games only"));
create_datfile(szFilename, DAT_FDS_ONLY);
_sntprintf(szFilename, MAX_PATH, _T("%s") _T(APP_TITLE) _T(" v%.20s (%s%s).dat"), buffer, szAppBurnVer, szProgramString, _T(", SNES Games only"));
create_datfile(szFilename, DAT_SNES_ONLY);
_sntprintf(szFilename, MAX_PATH, _T("%s") _T(APP_TITLE) _T(" v%.20s (%s%s).dat"), buffer, szAppBurnVer, szProgramString, _T(", Neo Geo Pocket Games only"));
create_datfile(szFilename, DAT_NGP_ONLY);
@ -2675,6 +2679,12 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify)
}
break;
case MENU_CLRMAME_PRO_XML_SNES_ONLY:
if (UseDialogs()) {
CreateDatfileWindows(DAT_SNES_ONLY);
}
break;
case MENU_CLRMAME_PRO_XML_NGP_ONLY:
if (UseDialogs()) {
CreateDatfileWindows(DAT_NGP_ONLY);
@ -3675,10 +3685,10 @@ int ScrnSize()
} else {
if (nWindowSize) {
nMaxSize = nWindowSize;
if (bDrvOkay && nWindowSize == 2 && nBmapWidth >= 400 && nBmapHeight >= 400) {
if (bDrvOkay && nWindowSize > 1 && nBmapWidth >= 400 && nBmapHeight >= 400) {
// For Popeye, Hole Land and Syvalion, MCR..etc. when running Windowed: Double Size
bprintf(PRINT_NORMAL, _T(" * Game is double-sized to begin with.\n"));
nMaxSize = 1;
nMaxSize--;
}
} else {
if (nBmapWidth < nBmapHeight) {

View File

@ -128,6 +128,7 @@ HTREEITEM hFilterPce = NULL;
HTREEITEM hFilterMsx = NULL;
HTREEITEM hFilterNes = NULL;
HTREEITEM hFilterFds = NULL;
HTREEITEM hFilterSnes = NULL;
HTREEITEM hFilterNgp = NULL;
HTREEITEM hFilterChannelF = NULL;
HTREEITEM hFilterSms = NULL;
@ -274,17 +275,17 @@ static UINT64 MASKMIDWAY = 1 << MidwayValue;
static UINT64 NesValue = HARDWARE_PREFIX_NES >> 24;
static UINT64 MASKNES = 1 << NesValue;
// this is where things start going above the 32bit-zone. *solved w/64bit UINT?*
static UINT64 FdsValue = (UINT64)HARDWARE_PREFIX_FDS >> 24;
static UINT64 MASKFDS = (UINT64)1 << FdsValue; // 1 << 0x1f - needs casting or.. bonkers!
// this is where things start going above the 32bit-zone. *solved w/64bit UINT?*
static UINT64 SnesValue = (UINT64)HARDWARE_PREFIX_SNES >> 24;
static UINT64 MASKSNES = (UINT64)1 << SnesValue;
static UINT64 NgpValue = (UINT64)HARDWARE_PREFIX_NGP >> 24;
static UINT64 MASKNGP = (UINT64)1 << NgpValue;
static UINT64 ChannelFValue = (UINT64)HARDWARE_PREFIX_CHANNELF >> 24;
static UINT64 MASKCHANNELF = (UINT64)1 << ChannelFValue;
static UINT64 MASKALL = ((UINT64)MASKCAPMISC | MASKCAVE | MASKCPS | MASKCPS2 | MASKCPS3 | MASKDATAEAST | MASKGALAXIAN | MASKIREM | MASKKANEKO | MASKKONAMI | MASKNEOGEO | MASKPACMAN | MASKPGM | MASKPSIKYO | MASKSEGA | MASKSETA | MASKTAITO | MASKTECHNOS | MASKTOAPLAN | MASKMISCPRE90S | MASKMISCPOST90S | MASKMEGADRIVE | MASKPCENGINE | MASKSMS | MASKGG | MASKSG1000 | MASKCOLECO | MASKMSX | MASKSPECTRUM | MASKMIDWAY | MASKNES | MASKFDS | MASKNGP | MASKCHANNELF );
static UINT64 MASKALL = ((UINT64)MASKCAPMISC | MASKCAVE | MASKCPS | MASKCPS2 | MASKCPS3 | MASKDATAEAST | MASKGALAXIAN | MASKIREM | MASKKANEKO | MASKKONAMI | MASKNEOGEO | MASKPACMAN | MASKPGM | MASKPSIKYO | MASKSEGA | MASKSETA | MASKTAITO | MASKTECHNOS | MASKTOAPLAN | MASKMISCPRE90S | MASKMISCPOST90S | MASKMEGADRIVE | MASKPCENGINE | MASKSMS | MASKGG | MASKSG1000 | MASKCOLECO | MASKMSX | MASKSPECTRUM | MASKMIDWAY | MASKNES | MASKFDS | MASKSNES | MASKNGP | MASKCHANNELF );
#define UNAVAILABLE (1 << 27)
#define AVAILABLE (1 << 28)
@ -1479,6 +1480,7 @@ static void CreateFilters()
_TVCreateFiltersA(hHardware , IDS_SEL_SPECTRUM , hFilterSpectrum , nLoadMenuShowX & MASKSPECTRUM );
_TVCreateFiltersA(hHardware , IDS_SEL_NES , hFilterNes , nLoadMenuShowX & MASKNES );
_TVCreateFiltersA(hHardware , IDS_SEL_FDS , hFilterFds , nLoadMenuShowX & MASKFDS );
_TVCreateFiltersA(hHardware , IDS_SEL_SNES , hFilterSnes , nLoadMenuShowX & MASKSNES );
_TVCreateFiltersA(hHardware , IDS_SEL_NGP , hFilterNgp , nLoadMenuShowX & MASKNGP );
_TVCreateFiltersA(hHardware , IDS_SEL_CHANNELF , hFilterChannelF , nLoadMenuShowX & MASKCHANNELF );
_TVCreateFiltersA(hHardware , IDS_SEL_MISCPRE90S , hFilterMiscPre90s , nLoadMenuShowX & MASKMISCPRE90S );
@ -1496,7 +1498,7 @@ static void CreateFilters()
TreeView_SelectSetFirstVisible(hFilterList, hFavorites);
}
#define ICON_MAXCONSOLES 12
#define ICON_MAXCONSOLES 13
enum {
ICON_MEGADRIVE = 0,
@ -1509,8 +1511,9 @@ enum {
ICON_SPECTRUM = 7,
ICON_NES = 8,
ICON_FDS = 9,
ICON_NGP = 10,
ICON_CHANNELF = 11
ICON_SNES = 10,
ICON_NGP = 11,
ICON_CHANNELF = 12
};
static HICON hConsDrvIcon[ICON_MAXCONSOLES];
@ -1565,6 +1568,9 @@ void LoadDrvIcons()
_stprintf(szIcon, _T("%sfds_icon.ico"), szAppIconsPath);
hConsDrvIcon[ICON_FDS] = (HICON)LoadImage(hAppInst, szIcon, IMAGE_ICON, nIconsSizeXY, nIconsSizeXY, LR_LOADFROMFILE);
_stprintf(szIcon, _T("%ssnes_icon.ico"), szAppIconsPath);
hConsDrvIcon[ICON_SNES] = (HICON)LoadImage(hAppInst, szIcon, IMAGE_ICON, nIconsSizeXY, nIconsSizeXY, LR_LOADFROMFILE);
_stprintf(szIcon, _T("%sngp_icon.ico"), szAppIconsPath);
hConsDrvIcon[ICON_NGP] = (HICON)LoadImage(hAppInst, szIcon, IMAGE_ICON, nIconsSizeXY, nIconsSizeXY, LR_LOADFROMFILE);
@ -1590,6 +1596,7 @@ void LoadDrvIcons()
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SPECTRUM)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NGP)
|| ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_CHANNELF)
)) {
@ -1648,6 +1655,11 @@ void LoadDrvIcons()
continue;
}
if ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES) {
hDrvIcon[nDrvIndex] = hConsDrvIcon[ICON_SNES];
continue;
}
if ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NGP) {
hDrvIcon[nDrvIndex] = hConsDrvIcon[ICON_NGP];
continue;
@ -1855,6 +1867,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
_TreeView_SetCheckState(hFilterList, hFilterSpectrum, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterNes, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterFds, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterSnes, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterNgp, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterChannelF, FALSE);
_TreeView_SetCheckState(hFilterList, hFilterMidway, FALSE);
@ -1896,6 +1909,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
_TreeView_SetCheckState(hFilterList, hFilterSpectrum, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterNes, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterFds, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterSnes, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterNgp, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterChannelF, TRUE);
_TreeView_SetCheckState(hFilterList, hFilterMidway, TRUE);
@ -2141,6 +2155,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
if (hItemChanged == hFilterSpectrum) _ToggleGameListing(nLoadMenuShowX, MASKSPECTRUM);
if (hItemChanged == hFilterNes) _ToggleGameListing(nLoadMenuShowX, MASKNES);
if (hItemChanged == hFilterFds) _ToggleGameListing(nLoadMenuShowX, MASKFDS);
if (hItemChanged == hFilterSnes) _ToggleGameListing(nLoadMenuShowX, MASKSNES);
if (hItemChanged == hFilterNgp) _ToggleGameListing(nLoadMenuShowX, MASKNGP);
if (hItemChanged == hFilterChannelF) _ToggleGameListing(nLoadMenuShowX, MASKCHANNELF);
if (hItemChanged == hFilterMidway) _ToggleGameListing(nLoadMenuShowX, MASKMIDWAY);
@ -2536,7 +2551,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
TvItem.mask = TVIF_PARAM | TVIF_STATE | TVIF_CHILDREN;
SendMessage(hSelList, TVM_GETITEM, 0, (LPARAM)&TvItem);
// dprintf(_T(" - Item (%i×%i) - (%i×%i) %hs\n"), lplvcd->nmcd.rc.left, lplvcd->nmcd.rc.top, lplvcd->nmcd.rc.right, lplvcd->nmcd.rc.bottom, ((NODEINFO*)TvItem.lParam)->pszROMName);
// dprintf(_T(" - Item (%i?i) - (%i?i) %hs\n"), lplvcd->nmcd.rc.left, lplvcd->nmcd.rc.top, lplvcd->nmcd.rc.right, lplvcd->nmcd.rc.bottom, ((NODEINFO*)TvItem.lParam)->pszROMName);
// Set the foreground and background colours unless the item is highlighted
if (!(TvItem.state & (TVIS_SELECTED | TVIS_DROPHILITED))) {

View File

@ -194,7 +194,6 @@ BEGIN
IDS_SEL_GENUINE "Genuine"
IDS_SEL_SETSTATUS "Showing %i of %i sets [%i Unavailable sets]"
IDS_SEL_KANEKO "Kaneko"
IDS_SEL_SNES "SNES"
IDS_SEL_DATAEAST "Data East"
IDS_SEL_CAPCOM_MISC "Capcom (Other)"
IDS_SEL_SETA "Seta"
@ -207,6 +206,7 @@ BEGIN
IDS_SEL_MSX "MSX"
IDS_SEL_NES "NES"
IDS_SEL_FDS "FDS"
IDS_SEL_SNES "SNES"
IDS_SEL_NGP "Neo Geo Pocket"
IDS_SEL_CHANNELF "Fairchild Channel F"
IDS_SEL_SPECTRUM "ZX Spectrum"