mirror of
https://github.com/libretro/FBNeo.git
synced 2024-11-23 08:59:39 +00:00
Merge branch 'finalburnneo-master'
This commit is contained in:
commit
5909c67c29
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
242
src/burn/drv/snes/apu.cpp
Normal 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
49
src/burn/drv/snes/apu.h
Normal 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
604
src/burn/drv/snes/cart.cpp
Normal 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
42
src/burn/drv/snes/cart.h
Normal 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
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
32
src/burn/drv/snes/cpu.h
Normal 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
|
2598
src/burn/drv/snes/cpu_sa1.cpp
Normal file
2598
src/burn/drv/snes/cpu_sa1.cpp
Normal file
File diff suppressed because it is too large
Load Diff
29
src/burn/drv/snes/cpu_sa1.h
Normal file
29
src/burn/drv/snes/cpu_sa1.h
Normal 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
859
src/burn/drv/snes/cx4.cpp
Normal 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
12
src/burn/drv/snes/cx4.h
Normal 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
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
404
src/burn/drv/snes/dma.cpp
Normal 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
52
src/burn/drv/snes/dma.h
Normal 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
601
src/burn/drv/snes/dsp.cpp
Normal 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
99
src/burn/drv/snes/dsp.h
Normal 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
107
src/burn/drv/snes/input.cpp
Normal 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
39
src/burn/drv/snes/input.h
Normal 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
|
21
src/burn/drv/snes/license.txt
Normal file
21
src/burn/drv/snes/license.txt
Normal 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
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
56
src/burn/drv/snes/ppu.h
Normal 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
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
15
src/burn/drv/snes/sa1.h
Normal 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
594
src/burn/drv/snes/sdd1.cpp
Normal 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
6
src/burn/drv/snes/sdd1.h
Normal 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
696
src/burn/drv/snes/snes.cpp
Normal 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
102
src/burn/drv/snes/snes.h
Normal 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
|
398
src/burn/drv/snes/snes_other.cpp
Normal file
398
src/burn/drv/snes/snes_other.cpp
Normal 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
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
59
src/burn/drv/snes/spc.h
Normal 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
|
259
src/burn/drv/snes/statehandler.cpp
Normal file
259
src/burn/drv/snes/statehandler.cpp
Normal 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;
|
||||
}
|
33
src/burn/drv/snes/statehandler.h
Normal file
33
src/burn/drv/snes/statehandler.h
Normal 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
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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++) {
|
||||
|
@ -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
|
||||
|
@ -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/") },
|
||||
|
@ -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++) {
|
||||
|
@ -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/")},
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
@ -998,7 +999,7 @@ static void RefreshPanel()
|
||||
EnableWindow(hInfoLabel[i], FALSE);
|
||||
}
|
||||
|
||||
CheckDlgButton(hSelDlg, IDC_CHECKAUTOEXPAND, (nLoadMenuShowY & AUTOEXPAND) ? BST_CHECKED : BST_UNCHECKED);
|
||||
CheckDlgButton(hSelDlg, IDC_CHECKAUTOEXPAND, (nLoadMenuShowY & AUTOEXPAND) ? BST_CHECKED : BST_UNCHECKED);
|
||||
CheckDlgButton(hSelDlg, IDC_CHECKAVAILABLE, (nLoadMenuShowY & AVAILABLE) ? BST_CHECKED : BST_UNCHECKED);
|
||||
CheckDlgButton(hSelDlg, IDC_CHECKUNAVAILABLE, (nLoadMenuShowY & UNAVAILABLE) ? BST_CHECKED : BST_UNCHECKED);
|
||||
|
||||
@ -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);
|
||||
@ -2257,17 +2272,17 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
|
||||
}
|
||||
break;
|
||||
case IDC_SEL_IPSMANAGER:
|
||||
if (bDrvSelected) {
|
||||
int nOldnBurnDrvActive = nBurnDrvActive;
|
||||
if (bDrvSelected) {
|
||||
int nOldnBurnDrvActive = nBurnDrvActive;
|
||||
IpsManagerCreate(hSelDlg);
|
||||
nBurnDrvActive = nOldnBurnDrvActive; // due to some weird bug in sel.cpp, nBurnDrvActive can sometimes change when clicking in new dialogs.
|
||||
nBurnDrvActive = nOldnBurnDrvActive; // due to some weird bug in sel.cpp, nBurnDrvActive can sometimes change when clicking in new dialogs.
|
||||
LoadIpsActivePatches();
|
||||
if (GetIpsNumActivePatches()) {
|
||||
EnableWindow(GetDlgItem(hDlg, IDC_SEL_APPLYIPS), TRUE);
|
||||
} else {
|
||||
EnableWindow(GetDlgItem(hDlg, IDC_SEL_APPLYIPS), FALSE);
|
||||
}
|
||||
SetFocus(hSelList);
|
||||
SetFocus(hSelList);
|
||||
} else {
|
||||
MessageBox(hSelDlg, FBALoadStringEx(hAppInst, IDS_ERR_NO_DRIVER_SELECTED, true), FBALoadStringEx(hAppInst, IDS_ERR_ERROR, true), MB_OK);
|
||||
}
|
||||
@ -2451,8 +2466,8 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
|
||||
{
|
||||
nLoadMenuExpand &= ~TvhFilterToBitmask(curItem.hItem);
|
||||
}
|
||||
else if (pnmtv->action == TVE_EXPAND)
|
||||
{
|
||||
else if (pnmtv->action == TVE_EXPAND)
|
||||
{
|
||||
nLoadMenuExpand |= TvhFilterToBitmask(curItem.hItem);
|
||||
}
|
||||
}
|
||||
@ -2488,7 +2503,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
|
||||
TreeView_HitTest(pNmHdr->hwndFrom, &thi);
|
||||
|
||||
HTREEITEM hSelectHandle = thi.hItem;
|
||||
if(hSelectHandle == NULL) return 1;
|
||||
if(hSelectHandle == NULL) return 1;
|
||||
|
||||
TreeView_SelectItem(hSelList, hSelectHandle);
|
||||
|
||||
@ -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))) {
|
||||
@ -2704,9 +2719,9 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
|
||||
}
|
||||
}
|
||||
|
||||
CheckDlgButton(hDlg, IDC_SEL_APPLYIPS, BST_UNCHECKED);
|
||||
CheckDlgButton(hDlg, IDC_SEL_APPLYIPS, BST_UNCHECKED);
|
||||
|
||||
if (GetIpsNumPatches()) {
|
||||
if (GetIpsNumPatches()) {
|
||||
if (!nShowMVSCartsOnly) {
|
||||
EnableWindow(GetDlgItem(hDlg, IDC_SEL_IPSMANAGER), TRUE);
|
||||
LoadIpsActivePatches();
|
||||
@ -2869,7 +2884,7 @@ static INT_PTR CALLBACK DialogProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lP
|
||||
TreeView_HitTest(pNmHdr->hwndFrom, &thi);
|
||||
|
||||
HTREEITEM hSelectHandle = thi.hItem;
|
||||
if(hSelectHandle == NULL) return 1;
|
||||
if(hSelectHandle == NULL) return 1;
|
||||
|
||||
TreeView_SelectItem(hSelList, hSelectHandle);
|
||||
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user