mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-19 06:51:33 +02:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9e3827e2a2 | ||
|
e499670ad9 | ||
|
805398e5a8 | ||
|
7e6e3e3a69 | ||
|
16ba1d1191 | ||
|
29c871ef62 | ||
|
42f1d08c02 | ||
|
521f4f6952 | ||
|
92cfb1268a | ||
|
4d922ba17c | ||
|
3b2918791c | ||
|
b7d34a8aa3 | ||
|
8fd90cc123 | ||
|
e651beb72e | ||
|
4cbba77fc7 | ||
|
1194d3f9dc | ||
|
89a1b3d65f | ||
|
5a82cdf978 | ||
|
52be510d2b | ||
|
8b7219bdef |
@@ -1,5 +1,5 @@
|
||||
bsnes
|
||||
Version: 0.028
|
||||
Version: 0.030
|
||||
Author: byuu
|
||||
|
||||
--------
|
||||
@@ -17,7 +17,6 @@ Please see license.txt for important licensing information.
|
||||
Known Limitations:
|
||||
------------------
|
||||
S-CPU
|
||||
- Invalid DMA / HDMA transfers not fully emulated
|
||||
- Multiply / Divide register delays not implemented
|
||||
|
||||
S-PPU
|
||||
|
68
src/Makefile
68
src/Makefile
@@ -1,7 +1,5 @@
|
||||
include lib/nall/Makefile.string
|
||||
|
||||
prefix = /usr/local
|
||||
arch = ARCH_LSB
|
||||
|
||||
################
|
||||
### compiler ###
|
||||
@@ -37,12 +35,11 @@ endif
|
||||
|
||||
ifeq ($(platform),x) # X11
|
||||
ruby = video.glx video.xv video.sdl audio.openal audio.oss audio.ao input.sdl input.x
|
||||
arch += PLATFORM_X
|
||||
link += `pkg-config --libs gtk+-2.0`
|
||||
link += $(call mklib,Xtst)
|
||||
delete = rm -f $1
|
||||
else ifeq ($(platform),win) # Windows
|
||||
ruby = video.direct3d video.directdraw video.gdi audio.directsound input.directinput
|
||||
arch += PLATFORM_WIN
|
||||
link += $(if $(findstring mingw,$(compiler)),-mwindows)
|
||||
link += $(call mklib,uuid)
|
||||
link += $(call mklib,kernel32)
|
||||
@@ -61,13 +58,16 @@ endif
|
||||
### ruby ###
|
||||
############
|
||||
|
||||
rubyflags =
|
||||
rubyflags += $(if $(findstring .sdl,$(ruby)),`sdl-config --cflags`)
|
||||
|
||||
link += $(if $(findstring video.direct3d,$(ruby)),$(call mklib,d3d9))
|
||||
link += $(if $(findstring video.directdraw,$(ruby)),$(call mklib,ddraw))
|
||||
link += $(if $(findstring video.glx,$(ruby)),$(call mklib,GL))
|
||||
link += $(if $(findstring video.xv,$(ruby)),$(call mklib,Xv))
|
||||
link += $(if $(findstring audio.ao,$(ruby)),$(call mklib,ao))
|
||||
link += $(if $(findstring audio.directsound,$(ruby)),$(call mklib,dsound))
|
||||
link += $(if $(findstring audio.openal,$(ruby)),$(call mklib,openal) $(call mklib,alut))
|
||||
link += $(if $(findstring audio.openal,$(ruby)),$(if $(call streq,$(platform),x),$(call mklib,openal),$(call mklib,openal32)))
|
||||
link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid))
|
||||
link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`)
|
||||
|
||||
@@ -75,8 +75,8 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`)
|
||||
### main target and dependencies ###
|
||||
####################################
|
||||
|
||||
objects = main libco hiro ruby $(ruby) string reader cart cheat \
|
||||
memory smemory cpu scpu smp ssmp bdsp ppu bppu snes \
|
||||
objects = main libco hiro ruby libfilter string reader cart cheat \
|
||||
memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \
|
||||
bsx srtc sdd1 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
|
||||
|
||||
ifeq ($(enable_gzip),true)
|
||||
@@ -89,7 +89,6 @@ ifeq ($(enable_jma),true)
|
||||
flags += $(call mkdef,JMA_SUPPORT)
|
||||
endif
|
||||
|
||||
arch := $(patsubst %,$(call mkdef,%),$(arch))
|
||||
objects := $(patsubst %,obj/%.$(obj),$(objects))
|
||||
rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),$(call mkdef,$c))
|
||||
|
||||
@@ -124,47 +123,22 @@ all: build;
|
||||
### main ###
|
||||
############
|
||||
|
||||
obj/main.$(obj): ui/main.cpp config/* lib/nall/* lib/ruby/* ui/* ui/loader/* ui/settings/*
|
||||
$(call compile,$(arch))
|
||||
|
||||
obj/bsnes.res : ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc
|
||||
obj/bsnesrc.$(obj): ui/bsnes.rc; windres -I data ui/bsnes.rc obj/bsnesrc.$(obj)
|
||||
|
||||
############
|
||||
### ruby ###
|
||||
############
|
||||
|
||||
obj/ruby.$(obj) : lib/ruby/ruby.cpp lib/ruby/*
|
||||
$(call compile,$(rubydef))
|
||||
obj/video.direct3d.$(obj) : lib/ruby/video/direct3d.cpp lib/ruby/video/direct3d.*
|
||||
obj/video.directdraw.$(obj) : lib/ruby/video/directdraw.cpp lib/ruby/video/directdraw.*
|
||||
obj/video.gdi.$(obj) : lib/ruby/video/gdi.cpp lib/ruby/video/gdi.*
|
||||
obj/video.glx.$(obj) : lib/ruby/video/glx.cpp lib/ruby/video/glx.*
|
||||
obj/video.sdl.$(obj) : lib/ruby/video/sdl.cpp lib/ruby/video/sdl.*
|
||||
$(call compile,`sdl-config --cflags`)
|
||||
obj/video.xv.$(obj) : lib/ruby/video/xv.cpp lib/ruby/video/xv.*
|
||||
obj/audio.ao.$(obj) : lib/ruby/audio/ao.cpp lib/ruby/audio/ao.*
|
||||
obj/audio.directsound.$(obj): lib/ruby/audio/directsound.cpp lib/ruby/audio/directsound.*
|
||||
obj/audio.openal.$(obj) : lib/ruby/audio/openal.cpp lib/ruby/audio/openal.*
|
||||
obj/audio.oss.$(obj) : lib/ruby/audio/oss.cpp lib/ruby/audio/oss.*
|
||||
obj/input.directinput.$(obj): lib/ruby/input/directinput.cpp lib/ruby/input/directinput.*
|
||||
obj/input.sdl.$(obj) : lib/ruby/input/sdl.cpp lib/ruby/input/sdl.*
|
||||
$(call compile,`sdl-config --cflags`)
|
||||
obj/input.x.$(obj) : lib/ruby/input/x.cpp lib/ruby/input/x.*
|
||||
|
||||
############
|
||||
### hiro ###
|
||||
############
|
||||
|
||||
obj/hiro.$(obj): lib/hiro.cpp lib/hiro.* lib/hiro_gtk/* lib/hiro_win/*
|
||||
$(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`))
|
||||
obj/main.$(obj): ui/main.cpp ui/* ui/base/* ui/loader/* ui/settings/*
|
||||
obj/bsnes.res: ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc
|
||||
obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj)
|
||||
|
||||
#################
|
||||
### libraries ###
|
||||
#################
|
||||
|
||||
obj/libco.$(obj): lib/libco.c lib/libco.* lib/libco/*
|
||||
obj/string.$(obj): lib/nall/string.cpp lib/nall/*
|
||||
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/*
|
||||
$(call compile,$(rubydef) $(rubyflags))
|
||||
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/*
|
||||
$(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`))
|
||||
obj/libco.$(obj): lib/libco/libco.c lib/libco/*
|
||||
$(call compile,-static)
|
||||
obj/libfilter.$(obj): lib/libfilter/libfilter.cpp lib/libfilter/*
|
||||
obj/string.$(obj): lib/nall/string.cpp lib/nall/*
|
||||
|
||||
#################
|
||||
### utilities ###
|
||||
@@ -179,7 +153,6 @@ obj/cheat.$(obj) : cheat/cheat.cpp cheat/*
|
||||
##############
|
||||
|
||||
obj/memory.$(obj) : memory/memory.cpp memory/*
|
||||
obj/bmemory.$(obj): memory/bmemory/bmemory.cpp memory/bmemory/* memory/bmemory/mapper/*
|
||||
obj/smemory.$(obj): memory/smemory/smemory.cpp memory/smemory/* memory/smemory/mapper/*
|
||||
|
||||
###########
|
||||
@@ -202,6 +175,7 @@ obj/ssmp.$(obj): smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/core/* smp/ssmp/memory/*
|
||||
|
||||
obj/adsp.$(obj): dsp/adsp/adsp.cpp dsp/adsp/*
|
||||
obj/bdsp.$(obj): dsp/bdsp/bdsp.cpp dsp/bdsp/*
|
||||
obj/sdsp.$(obj): dsp/sdsp/sdsp.cpp dsp/sdsp/*
|
||||
|
||||
###########
|
||||
### ppu ###
|
||||
@@ -270,8 +244,8 @@ build: $(objects)
|
||||
$(strip $(cpp) $(call mkbin,../bsnes) $(objects) $(link))
|
||||
|
||||
install:
|
||||
install -m 755 ../bsnes $(prefix)/bin/bsnes
|
||||
install -m 644 data/bsnes.png $(prefix)/share/icons/bsnes.png
|
||||
install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes
|
||||
install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/icons/bsnes.png
|
||||
|
||||
clean:
|
||||
-@$(call delete,obj/*.$(obj))
|
||||
|
18
src/base.h
18
src/base.h
@@ -1,38 +1,34 @@
|
||||
#define BSNES_VERSION "0.028"
|
||||
#define BSNES_VERSION "0.030"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
#define CPUCORE sCPU
|
||||
#define SMPCORE sSMP
|
||||
#define DSPCORE bDSP
|
||||
#define DSPCORE sDSP
|
||||
#define PPUCORE bPPU
|
||||
|
||||
//FAVOR_ACCURACY calculates RTO during frameskip, whereas FAVOR_SPEED does not
|
||||
//FAST_FRAMESKIP disables calculation of RTO during frameskip
|
||||
//frameskip offers near-zero speedup if RTO is calculated
|
||||
//accuracy is not affected by this define when frameskipping is off
|
||||
|
||||
//#define FAVOR_ACCURACY
|
||||
#define FAVOR_SPEED
|
||||
#define FAST_FRAMESKIP
|
||||
|
||||
//game genie + pro action replay code support (~1-3% speed hit)
|
||||
#define CHEAT_SYSTEM
|
||||
|
||||
#if !defined(ARCH_LSB) && !defined(ARCH_MSB)
|
||||
#define ARCH_LSB //guess
|
||||
#endif
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/config.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <libco.h>
|
||||
#include <libco/libco.h>
|
||||
#include <bbase.h>
|
||||
|
||||
//platform-specific global functions
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include "../base.h"
|
||||
|
||||
#include "../base.h"
|
||||
#define CART_CPP
|
||||
|
||||
#include "cart_normal.cpp"
|
||||
#include "cart_bsx.cpp"
|
||||
#include "cart_bsc.cpp"
|
||||
@@ -82,12 +83,9 @@ void Cartridge::load_end() {
|
||||
memory::stBrom.write_protect(true);
|
||||
memory::stBram.write_protect(false);
|
||||
|
||||
char fn[PATH_MAX];
|
||||
strcpy(fn, cart.fn);
|
||||
modify_extension(fn, "cht");
|
||||
if(fexists(fn)) {
|
||||
if(fexists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
cheat.clear();
|
||||
cheat.load(fn);
|
||||
cheat.load(cheatfn);
|
||||
}
|
||||
|
||||
cart.loaded = true;
|
||||
@@ -114,11 +112,11 @@ bool Cartridge::unload() {
|
||||
safe_free(stB.rom);
|
||||
safe_free(stB.ram);
|
||||
|
||||
char fn[PATH_MAX];
|
||||
char fn[PATH_MAX];
|
||||
strcpy(fn, cart.fn);
|
||||
modify_extension(fn, "cht");
|
||||
if(cheat.count() > 0 || fexists(fn)) {
|
||||
cheat.save(fn);
|
||||
if(cheat.count() > 0 || fexists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
cheat.save(cheatfn);
|
||||
cheat.clear();
|
||||
}
|
||||
|
||||
|
@@ -20,11 +20,11 @@ public:
|
||||
CKSUM = 0x1e,
|
||||
RESL = 0x3c,
|
||||
RESH = 0x3d,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
@@ -67,7 +67,8 @@ public:
|
||||
struct {
|
||||
CartridgeType type;
|
||||
|
||||
uint32 crc32;
|
||||
uint32 crc32;
|
||||
char filename[PATH_MAX * 4];
|
||||
char name[128];
|
||||
|
||||
Region region;
|
||||
@@ -122,14 +123,18 @@ public:
|
||||
|
||||
bool load_file(const char *fn, uint8 *&data, uint &size);
|
||||
bool save_file(const char *fn, uint8 *data, uint size);
|
||||
char* modify_extension(char *filename, const char *extension);
|
||||
char* get_save_filename(const char *source, const char *extension);
|
||||
char* modify_extension(char *filename, const char *extension);
|
||||
char* get_base_filename(char *filename);
|
||||
char* get_path_filename(char *filename, const char *path, const char *source, const char *extension);
|
||||
char* get_save_filename(const char *source, const char *extension);
|
||||
char* get_cheat_filename(const char *source, const char *extension);
|
||||
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
|
||||
private:
|
||||
char savefn[PATH_MAX];
|
||||
char savefn[PATH_MAX];
|
||||
char cheatfn[PATH_MAX];
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
@@ -5,8 +7,8 @@ void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
||||
strcpy(bs.fn, slot ? slot : "");
|
||||
load_begin(CartridgeBSC);
|
||||
|
||||
uint8 *data;
|
||||
uint size;
|
||||
uint8_t *data = 0;
|
||||
unsigned size;
|
||||
load_file(cart.fn, data, size);
|
||||
cart.rom = data, cart.rom_size = size;
|
||||
|
||||
@@ -34,8 +36,21 @@ uint size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, cart.fn);
|
||||
get_base_filename(info.filename);
|
||||
if(*bs.fn) {
|
||||
char filenameBS[PATH_MAX];
|
||||
strcpy(filenameBS, bs.fn);
|
||||
get_base_filename(filenameBS);
|
||||
strcat(info.filename, " + ");
|
||||
strcat(info.filename, filenameBS);
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::unload_cart_bsc() {
|
||||
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
@@ -10,8 +12,8 @@ void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC;
|
||||
|
||||
uint8 *data;
|
||||
uint size;
|
||||
uint8_t *data = 0;
|
||||
unsigned size;
|
||||
load_file(cart.fn, data, size);
|
||||
cart.rom = data, cart.rom_size = size;
|
||||
cart.ram = 0, cart.ram_size = 0;
|
||||
@@ -37,9 +39,14 @@ uint size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
strcpy(info.filename, !*bs.fn ? cart.fn : bs.fn);
|
||||
get_base_filename(info.filename);
|
||||
}
|
||||
|
||||
void Cartridge::unload_cart_bsx() {
|
||||
save_file(get_save_filename(cart.fn, "srm"), bsxcart.sram.handle (), bsxcart.sram.size ());
|
||||
save_file(get_save_filename(cart.fn, "psr"), bsxcart.psram.handle(), bsxcart.psram.size());
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
#include "../reader/filereader.h"
|
||||
|
||||
#if defined(GZIP_SUPPORT)
|
||||
@@ -10,7 +12,7 @@
|
||||
#endif
|
||||
|
||||
char* Cartridge::modify_extension(char *filename, const char *extension) {
|
||||
int i;
|
||||
int i;
|
||||
for(i = strlen(filename); i >= 0; i--) {
|
||||
if(filename[i] == '.') break;
|
||||
if(filename[i] == '/') break;
|
||||
@@ -20,32 +22,65 @@ int i;
|
||||
strcat(filename, ".");
|
||||
strcat(filename, extension);
|
||||
return filename;
|
||||
}
|
||||
|
||||
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
|
||||
char* Cartridge::get_base_filename(char *filename) {
|
||||
//remove extension
|
||||
for(int i = strlen(filename) - 1; i >= 0; i--) {
|
||||
if(filename[i] == '.') {
|
||||
filename[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//remove directory information
|
||||
for(int i = strlen(filename) - 1; i >= 0; i--) {
|
||||
if(filename[i] == '/' || filename[i] == '\\') {
|
||||
i++;
|
||||
char *output = filename;
|
||||
while(true) {
|
||||
*output++ = filename[i];
|
||||
if(!filename[i]) break;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* Cartridge::get_save_filename(const char *source, const char *extension) {
|
||||
strcpy(savefn, source);
|
||||
for(char *p = savefn; *p; p++) { if(*p == '\\') *p = '/'; }
|
||||
modify_extension(savefn, extension);
|
||||
char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) {
|
||||
strcpy(filename, source);
|
||||
for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; }
|
||||
modify_extension(filename, extension);
|
||||
|
||||
//override path with user-specified folder, if one was defined
|
||||
if(config::path.save != "") {
|
||||
lstring part;
|
||||
split(part, "/", savefn);
|
||||
string fn = (const char*)config::path.save;
|
||||
//override path with user-specified folder, if one was defined
|
||||
if(*path) {
|
||||
lstring part;
|
||||
split(part, "/", filename);
|
||||
string fn = path;
|
||||
if(strend(fn, "/") == false) strcat(fn, "/");
|
||||
strcat(fn, part[count(part) - 1]);
|
||||
strcpy(savefn, fn);
|
||||
strcpy(filename, fn);
|
||||
|
||||
//resolve relative path, if found
|
||||
//resolve relative path, if found
|
||||
if(strbegin(fn, "./") == true) {
|
||||
ltrim(fn, "./");
|
||||
strcpy(savefn, config::path.base);
|
||||
strcat(savefn, fn);
|
||||
strcpy(filename, config::path.base);
|
||||
strcat(filename, fn);
|
||||
}
|
||||
}
|
||||
|
||||
return savefn;
|
||||
return filename;
|
||||
}
|
||||
|
||||
char* Cartridge::get_save_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(savefn, config::path.save, source, extension);
|
||||
}
|
||||
|
||||
char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(cheatfn, config::path.cheat, source, extension);
|
||||
}
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) {
|
||||
dprintf("* Loading \"%s\"...", fn);
|
||||
@@ -54,8 +89,8 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) {
|
||||
|
||||
switch(Reader::detect(fn)) {
|
||||
default:
|
||||
case Reader::RF_NORMAL: {
|
||||
FileReader ff(fn);
|
||||
case Reader::Normal: {
|
||||
FileReader ff(fn);
|
||||
if(!ff.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
@@ -64,9 +99,9 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) {
|
||||
data = ff.read();
|
||||
} break;
|
||||
|
||||
#ifdef GZIP_SUPPORT
|
||||
case Reader::RF_GZ: {
|
||||
GZReader gf(fn);
|
||||
#ifdef GZIP_SUPPORT
|
||||
case Reader::GZIP: {
|
||||
GZReader gf(fn);
|
||||
if(!gf.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
@@ -75,17 +110,17 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) {
|
||||
data = gf.read();
|
||||
} break;
|
||||
|
||||
case Reader::RF_ZIP: {
|
||||
ZipReader zf(fn);
|
||||
case Reader::ZIP: {
|
||||
ZipReader zf(fn);
|
||||
size = zf.size();
|
||||
data = zf.read();
|
||||
} break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JMA_SUPPORT
|
||||
case Reader::RF_JMA: {
|
||||
#ifdef JMA_SUPPORT
|
||||
case Reader::JMA: {
|
||||
try {
|
||||
JMAReader jf(fn);
|
||||
JMAReader jf(fn);
|
||||
size = jf.size();
|
||||
data = jf.read();
|
||||
} catch(JMA::jma_errors jma_error) {
|
||||
@@ -93,15 +128,17 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::save_file(const char *fn, uint8 *data, uint size) {
|
||||
FileWriter ff(fn);
|
||||
FileWriter ff(fn);
|
||||
if(!ff.ready())return false;
|
||||
ff.write(data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,184 +1,189 @@
|
||||
void Cartridge::read_header() {
|
||||
uint8 *rom = cart.rom;
|
||||
uint index = info.header_index;
|
||||
uint8 mapper = rom[index + MAPPER];
|
||||
uint8 rom_type = rom[index + ROM_TYPE];
|
||||
uint8 company = rom[index + COMPANY];
|
||||
uint8 region = rom[index + REGION] & 0x7f;
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
bool has_bsxflash = false;
|
||||
if(rom[index - 14] == 'Z') {
|
||||
if(rom[index - 11] == 'J') {
|
||||
uint8 n13 = rom[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) {
|
||||
has_bsxflash = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsxflash == true) {
|
||||
info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
||||
} else if(index == 0x7fc0 && cart.rom_size >= 0x401000) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapper == 0x32) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
info.mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
info.mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
info.mapper = ExHiROM;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
info.superfx = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) {
|
||||
info.sa1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x35 && rom_type == 0x55) {
|
||||
info.srtc = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
info.sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0xf3) {
|
||||
info.cx4 = true;
|
||||
}
|
||||
|
||||
if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(info.dsp1 == true) {
|
||||
if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) {
|
||||
info.dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapper & 0x2f) == 0x20) {
|
||||
info.dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapper & 0x2f) == 0x21) {
|
||||
info.dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0x05) {
|
||||
info.dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
info.dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x03) {
|
||||
info.dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x25) {
|
||||
info.obc1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6) {
|
||||
//TODO: both ST010 and ST011 share the same mapper + rom_type
|
||||
//need way to determine which is which
|
||||
//for now, default to supported ST010
|
||||
info.st010 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf5) {
|
||||
info.st018 = true;
|
||||
}
|
||||
|
||||
if(rom[info.header_index + RAM_SIZE] & 7) {
|
||||
info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7);
|
||||
} else {
|
||||
info.ram_size = 0;
|
||||
}
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||
|
||||
memcpy(&info.name, &rom[info.header_index + CART_NAME], 21);
|
||||
info.name[21] = 0;
|
||||
|
||||
for(int i = 0; i < 22; i++) {
|
||||
if(info.name[i] & 0x80) {
|
||||
info.name[i] = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::find_header() {
|
||||
int32 score_lo = 0,
|
||||
score_hi = 0,
|
||||
score_ex = 0;
|
||||
uint8 *rom = cart.rom;
|
||||
|
||||
if(cart.rom_size < 0x010000) {
|
||||
//cart too small to be anything but lorom
|
||||
info.header_index = 0x007fc0;
|
||||
return;
|
||||
}
|
||||
|
||||
if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20)score_lo++;
|
||||
if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_TYPE] < 0x08)score_lo++;
|
||||
if(rom[0xffc0 + ROM_TYPE] < 0x08)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_SIZE] < 0x10)score_lo++;
|
||||
if(rom[0xffc0 + ROM_SIZE] < 0x10)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RAM_SIZE] < 0x08)score_lo++;
|
||||
if(rom[0xffc0 + RAM_SIZE] < 0x08)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + REGION] < 14)score_lo++;
|
||||
if(rom[0xffc0 + REGION] < 14)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + COMPANY] < 3)score_lo++;
|
||||
if(rom[0xffc0 + COMPANY] < 3)score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RESH] & 0x80)score_lo += 2;
|
||||
if(rom[0xffc0 + RESH] & 0x80)score_hi += 2;
|
||||
|
||||
uint16 cksum, icksum;
|
||||
cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_lo += 8;
|
||||
}
|
||||
|
||||
cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_hi += 8;
|
||||
}
|
||||
|
||||
if(cart.rom_size < 0x401000) {
|
||||
score_ex = 0;
|
||||
} else {
|
||||
if(rom[0x7fc0 + MAPPER] == 0x32)score_lo++;
|
||||
else score_ex += 16;
|
||||
}
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
info.header_index = 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
info.header_index = 0x00ffc0;
|
||||
} else {
|
||||
info.header_index = 0x40ffc0;
|
||||
}
|
||||
}
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::read_header() {
|
||||
uint8 *rom = cart.rom;
|
||||
uint index = info.header_index;
|
||||
uint8 mapper = rom[index + MAPPER];
|
||||
uint8 rom_type = rom[index + ROM_TYPE];
|
||||
uint8 company = rom[index + COMPANY];
|
||||
uint8 region = rom[index + REGION] & 0x7f;
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
bool has_bsxflash = false;
|
||||
if(rom[index - 14] == 'Z') {
|
||||
if(rom[index - 11] == 'J') {
|
||||
uint8 n13 = rom[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) {
|
||||
has_bsxflash = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsxflash == true) {
|
||||
info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
||||
} else if(index == 0x7fc0 && cart.rom_size >= 0x401000) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapper == 0x32) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
info.mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
info.mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
info.mapper = ExHiROM;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
info.superfx = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x23 && (rom_type == 0x34 || rom_type == 0x35)) {
|
||||
info.sa1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x35 && rom_type == 0x55) {
|
||||
info.srtc = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
info.sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0xf3) {
|
||||
info.cx4 = true;
|
||||
}
|
||||
|
||||
if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
info.dsp1 = true;
|
||||
}
|
||||
|
||||
if(info.dsp1 == true) {
|
||||
if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) {
|
||||
info.dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapper & 0x2f) == 0x20) {
|
||||
info.dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapper & 0x2f) == 0x21) {
|
||||
info.dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0x05) {
|
||||
info.dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
info.dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x03) {
|
||||
info.dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x25) {
|
||||
info.obc1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6) {
|
||||
//TODO: both ST010 and ST011 share the same mapper + rom_type.
|
||||
//need way to determine which is which.
|
||||
//for now, default to supported ST010.
|
||||
info.st010 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf5) {
|
||||
info.st018 = true;
|
||||
}
|
||||
|
||||
if(rom[info.header_index + RAM_SIZE] & 7) {
|
||||
info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7);
|
||||
} else {
|
||||
info.ram_size = 0;
|
||||
}
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||
|
||||
memcpy(&info.name, &rom[info.header_index + CART_NAME], 21);
|
||||
info.name[21] = 0;
|
||||
trim(info.name);
|
||||
|
||||
//convert undisplayable characters (half-width katakana, etc) to '?' characters
|
||||
for(int i = 0; i < 21; i++) {
|
||||
if(info.name[i] & 0x80) info.name[i] = '?';
|
||||
}
|
||||
|
||||
//always display something
|
||||
if(!info.name[0]) strcpy(info.name, "(untitled)");
|
||||
}
|
||||
|
||||
void Cartridge::find_header() {
|
||||
int32 score_lo = 0, score_hi = 0, score_ex = 0;
|
||||
uint8_t *rom = cart.rom;
|
||||
|
||||
if(cart.rom_size < 0x010000) {
|
||||
//cart too small to be anything but lorom
|
||||
info.header_index = 0x007fc0;
|
||||
return;
|
||||
}
|
||||
|
||||
if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20) score_lo++;
|
||||
if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_TYPE] < 0x08) score_lo++;
|
||||
if(rom[0xffc0 + ROM_TYPE] < 0x08) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_SIZE] < 0x10) score_lo++;
|
||||
if(rom[0xffc0 + ROM_SIZE] < 0x10) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RAM_SIZE] < 0x08) score_lo++;
|
||||
if(rom[0xffc0 + RAM_SIZE] < 0x08) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + REGION] < 14) score_lo++;
|
||||
if(rom[0xffc0 + REGION] < 14) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + COMPANY] < 3) score_lo++;
|
||||
if(rom[0xffc0 + COMPANY] < 3) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RESH] & 0x80) score_lo += 2;
|
||||
if(rom[0xffc0 + RESH] & 0x80) score_hi += 2;
|
||||
|
||||
uint16 cksum, icksum;
|
||||
cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_lo += 8;
|
||||
}
|
||||
|
||||
cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_hi += 8;
|
||||
}
|
||||
|
||||
if(cart.rom_size < 0x401000) {
|
||||
score_ex = 0;
|
||||
} else {
|
||||
if(rom[0x7fc0 + MAPPER] == 0x32) score_lo++;
|
||||
else score_ex += 16;
|
||||
}
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
info.header_index = 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
info.header_index = 0x00ffc0;
|
||||
} else {
|
||||
info.header_index = 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,14 +1,16 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_normal(const char *filename) {
|
||||
if(!filename || !*filename) return;
|
||||
|
||||
uint8 *data;
|
||||
uint size;
|
||||
uint8_t *data = 0;
|
||||
unsigned size;
|
||||
if(load_file(filename, data, size) == false) return;
|
||||
strcpy(cart.fn, filename);
|
||||
|
||||
load_begin(CartridgeNormal);
|
||||
|
||||
//load ROM data, ignore 512-byte header if detected
|
||||
//load ROM data, ignore 512-byte header if detected
|
||||
if((size & 0x7fff) != 512) {
|
||||
cart.rom = (uint8*)malloc(cart.rom_size = size);
|
||||
memcpy(cart.rom, data, size);
|
||||
@@ -34,8 +36,14 @@ uint size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, cart.fn);
|
||||
get_base_filename(info.filename);
|
||||
}
|
||||
|
||||
void Cartridge::unload_cart_normal() {
|
||||
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
@@ -10,8 +12,8 @@ void Cartridge::load_cart_st(const char *base, const char *slotA, const char *sl
|
||||
info.mapper = STROM;
|
||||
info.region = NTSC;
|
||||
|
||||
uint8 *data;
|
||||
uint size;
|
||||
uint8_t *data = 0;
|
||||
unsigned size;
|
||||
if(load_file(cart.fn, data, size) == true) {
|
||||
cart.rom = (uint8*)malloc(cart.rom_size = 0x040000);
|
||||
memcpy(cart.rom, data, min(size, cart.rom_size));
|
||||
@@ -51,9 +53,32 @@ uint size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
if(!*stA.fn && !*stB.fn) {
|
||||
strcpy(info.filename, cart.fn);
|
||||
get_base_filename(info.filename);
|
||||
} else if(*stA.fn && !*stB.fn) {
|
||||
strcpy(info.filename, stA.fn);
|
||||
get_base_filename(info.filename);
|
||||
} else if(!*stA.fn && *stB.fn) {
|
||||
strcpy(info.filename, stB.fn);
|
||||
get_base_filename(info.filename);
|
||||
} else {
|
||||
char filenameA[PATH_MAX], filenameB[PATH_MAX];
|
||||
strcpy(filenameA, stA.fn);
|
||||
get_base_filename(filenameA);
|
||||
strcpy(filenameB, stB.fn);
|
||||
get_base_filename(filenameB);
|
||||
strcpy(info.filename, filenameA);
|
||||
strcat(info.filename, " + ");
|
||||
strcat(info.filename, filenameB);
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::unload_cart_st() {
|
||||
if(stA.ram) save_file(get_save_filename(stA.fn, "srm"), stA.ram, stA.ram_size);
|
||||
if(stB.ram) save_file(get_save_filename(stB.fn, "srm"), stB.ram, stB.ram_size);
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
@@ -1,3 +1,3 @@
|
||||
@make platform=win compiler=mingw32-gcc
|
||||
::@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
::@make platform=win compiler=mingw32-gcc
|
||||
@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
@pause
|
||||
|
@@ -10,24 +10,24 @@ Cheat cheat;
|
||||
*****/
|
||||
|
||||
bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
|
||||
string t, part;
|
||||
string t, part;
|
||||
strcpy(t, str);
|
||||
strlower(t());
|
||||
if(strlen(t) == 8 || (strlen(t) == 9 && t()[6] == ':')) {
|
||||
type = CT_PRO_ACTION_REPLAY;
|
||||
type = ProActionReplay;
|
||||
replace(t, ":", "");
|
||||
uint32 r = strhex((const char*)t);
|
||||
uint32 r = strhex((const char*)t);
|
||||
addr = r >> 8;
|
||||
data = r & 0xff;
|
||||
return true;
|
||||
} else if(strlen(t) == 9 && t()[4] == '-') {
|
||||
type = CT_GAME_GENIE;
|
||||
type = GameGenie;
|
||||
replace(t, "-", "");
|
||||
strtr(t, "df4709156bc8a23e", "0123456789abcdef");
|
||||
uint32 r = strhex((const char*)t);
|
||||
//8421 8421 8421 8421 8421 8421
|
||||
//abcd efgh ijkl mnop qrst uvwx
|
||||
//ijkl qrst opab cduv wxef ghmn
|
||||
uint32 r = strhex((const char*)t);
|
||||
//8421 8421 8421 8421 8421 8421
|
||||
//abcd efgh ijkl mnop qrst uvwx
|
||||
//ijkl qrst opab cduv wxef ghmn
|
||||
addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) |
|
||||
(!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) |
|
||||
(!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) |
|
||||
@@ -47,10 +47,10 @@ string t, part;
|
||||
}
|
||||
|
||||
bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||
if(type == CT_PRO_ACTION_REPLAY) {
|
||||
if(type == ProActionReplay) {
|
||||
sprintf(str, "%0.6x:%0.2x", addr, data);
|
||||
return true;
|
||||
} else if(type == CT_GAME_GENIE) {
|
||||
} else if(type == GameGenie) {
|
||||
uint32 r = addr;
|
||||
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) |
|
||||
(!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) |
|
||||
@@ -79,9 +79,9 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||
*****/
|
||||
|
||||
uint Cheat::mirror_address(uint addr) {
|
||||
if((addr & 0x40e000) != 0x0000)return addr;
|
||||
//8k WRAM mirror
|
||||
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
||||
if((addr & 0x40e000) != 0x0000) return addr;
|
||||
//8k WRAM mirror
|
||||
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
||||
return (0x7e0000 + (addr & 0x1fff));
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ void Cheat::set(uint32 addr) {
|
||||
|
||||
mask[addr >> 3] |= 1 << (addr & 7);
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
for(int x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] |= 1 << (mirror & 7);
|
||||
@@ -104,16 +104,16 @@ void Cheat::set(uint32 addr) {
|
||||
void Cheat::clear(uint32 addr) {
|
||||
addr = mirror_address(addr);
|
||||
|
||||
//is there more than one cheat code using the same address
|
||||
//(and likely a different override value) that is enabled?
|
||||
//if so, do not clear code lookup table entry for this address.
|
||||
uint8 r;
|
||||
//is there more than one cheat code using the same address
|
||||
//(and likely a different override value) that is enabled?
|
||||
//if so, do not clear code lookup table entry for this address.
|
||||
uint8 r;
|
||||
if(read(addr, r) == true)return;
|
||||
|
||||
mask[addr >> 3] &= ~(1 << (addr & 7));
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
for(int x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
||||
@@ -133,13 +133,13 @@ uint8 r;
|
||||
bool Cheat::read(uint32 addr, uint8 &data) {
|
||||
addr = mirror_address(addr);
|
||||
for(int i = 0; i < cheat_count; i++) {
|
||||
if(enabled(i) == false)continue;
|
||||
if(enabled(i) == false) continue;
|
||||
if(addr == mirror_address(index[i].addr)) {
|
||||
data = index[i].data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//code not found, or code is disabled
|
||||
//code not found, or code is disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -147,9 +147,10 @@ bool Cheat::read(uint32 addr, uint8 &data) {
|
||||
* update_cheat_status() will scan to see if any codes are
|
||||
* enabled. if any are, make sure the cheat system is on.
|
||||
* otherwise, turn cheat system off to speed up emulation.
|
||||
*****/
|
||||
*****/
|
||||
|
||||
void Cheat::update_cheat_status() {
|
||||
for(int i = 0; i < cheat_count; i++) {
|
||||
for(unsigned i = 0; i < cheat_count; i++) {
|
||||
if(index[i].enabled) {
|
||||
cheat_enabled = true;
|
||||
return;
|
||||
@@ -163,11 +164,11 @@ void Cheat::update_cheat_status() {
|
||||
*****/
|
||||
|
||||
bool Cheat::add(bool enable, char *code, char *desc) {
|
||||
if(cheat_count >= CHEAT_LIMIT)return false;
|
||||
if(cheat_count >= CheatLimit) return false;
|
||||
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false)return false;
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false) return false;
|
||||
|
||||
index[cheat_count].enabled = enable;
|
||||
index[cheat_count].addr = addr;
|
||||
@@ -188,17 +189,17 @@ uint8 data, type;
|
||||
}
|
||||
|
||||
bool Cheat::edit(uint32 n, bool enable, char *code, char *desc) {
|
||||
if(n >= cheat_count)return false;
|
||||
if(n >= cheat_count) return false;
|
||||
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false)return false;
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false) return false;
|
||||
|
||||
//disable current code and clear from code lookup table
|
||||
//disable current code and clear from code lookup table
|
||||
index[n].enabled = false;
|
||||
clear(index[n].addr);
|
||||
|
||||
//update code and enable in code lookup table
|
||||
//update code and enable in code lookup table
|
||||
index[n].enabled = enable;
|
||||
index[n].addr = addr;
|
||||
index[n].data = data;
|
||||
@@ -217,9 +218,9 @@ uint8 data, type;
|
||||
}
|
||||
|
||||
bool Cheat::remove(uint32 n) {
|
||||
if(n >= cheat_count)return false;
|
||||
if(n >= cheat_count) return false;
|
||||
|
||||
for(int i = n; i < cheat_count; i++) {
|
||||
for(unsigned i = n; i < cheat_count; i++) {
|
||||
index[i].enabled = index[i + 1].enabled;
|
||||
index[i].addr = index[i + 1].addr;
|
||||
index[i].data = index[i + 1].data;
|
||||
@@ -233,7 +234,7 @@ bool Cheat::remove(uint32 n) {
|
||||
}
|
||||
|
||||
bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc) {
|
||||
if(n >= cheat_count)return false;
|
||||
if(n >= cheat_count) return false;
|
||||
enable = index[n].enabled;
|
||||
addr = index[n].addr;
|
||||
data = index[n].data;
|
||||
@@ -247,19 +248,19 @@ bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, c
|
||||
*****/
|
||||
|
||||
bool Cheat::enabled(uint32 n) {
|
||||
if(n >= cheat_count)return false;
|
||||
if(n >= cheat_count) return false;
|
||||
return index[n].enabled;
|
||||
}
|
||||
|
||||
void Cheat::enable(uint32 n) {
|
||||
if(n >= cheat_count)return;
|
||||
if(n >= cheat_count) return;
|
||||
index[n].enabled = true;
|
||||
set(index[n].addr);
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
void Cheat::disable(uint32 n) {
|
||||
if(n >= cheat_count)return;
|
||||
if(n >= cheat_count) return;
|
||||
index[n].enabled = false;
|
||||
clear(index[n].addr);
|
||||
update_cheat_status();
|
||||
@@ -274,16 +275,16 @@ void Cheat::disable(uint32 n) {
|
||||
/* ... */
|
||||
|
||||
bool Cheat::load(const char *fn) {
|
||||
string data;
|
||||
string data;
|
||||
if(!fread(data, fn)) return false;
|
||||
replace(data, "\r\n", "\n");
|
||||
qreplace(data, "=", ",");
|
||||
qreplace(data, " ", "");
|
||||
|
||||
lstring line;
|
||||
lstring line;
|
||||
split(line, "\n", data);
|
||||
for(int i = 0; i < ::count(line); i++) {
|
||||
lstring part;
|
||||
for(unsigned i = 0; i < ::count(line); i++) {
|
||||
lstring part;
|
||||
split(part, ",", line[i]);
|
||||
if(::count(part) != 3) continue;
|
||||
trim(part[2], "\"");
|
||||
@@ -294,9 +295,9 @@ lstring line;
|
||||
}
|
||||
|
||||
bool Cheat::save(const char *fn) {
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
if(!fp) return false;
|
||||
for(int i = 0; i < cheat_count; i++) {
|
||||
for(unsigned i = 0; i < cheat_count; i++) {
|
||||
fprintf(fp, "%9s = %8s, \"%s\"\r\n",
|
||||
index[i].code,
|
||||
index[i].enabled ? "enabled" : "disabled",
|
||||
@@ -314,7 +315,7 @@ void Cheat::clear() {
|
||||
cheat_enabled = false;
|
||||
cheat_count = 0;
|
||||
memset(mask, 0, 0x200000);
|
||||
for(int i = 0; i <= CHEAT_LIMIT; i++) {
|
||||
for(unsigned i = 0; i <= CheatLimit; i++) {
|
||||
index[i].enabled = false;
|
||||
index[i].addr = 0x000000;
|
||||
index[i].data = 0x00;
|
||||
|
@@ -1,51 +1,51 @@
|
||||
#define CHEAT_LIMIT 1024
|
||||
|
||||
class Cheat {
|
||||
public:
|
||||
enum {
|
||||
CT_PRO_ACTION_REPLAY,
|
||||
CT_GAME_GENIE
|
||||
public:
|
||||
enum { CheatLimit = 1024 };
|
||||
|
||||
enum Type {
|
||||
ProActionReplay,
|
||||
GameGenie,
|
||||
};
|
||||
|
||||
struct CheatIndex {
|
||||
bool enabled;
|
||||
bool enabled;
|
||||
uint32 addr;
|
||||
uint8 data;
|
||||
char code[ 16 + 1];
|
||||
char desc[128 + 1];
|
||||
} index[CHEAT_LIMIT + 1];
|
||||
uint8 data;
|
||||
char code[ 16 + 1];
|
||||
char desc[128 + 1];
|
||||
} index[CheatLimit + 1];
|
||||
|
||||
bool cheat_enabled;
|
||||
bool cheat_enabled;
|
||||
uint32 cheat_count;
|
||||
uint8 mask[0x200000];
|
||||
uint8 mask[0x200000];
|
||||
|
||||
inline bool enabled() { return cheat_enabled; }
|
||||
inline uint count() { return cheat_count; }
|
||||
inline bool exists(uint32 addr) { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
|
||||
|
||||
bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type);
|
||||
bool encode(char *str, uint32 addr, uint8 data, uint8 type);
|
||||
bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type);
|
||||
bool encode(char *str, uint32 addr, uint8 data, uint8 type);
|
||||
|
||||
bool read(uint32 addr, uint8 &data);
|
||||
bool read(uint32 addr, uint8 &data);
|
||||
|
||||
void update_cheat_status();
|
||||
bool add(bool enable, char *code, char *desc);
|
||||
bool edit(uint32 n, bool enable, char *code, char *desc);
|
||||
bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc);
|
||||
bool remove (uint32 n);
|
||||
bool enabled(uint32 n);
|
||||
void enable (uint32 n);
|
||||
void disable(uint32 n);
|
||||
bool load(const char *fn);
|
||||
bool save(const char *fn);
|
||||
void clear();
|
||||
void update_cheat_status();
|
||||
bool add(bool enable, char *code, char *desc);
|
||||
bool edit(uint32 n, bool enable, char *code, char *desc);
|
||||
bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc);
|
||||
bool remove(uint32 n);
|
||||
bool enabled(uint32 n);
|
||||
void enable(uint32 n);
|
||||
void disable(uint32 n);
|
||||
bool load(const char *fn);
|
||||
bool save(const char *fn);
|
||||
void clear();
|
||||
|
||||
Cheat();
|
||||
|
||||
private:
|
||||
uint mirror_address(uint addr);
|
||||
void set(uint32 addr);
|
||||
void clear(uint32 addr);
|
||||
uint mirror_address(uint addr);
|
||||
void set(uint32 addr);
|
||||
void clear(uint32 addr);
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define BSX_CPP
|
||||
|
||||
#include "bsx_base.cpp"
|
||||
#include "bsx_cart.cpp"
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef BSX_CPP
|
||||
|
||||
void BSXBase::init() {
|
||||
}
|
||||
|
||||
@@ -131,3 +133,5 @@ void BSXBase::mmio_write(uint addr, uint8 data) {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef BSX_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef BSX_CPP
|
||||
|
||||
void BSXCart::init() {
|
||||
}
|
||||
|
||||
@@ -93,3 +95,5 @@ BSXCart::~BSXCart() {
|
||||
safe_free(sram_data);
|
||||
safe_free(psram_data);
|
||||
}
|
||||
|
||||
#endif //ifdef BSX_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef BSX_CPP
|
||||
|
||||
void BSXFlash::init() {}
|
||||
void BSXFlash::enable() {}
|
||||
|
||||
@@ -107,3 +109,5 @@ void BSXFlash::write(uint addr, uint8 data) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef BSX_CPP
|
||||
|
@@ -5,7 +5,8 @@
|
||||
Portions (c) anomie, Overload, zsKnight, Nach, byuu
|
||||
*/
|
||||
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define CX4_CPP
|
||||
|
||||
#include "cx4data.cpp"
|
||||
#include "cx4fn.cpp"
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
const uint8 Cx4::immediate_data[48] = {
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f,
|
||||
@@ -181,3 +183,5 @@ const int16 Cx4::CosTable[512] = {
|
||||
32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568,
|
||||
32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765
|
||||
};
|
||||
|
||||
#endif //ifdef CX4_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
#include <math.h>
|
||||
#define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000)
|
||||
#define sar(b, n) ((b) >> (n))
|
||||
@@ -240,3 +242,5 @@ uint8 bit = 0x80;
|
||||
LineY += D;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef CX4_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
//Build OAM
|
||||
void Cx4::op00_00() {
|
||||
uint32 oamptr = ram[0x626] << 2;
|
||||
@@ -217,3 +219,5 @@ uint16 mask2 = 0x3f3f;
|
||||
destptr += 16;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef CX4_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
//Sprite Functions
|
||||
void Cx4::op00() {
|
||||
switch(reg[0x4d]) {
|
||||
@@ -220,3 +222,5 @@ void Cx4::op89() {
|
||||
str(0, 0x054336);
|
||||
str(1, 0xffffff);
|
||||
}
|
||||
|
||||
#endif //ifdef CX4_CPP
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define DSP1_CPP
|
||||
|
||||
#include "dsp1emu.cpp"
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef DSP1_CPP
|
||||
|
||||
// DSP-1's emulation code
|
||||
//
|
||||
// Based on research by Overload, The Dumper, Neviksti and Andreas Naive
|
||||
@@ -1620,3 +1622,4 @@ const int16 Dsp1::SinTable[256] = {
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif //ifdef DSP1_CPP
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define DSP2_CPP
|
||||
|
||||
#include "dsp2_op.cpp"
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef DSP2_CPP
|
||||
|
||||
//convert bitmap to bitplane tile
|
||||
void DSP2::op01() {
|
||||
//op01 size is always 32 bytes input and output
|
||||
@@ -171,3 +173,5 @@ uint8 pixelarray[512];
|
||||
status.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1];
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef DSP2_CPP
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define DSP3_CPP
|
||||
|
||||
namespace DSP3i {
|
||||
#define bool8 uint8
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef DSP3_CPP
|
||||
|
||||
//DSP-3 emulator code
|
||||
//Copyright (c) 2003-2006 John Weidman, Kris Bleakley, Lancer, z80 gaiden
|
||||
|
||||
@@ -1140,3 +1142,5 @@ void InitDSP3()
|
||||
{
|
||||
DSP3_Reset();
|
||||
}
|
||||
|
||||
#endif //ifdef DSP3_CPP
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define DSP4_CPP
|
||||
|
||||
namespace DSP4i {
|
||||
inline uint16 READ_WORD(uint8 *addr) {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef DSP4_CPP
|
||||
|
||||
//DSP-4 emulator code
|
||||
//Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden
|
||||
|
||||
@@ -2144,3 +2146,5 @@ void DSP4GetByte()
|
||||
dsp4_byte = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef DSP4_CPP
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define SDD1_CPP
|
||||
|
||||
#include "sdd1emu.cpp"
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SDD1_CPP
|
||||
|
||||
/************************************************************************
|
||||
|
||||
S-DD1'algorithm emulation code
|
||||
@@ -445,3 +447,5 @@ SDD1emu::SDD1emu() :
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#endif //ifdef SDD1_CPP
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define ST010_CPP
|
||||
|
||||
#include "st010_data.h"
|
||||
#include "st010_op.cpp"
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef ST010_CPP
|
||||
|
||||
//ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather
|
||||
//bsnes port - Copyright (C) 2007 byuu
|
||||
|
||||
@@ -255,3 +257,5 @@ int16 x1, y1;
|
||||
writew(0x0010, x1);
|
||||
writew(0x0012, y1);
|
||||
}
|
||||
|
||||
#endif //ifdef ST010_CPP
|
||||
|
@@ -1,28 +1,36 @@
|
||||
namespace config {
|
||||
|
||||
configuration& config() {
|
||||
static configuration config;
|
||||
static configuration config;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
integral_setting File::autodetect_type(config(), "file.autodetect_type",
|
||||
"Auto-detect file type by inspecting file header, rather than by file extension.\n"
|
||||
"In other words, if a .zip file is renamed to .smc, it will still be correctly\n"
|
||||
"identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n"
|
||||
"chance of a false detection when loading an uncompressed image file, if this\n"
|
||||
"option is enabled.",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
string file_updatepath(const char *req_file, const char *req_path) {
|
||||
string file(req_file);
|
||||
string file(req_file);
|
||||
replace(file, "\\", "/");
|
||||
if(!req_path || strlen(req_path) == 0) { return file; }
|
||||
|
||||
string path(req_path);
|
||||
string path(req_path);
|
||||
replace(path, "\\", "/");
|
||||
if(!strend(path, "/")) { strcat(path, "/"); }
|
||||
|
||||
if(strbegin(path, "./")) {
|
||||
ltrim(path(), "./");
|
||||
string temp;
|
||||
string temp;
|
||||
strcpy(temp, config::path.base);
|
||||
strcat(temp, path);
|
||||
strcpy(path, temp);
|
||||
}
|
||||
|
||||
lstring part;
|
||||
lstring part;
|
||||
split(part, "/", file);
|
||||
strcat(path, part[count(part) - 1]);
|
||||
return path;
|
||||
@@ -32,35 +40,17 @@ string_setting Path::base("path.base",
|
||||
"Path that bsnes resides in", "");
|
||||
string_setting Path::rom(config(), "path.rom",
|
||||
"Default path to look for ROM files in (\"\" = use default directory)", "");
|
||||
string_setting Path::save(config(), "path.save",
|
||||
"Default path for all save RAM and cheat files (\"\" = use current directory)", "");
|
||||
string_setting Path::save(config(), "path.save",
|
||||
"Default path for all save RAM files (\"\" = use current directory)", "");
|
||||
string_setting Path::cheat(config(), "path.cheat",
|
||||
"Default path for all cheat files (\"\" = use current directory)", "");
|
||||
string_setting Path::bsx(config(), "path.bsx", "", "");
|
||||
string_setting Path::st(config(), "path.st", "", "");
|
||||
|
||||
integral_setting SNES::gamma_ramp(config(), "snes.colorfilter.gamma_ramp",
|
||||
"Use precalculated TV-style gamma ramp", integral_setting::boolean, true);
|
||||
integral_setting SNES::sepia(config(), "snes.colorfilter.sepia",
|
||||
"Convert color to sepia tone", integral_setting::boolean, false);
|
||||
integral_setting SNES::grayscale(config(), "snes.colorfilter.grayscale",
|
||||
"Convert color to grayscale tone", integral_setting::boolean, false);
|
||||
integral_setting SNES::invert(config(), "snes.colorfilter.invert",
|
||||
"Invert output image colors", integral_setting::boolean, false);
|
||||
integral_setting SNES::contrast(config(), "snes.colorfilter.contrast",
|
||||
"Contrast", integral_setting::decimal, 0);
|
||||
integral_setting SNES::brightness(config(), "snes.colorfilter.brightness",
|
||||
"Brightness", integral_setting::decimal, 0);
|
||||
integral_setting SNES::gamma(config(), "snes.colorfilter.gamma",
|
||||
"Gamma", integral_setting::decimal, 80);
|
||||
|
||||
integral_setting SNES::ntsc_merge_fields(config(), "snes.ntsc_merge_fields",
|
||||
"Merge fields in NTSC video filter\n"
|
||||
"Set to true if using filter at any refresh rate other than 60hz\n"
|
||||
"", integral_setting::boolean, true);
|
||||
|
||||
integral_setting SNES::controller_port0(config(), "snes.controller_port_1",
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::DEVICEID_JOYPAD1);
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad1);
|
||||
integral_setting SNES::controller_port1(config(), "snes.controller_port_2",
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::DEVICEID_JOYPAD2);
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad2);
|
||||
|
||||
integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate",
|
||||
"NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272);
|
||||
@@ -126,4 +116,4 @@ integral_setting PPU::oam_pri1_enable("ppu.oam_pri1_enable", "Enable OAM Priorit
|
||||
integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true);
|
||||
integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true);
|
||||
|
||||
};
|
||||
} //namespace config
|
||||
|
@@ -2,16 +2,18 @@ namespace config {
|
||||
|
||||
extern configuration& config();
|
||||
|
||||
string file_updatepath(const char *, const char *);
|
||||
string file_updatepath(const char*, const char*);
|
||||
|
||||
extern struct File {
|
||||
static integral_setting autodetect_type;
|
||||
} file;
|
||||
|
||||
extern struct Path {
|
||||
static string_setting base, rom, save;
|
||||
static string_setting base, rom, save, cheat;
|
||||
static string_setting bsx, st;
|
||||
} path;
|
||||
|
||||
extern struct SNES {
|
||||
static integral_setting gamma_ramp, sepia, grayscale, invert, contrast, brightness, gamma;
|
||||
static integral_setting ntsc_merge_fields;
|
||||
static integral_setting controller_port0;
|
||||
static integral_setting controller_port1;
|
||||
} snes;
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#include "../base.h"
|
||||
#include "../base.h"
|
||||
#define CPU_CPP
|
||||
|
||||
#include "dcpu.cpp"
|
||||
|
||||
CPU::CPU() {
|
||||
|
118
src/cpu/cpu.h
118
src/cpu/cpu.h
@@ -4,81 +4,75 @@ class CPU : public MMIO {
|
||||
public:
|
||||
virtual void enter() = 0;
|
||||
|
||||
public:
|
||||
//CPU version number
|
||||
//* 1 and 2 are known
|
||||
//* reported by $4210
|
||||
//* affects DRAM refresh behavior
|
||||
uint8 cpu_version;
|
||||
//CPU version number
|
||||
//* 1 and 2 are known
|
||||
//* reported by $4210
|
||||
//* affects DRAM refresh behavior
|
||||
uint8 cpu_version;
|
||||
|
||||
//timing
|
||||
//timing
|
||||
virtual uint16 vcounter() = 0;
|
||||
virtual uint16 hcounter() = 0;
|
||||
virtual uint16 hclock() = 0;
|
||||
virtual bool interlace() = 0;
|
||||
virtual bool interlace_field() = 0;
|
||||
virtual bool overscan() = 0;
|
||||
virtual uint16 region_scanlines() = 0;
|
||||
virtual void set_interlace(bool r) = 0;
|
||||
virtual void set_overscan (bool r) = 0;
|
||||
|
||||
CPURegs regs;
|
||||
virtual uint8 port_read (uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
virtual uint16 hdot() = 0;
|
||||
|
||||
virtual uint8 pio_status() = 0;
|
||||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
CPURegs regs;
|
||||
enum {
|
||||
FLAG_N = 0x80, FLAG_V = 0x40,
|
||||
FLAG_M = 0x20, FLAG_X = 0x10,
|
||||
FLAG_D = 0x08, FLAG_I = 0x04,
|
||||
FLAG_Z = 0x02, FLAG_C = 0x01
|
||||
};
|
||||
virtual uint8 pio_status() = 0;
|
||||
virtual void scanline() = 0;
|
||||
virtual void frame() = 0;
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void scanline() = 0;
|
||||
virtual void frame() = 0;
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
/*****
|
||||
* in opcode-based CPU emulators, the main emulation routine
|
||||
* will only be able to call the disassemble_opcode() function
|
||||
* on clean opcode edges. but with cycle-based CPU emulators,
|
||||
* the CPU may be in the middle of executing an opcode when the
|
||||
* emulator (e.g. debugger) wants to disassemble an opcode. this
|
||||
* would mean that important registers may not reflect what they
|
||||
* did at the start of the opcode (especially regs.pc), so in
|
||||
* cycle-based emulators, this function should be overridden to
|
||||
* reflect whether or not an opcode has only been partially
|
||||
* executed. if not, the debugger should abort attempts to skip,
|
||||
* disable, or disassemble the current opcode.
|
||||
*****/
|
||||
/*****
|
||||
* in opcode-based CPU emulators, the main emulation routine
|
||||
* will only be able to call the disassemble_opcode() function
|
||||
* on clean opcode edges. but with cycle-based CPU emulators,
|
||||
* the CPU may be in the middle of executing an opcode when the
|
||||
* emulator (e.g. debugger) wants to disassemble an opcode. this
|
||||
* would mean that important registers may not reflect what they
|
||||
* did at the start of the opcode (especially regs.pc), so in
|
||||
* cycle-based emulators, this function should be overridden to
|
||||
* reflect whether or not an opcode has only been partially
|
||||
* executed. if not, the debugger should abort attempts to skip,
|
||||
* disable, or disassemble the current opcode.
|
||||
*****/
|
||||
virtual bool in_opcode() { return false; }
|
||||
|
||||
/*****
|
||||
* opcode disassembler
|
||||
*****/
|
||||
enum {
|
||||
OPTYPE_DP = 0, //dp
|
||||
OPTYPE_DPX, //dp,x
|
||||
OPTYPE_DPY, //dp,y
|
||||
OPTYPE_IDP, //(dp)
|
||||
OPTYPE_IDPX, //(dp,x)
|
||||
OPTYPE_IDPY, //(dp),y
|
||||
OPTYPE_ILDP, //[dp]
|
||||
OPTYPE_ILDPY, //[dp],y
|
||||
OPTYPE_ADDR, //addr
|
||||
OPTYPE_ADDRX, //addr,x
|
||||
OPTYPE_ADDRY, //addr,y
|
||||
OPTYPE_IADDRX, //(addr,x)
|
||||
OPTYPE_ILADDR, //[addr]
|
||||
OPTYPE_LONG, //long
|
||||
OPTYPE_LONGX, //long, x
|
||||
OPTYPE_SR, //sr,s
|
||||
OPTYPE_ISRY, //(sr,s),y
|
||||
OPTYPE_ADDR_PC, //pbr:addr
|
||||
OPTYPE_IADDR_PC, //pbr:(addr)
|
||||
OPTYPE_RELB, //relb
|
||||
OPTYPE_RELW, //relw
|
||||
};
|
||||
/*****
|
||||
* opcode disassembler
|
||||
*****/
|
||||
enum {
|
||||
OPTYPE_DP = 0, //dp
|
||||
OPTYPE_DPX, //dp,x
|
||||
OPTYPE_DPY, //dp,y
|
||||
OPTYPE_IDP, //(dp)
|
||||
OPTYPE_IDPX, //(dp,x)
|
||||
OPTYPE_IDPY, //(dp),y
|
||||
OPTYPE_ILDP, //[dp]
|
||||
OPTYPE_ILDPY, //[dp],y
|
||||
OPTYPE_ADDR, //addr
|
||||
OPTYPE_ADDRX, //addr,x
|
||||
OPTYPE_ADDRY, //addr,y
|
||||
OPTYPE_IADDRX, //(addr,x)
|
||||
OPTYPE_ILADDR, //[addr]
|
||||
OPTYPE_LONG, //long
|
||||
OPTYPE_LONGX, //long, x
|
||||
OPTYPE_SR, //sr,s
|
||||
OPTYPE_ISRY, //(sr,s),y
|
||||
OPTYPE_ADDR_PC, //pbr:addr
|
||||
OPTYPE_IADDR_PC, //pbr:(addr)
|
||||
OPTYPE_RELB, //relb
|
||||
OPTYPE_RELW, //relw
|
||||
};
|
||||
|
||||
void disassemble_opcode(char *output);
|
||||
uint8 dreadb(uint32 addr);
|
||||
|
@@ -1,11 +1,11 @@
|
||||
class CPURegFlags {
|
||||
public:
|
||||
union {
|
||||
uint8 data;
|
||||
struct {
|
||||
bool order_msb8(n:1, v:1, m:1, x:1, d:1, i:1, z:1, c:1);
|
||||
union {
|
||||
uint8 data;
|
||||
struct {
|
||||
bool order_msb8(n:1, v:1, m:1, x:1, d:1, i:1, z:1, c:1);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
inline operator unsigned() const { return data; }
|
||||
template<typename T> inline unsigned operator = (const T i) { data = i; return data; }
|
||||
@@ -18,10 +18,10 @@ union {
|
||||
|
||||
class CPUReg16 {
|
||||
public:
|
||||
union {
|
||||
uint16 w;
|
||||
struct { uint8 order_lsb2(l, h); };
|
||||
};
|
||||
union {
|
||||
uint16 w;
|
||||
struct { uint8 order_lsb2(l, h); };
|
||||
};
|
||||
|
||||
inline operator unsigned() const { return w; }
|
||||
template<typename T> inline unsigned operator = (const T i) { w = i; return w; }
|
||||
@@ -41,11 +41,11 @@ union {
|
||||
|
||||
class CPUReg24 {
|
||||
public:
|
||||
union {
|
||||
uint32 d;
|
||||
struct { uint16 order_lsb2(w, wh); };
|
||||
struct { uint8 order_lsb4(l, h, b, bh); };
|
||||
};
|
||||
union {
|
||||
uint32 d;
|
||||
struct { uint16 order_lsb2(w, wh); };
|
||||
struct { uint8 order_lsb4(l, h, b, bh); };
|
||||
};
|
||||
|
||||
inline operator unsigned() const { return d; }
|
||||
template<typename T> inline unsigned operator = (const T i) { d = uclip<24>(i); return d; }
|
||||
@@ -65,11 +65,11 @@ union {
|
||||
|
||||
class CPURegs {
|
||||
public:
|
||||
CPUReg24 pc;
|
||||
CPUReg16 a, x, y, s, d;
|
||||
CPURegFlags p;
|
||||
uint8 db;
|
||||
uint8 mdr;
|
||||
bool e;
|
||||
CPUReg24 pc;
|
||||
CPUReg16 a, x, y, s, d;
|
||||
CPURegFlags p;
|
||||
uint8 db;
|
||||
uint8 mdr;
|
||||
bool e;
|
||||
CPURegs() : db(0), mdr(0x00), e(false) {}
|
||||
};
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
uint8 CPU::dreadb(uint32 addr) {
|
||||
if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) {
|
||||
//$[00-3f|80-bf]:[2000-5fff]
|
||||
@@ -423,7 +425,7 @@ uint8 op2 = dreadb(pc.d);
|
||||
strcat(s, t);
|
||||
strcat(s, " ");
|
||||
|
||||
sprintf(t, "V:%3d H:%4d", vcounter(), hclock());
|
||||
sprintf(t, "V:%3d H:%4d", vcounter(), hcounter());
|
||||
strcat(s, t);
|
||||
}
|
||||
|
||||
@@ -473,3 +475,5 @@ static uint8 op_len_tbl[256] = {
|
||||
if(len == 6)return (regs.e || regs.p.x) ? 2 : 3;
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif //ifdef CPU_CPP
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
#include "opfn.cpp"
|
||||
|
||||
#include "op_read.cpp"
|
||||
@@ -43,8 +45,6 @@ void sCPU::op_irq() {
|
||||
regs.pc.w = rd.w;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
alwaysinline void sCPU::op_io_cond2() {
|
||||
if(regs.d.l != 0x00) {
|
||||
op_io();
|
||||
@@ -62,3 +62,5 @@ alwaysinline void sCPU::op_io_cond6(uint16 addr) {
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,57 +1,57 @@
|
||||
void (sCPU::*optbl[256])();
|
||||
void (sCPU::*optbl[256])();
|
||||
|
||||
CPUReg24 aa, rd;
|
||||
uint8 dp, sp;
|
||||
CPUReg24 aa, rd;
|
||||
uint8_t dp, sp;
|
||||
|
||||
void op_irq();
|
||||
void op_irq();
|
||||
|
||||
inline bool in_opcode() { return status.in_opcode; }
|
||||
|
||||
//op_read
|
||||
void op_adc_b();
|
||||
void op_adc_w();
|
||||
void op_and_b();
|
||||
void op_and_w();
|
||||
void op_bit_b();
|
||||
void op_bit_w();
|
||||
void op_cmp_b();
|
||||
void op_cmp_w();
|
||||
void op_cpx_b();
|
||||
void op_cpx_w();
|
||||
void op_cpy_b();
|
||||
void op_cpy_w();
|
||||
void op_eor_b();
|
||||
void op_eor_w();
|
||||
void op_lda_b();
|
||||
void op_lda_w();
|
||||
void op_ldx_b();
|
||||
void op_ldx_w();
|
||||
void op_ldy_b();
|
||||
void op_ldy_w();
|
||||
void op_ora_b();
|
||||
void op_ora_w();
|
||||
void op_sbc_b();
|
||||
void op_sbc_w();
|
||||
//op_rmw
|
||||
void op_inc_b();
|
||||
void op_inc_w();
|
||||
void op_dec_b();
|
||||
void op_dec_w();
|
||||
void op_asl_b();
|
||||
void op_asl_w();
|
||||
void op_lsr_b();
|
||||
void op_lsr_w();
|
||||
void op_rol_b();
|
||||
void op_rol_w();
|
||||
void op_ror_b();
|
||||
void op_ror_w();
|
||||
void op_trb_b();
|
||||
void op_trb_w();
|
||||
void op_tsb_b();
|
||||
void op_tsb_w();
|
||||
//op_read
|
||||
void op_adc_b();
|
||||
void op_adc_w();
|
||||
void op_and_b();
|
||||
void op_and_w();
|
||||
void op_bit_b();
|
||||
void op_bit_w();
|
||||
void op_cmp_b();
|
||||
void op_cmp_w();
|
||||
void op_cpx_b();
|
||||
void op_cpx_w();
|
||||
void op_cpy_b();
|
||||
void op_cpy_w();
|
||||
void op_eor_b();
|
||||
void op_eor_w();
|
||||
void op_lda_b();
|
||||
void op_lda_w();
|
||||
void op_ldx_b();
|
||||
void op_ldx_w();
|
||||
void op_ldy_b();
|
||||
void op_ldy_w();
|
||||
void op_ora_b();
|
||||
void op_ora_w();
|
||||
void op_sbc_b();
|
||||
void op_sbc_w();
|
||||
//op_rmw
|
||||
void op_inc_b();
|
||||
void op_inc_w();
|
||||
void op_dec_b();
|
||||
void op_dec_w();
|
||||
void op_asl_b();
|
||||
void op_asl_w();
|
||||
void op_lsr_b();
|
||||
void op_lsr_w();
|
||||
void op_rol_b();
|
||||
void op_rol_w();
|
||||
void op_ror_b();
|
||||
void op_ror_w();
|
||||
void op_trb_b();
|
||||
void op_trb_w();
|
||||
void op_tsb_b();
|
||||
void op_tsb_w();
|
||||
|
||||
void op_io_cond2();
|
||||
void op_io_cond4(uint16 x, uint16 y);
|
||||
void op_io_cond6(uint16 addr);
|
||||
void op_io_cond2();
|
||||
void op_io_cond4(uint16 x, uint16 y);
|
||||
void op_io_cond6(uint16 addr);
|
||||
|
||||
#include "op.h"
|
||||
#include "op.h"
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
//op_read
|
||||
inline void sCPU::op_adc_b() {
|
||||
int32 r = regs.a.l + rd.l + regs.p.c;
|
||||
@@ -371,3 +373,5 @@ inline void sCPU::op_tsb_w() {
|
||||
regs.p.z = ((rd.w & regs.a.w) == 0);
|
||||
rd.w |= regs.a.w;
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,273 +1,289 @@
|
||||
void sCPU::dma_add_clocks(uint clocks) {
|
||||
status.dma_clocks += clocks;
|
||||
add_clocks(clocks);
|
||||
}
|
||||
|
||||
/*****
|
||||
* used by both DMA and HDMA
|
||||
*
|
||||
* DMA address bus A cannot read from or write to the following addresses :
|
||||
* $[00-3f|80-bf]:43[00-7f] <DMA control registers>
|
||||
* $[00-3f|80-bf]:420b <DMA enable register>
|
||||
* $[00-3f|80-bf]:420c <HDMA enable register>
|
||||
* WRAM<>WRAM transfers via $2180
|
||||
*****/
|
||||
void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
|
||||
uint8 r;
|
||||
if(direction == 0) { //a->b
|
||||
if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 ||
|
||||
(abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
r = regs.mdr;
|
||||
} else {
|
||||
r = bus.read(abus);
|
||||
}
|
||||
bus.write(0x2100 | bbus, r);
|
||||
} else { //b->a
|
||||
if(bbus == 0x80 && ((abus & 0x7e0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//prevent WRAM->WRAM transfers
|
||||
r = regs.mdr;
|
||||
} else {
|
||||
r = bus.read(0x2100 | bbus);
|
||||
}
|
||||
if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300 ||
|
||||
(abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c)return;
|
||||
bus.write(abus, r);
|
||||
}
|
||||
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
|
||||
/*****
|
||||
* address calculation functions
|
||||
*****/
|
||||
|
||||
uint8 sCPU::dma_bbus(uint8 i, uint8 index) {
|
||||
switch(channel[i].xfermode) {
|
||||
default:
|
||||
case 0: return (channel[i].destaddr); break; //0
|
||||
case 1: return (channel[i].destaddr + (index & 1)); break; //0,1
|
||||
case 2: return (channel[i].destaddr); break; //0,0
|
||||
case 3: return (channel[i].destaddr + ((index >> 1) & 1)); break; //0,0,1,1
|
||||
case 4: return (channel[i].destaddr + (index & 3)); break; //0,1,2,3
|
||||
case 5: return (channel[i].destaddr + (index & 1)); break; //0,1,0,1
|
||||
case 6: return (channel[i].destaddr); break; //0,0 [2]
|
||||
case 7: return (channel[i].destaddr + ((index >> 1) & 1)); break; //0,0,1,1 [3]
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32 sCPU::dma_addr(uint8 i) {
|
||||
uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
|
||||
|
||||
if(channel[i].fixedxfer == false) {
|
||||
if(channel[i].reversexfer == false) {
|
||||
channel[i].srcaddr++;
|
||||
} else {
|
||||
channel[i].srcaddr--;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline uint32 sCPU::hdma_addr(uint8 i) {
|
||||
return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
|
||||
}
|
||||
|
||||
inline uint32 sCPU::hdma_iaddr(uint8 i) {
|
||||
return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
|
||||
}
|
||||
|
||||
/*****
|
||||
* DMA functions
|
||||
*****/
|
||||
|
||||
void sCPU::dma_transfertobusb(uint8 i, uint8 bbus) {
|
||||
if(cartridge.info.sdd1 == true && sdd1.dma_active() == true) {
|
||||
bus.write(0x2100 | bbus, sdd1.dma_read());
|
||||
} else {
|
||||
dma_transfer(0, bbus, dma_addr(i));
|
||||
}
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
|
||||
void sCPU::dma_transfertobusa(uint8 i, uint8 bbus) {
|
||||
dma_transfer(1, bbus, dma_addr(i));
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
|
||||
inline void sCPU::dma_write(uint8 i, uint8 index) {
|
||||
//cannot use dma_transfer() directly, due to current S-DD1 implementation
|
||||
if(channel[i].direction == 0) {
|
||||
dma_transfertobusb(i, index);
|
||||
} else {
|
||||
dma_transfertobusa(i, index);
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::dma_run() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled == false)continue;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(cartridge.info.sdd1 == true) {
|
||||
sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize);
|
||||
}
|
||||
|
||||
if(tracer.enabled() == true && tracer.cpudma_enabled() == true) {
|
||||
tprintf("[DMA] channel:%d direction:%s reverse:%c fixed:%c mode:%d b_addr:$21%0.2x "
|
||||
"a_addr:$%0.2x%0.4x length:$%0.4x (%5d)",
|
||||
i, channel[i].direction ? "b->a" : "a->b", channel[i].reversexfer ? '1' : '0',
|
||||
channel[i].fixedxfer ? '1' : '0', channel[i].xfermode, channel[i].destaddr,
|
||||
channel[i].srcbank, channel[i].srcaddr,
|
||||
channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536);
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
do {
|
||||
dma_write(i, dma_bbus(i, index++));
|
||||
} while(channel[i].dma_enabled && channel[i].xfersize);
|
||||
|
||||
channel[i].dma_enabled = false;
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
/*****
|
||||
* HDMA functions
|
||||
*****/
|
||||
|
||||
inline bool sCPU::hdma_active(uint8 i) {
|
||||
return (channel[i].hdma_enabled && !channel[i].hdma_completed);
|
||||
}
|
||||
|
||||
inline bool sCPU::hdma_active_after(uint8 i) {
|
||||
for(int n = i + 1; n < 8; n++) {
|
||||
if(hdma_active(n) == true) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint8 sCPU::hdma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(channel[i].hdma_enabled)r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline uint8 sCPU::hdma_active_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == true)r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::hdma_update(uint8 i) {
|
||||
channel[i].hdma_line_counter = bus.read(hdma_addr(i));
|
||||
dma_add_clocks(8);
|
||||
|
||||
channel[i].hdma_completed = (channel[i].hdma_line_counter == 0);
|
||||
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(!channel[i].hdma_completed || hdma_active_after(i)) {
|
||||
channel[i].hdma_iaddr >>= 8;
|
||||
channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_run() {
|
||||
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false)continue;
|
||||
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(channel[i].hdma_do_transfer) {
|
||||
int xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(int index = 0; index < xferlen; index++) {
|
||||
if(bool(config::cpu.hdma_enable) == true) {
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index),
|
||||
!channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i));
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel[i].hdma_line_counter--;
|
||||
channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80);
|
||||
if((channel[i].hdma_line_counter & 0x7f) == 0) {
|
||||
hdma_update(i);
|
||||
}
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
void sCPU::hdma_init_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_init() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled)continue;
|
||||
channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
|
||||
|
||||
channel[i].hdma_addr = channel[i].srcaddr;
|
||||
hdma_update(i);
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
/*****
|
||||
* power / reset functions
|
||||
*****/
|
||||
|
||||
void sCPU::dma_power() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dmap = 0xff;
|
||||
channel[i].direction = 1;
|
||||
channel[i].hdma_indirect = true;
|
||||
channel[i].reversexfer = true;
|
||||
channel[i].fixedxfer = true;
|
||||
channel[i].xfermode = 7;
|
||||
|
||||
channel[i].destaddr = 0xff;
|
||||
|
||||
channel[i].srcaddr = 0xffff;
|
||||
channel[i].srcbank = 0xff;
|
||||
|
||||
channel[i].xfersize = 0xffff;
|
||||
//channel[i].hdma_iaddr = 0xffff; //union with xfersize
|
||||
channel[i].hdma_ibank = 0xff;
|
||||
|
||||
channel[i].hdma_addr = 0xffff;
|
||||
channel[i].hdma_line_counter = 0xff;
|
||||
channel[i].unknown = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::dma_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = false;
|
||||
channel[i].hdma_enabled = false;
|
||||
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPU::dma_add_clocks(uint clocks) {
|
||||
status.dma_clocks += clocks;
|
||||
add_clocks(clocks);
|
||||
}
|
||||
|
||||
/*****
|
||||
* used by both DMA and HDMA
|
||||
*
|
||||
* DMA address bus A cannot read from or write to the following addresses :
|
||||
* $[00-3f|80-bf]:43[00-7f] <DMA control registers>
|
||||
* $[00-3f|80-bf]:420b <DMA enable register>
|
||||
* $[00-3f|80-bf]:420c <HDMA enable register>
|
||||
*
|
||||
* WRAM<>WRAM transfers via $2180 are also illegal
|
||||
*****/
|
||||
|
||||
void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
|
||||
if(direction == 0) {
|
||||
//a->b transfer (to $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//read most likely occurs; no write occurs
|
||||
//read is irrelevant, as it has no observable effect on emulation
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(0x2100 | bbus, regs.mdr); //TODO: verify if MDR is written here
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(0x2100 | bbus, bus.read(abus));
|
||||
}
|
||||
} else {
|
||||
//b->a transfer (from $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//no read occurs; write does occur
|
||||
//does not write MDR as expected
|
||||
//TODO: 0x00 was observed on hardware; verify if other values are possible
|
||||
bus.write(abus, 0x00);
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(abus, regs.mdr); //TODO: verify if MDR is written here
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(abus, bus.read(0x2100 | bbus));
|
||||
}
|
||||
}
|
||||
|
||||
//each byte *always* consumes 8 clocks, even if transfer is invalid and no read and/or write occurs
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
|
||||
/*****
|
||||
* address calculation functions
|
||||
*****/
|
||||
|
||||
uint8 sCPU::dma_bbus(uint8 i, uint8 index) {
|
||||
switch(channel[i].xfermode) { default:
|
||||
case 0: return (channel[i].destaddr); //0
|
||||
case 1: return (channel[i].destaddr + (index & 1)); //0,1
|
||||
case 2: return (channel[i].destaddr); //0,0
|
||||
case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1
|
||||
case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3
|
||||
case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1
|
||||
case 6: return (channel[i].destaddr); //0,0 [2]
|
||||
case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3]
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32 sCPU::dma_addr(uint8 i) {
|
||||
uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr);
|
||||
|
||||
if(channel[i].fixedxfer == false) {
|
||||
if(channel[i].reversexfer == false) {
|
||||
channel[i].srcaddr++;
|
||||
} else {
|
||||
channel[i].srcaddr--;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline uint32 sCPU::hdma_addr(uint8 i) {
|
||||
return (channel[i].srcbank << 16) | (channel[i].hdma_addr++);
|
||||
}
|
||||
|
||||
inline uint32 sCPU::hdma_iaddr(uint8 i) {
|
||||
return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++);
|
||||
}
|
||||
|
||||
/*****
|
||||
* DMA functions
|
||||
*****/
|
||||
|
||||
void sCPU::dma_transfertobusb(uint8 i, uint8 bbus) {
|
||||
if(cartridge.info.sdd1 == true && sdd1.dma_active() == true) {
|
||||
bus.write(0x2100 | bbus, sdd1.dma_read());
|
||||
} else {
|
||||
dma_transfer(0, bbus, dma_addr(i));
|
||||
}
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
|
||||
void sCPU::dma_transfertobusa(uint8 i, uint8 bbus) {
|
||||
dma_transfer(1, bbus, dma_addr(i));
|
||||
channel[i].xfersize--;
|
||||
}
|
||||
|
||||
inline void sCPU::dma_write(uint8 i, uint8 index) {
|
||||
//cannot use dma_transfer() directly, due to current S-DD1 implementation
|
||||
if(channel[i].direction == 0) {
|
||||
dma_transfertobusb(i, index);
|
||||
} else {
|
||||
dma_transfertobusa(i, index);
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::dma_run() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled == false) continue;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(cartridge.info.sdd1 == true) {
|
||||
sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize);
|
||||
}
|
||||
|
||||
if(tracer.enabled() == true && tracer.cpudma_enabled() == true) {
|
||||
tprintf("[DMA] channel:%d direction:%s reverse:%c fixed:%c mode:%d b_addr:$21%0.2x "
|
||||
"a_addr:$%0.2x%0.4x length:$%0.4x (%5d)",
|
||||
i, channel[i].direction ? "b->a" : "a->b", channel[i].reversexfer ? '1' : '0',
|
||||
channel[i].fixedxfer ? '1' : '0', channel[i].xfermode, channel[i].destaddr,
|
||||
channel[i].srcbank, channel[i].srcaddr,
|
||||
channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536);
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
do {
|
||||
dma_write(i, dma_bbus(i, index++));
|
||||
} while(channel[i].dma_enabled && channel[i].xfersize);
|
||||
|
||||
channel[i].dma_enabled = false;
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
/*****
|
||||
* HDMA functions
|
||||
*****/
|
||||
|
||||
inline bool sCPU::hdma_active(uint8 i) {
|
||||
return (channel[i].hdma_enabled && !channel[i].hdma_completed);
|
||||
}
|
||||
|
||||
inline bool sCPU::hdma_active_after(uint8 i) {
|
||||
for(int n = i + 1; n < 8; n++) {
|
||||
if(hdma_active(n) == true) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint8 sCPU::hdma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(channel[i].hdma_enabled) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline uint8 sCPU::hdma_active_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == true) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::hdma_update(uint8 i) {
|
||||
channel[i].hdma_line_counter = bus.read(hdma_addr(i));
|
||||
dma_add_clocks(8);
|
||||
|
||||
channel[i].hdma_completed = (channel[i].hdma_line_counter == 0);
|
||||
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(!channel[i].hdma_completed || hdma_active_after(i)) {
|
||||
channel[i].hdma_iaddr >>= 8;
|
||||
channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_run() {
|
||||
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false) continue;
|
||||
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(channel[i].hdma_do_transfer) {
|
||||
int xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(int index = 0; index < xferlen; index++) {
|
||||
if(bool(config::cpu.hdma_enable) == true) {
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index),
|
||||
!channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i));
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel[i].hdma_line_counter--;
|
||||
channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80);
|
||||
if((channel[i].hdma_line_counter & 0x7f) == 0) {
|
||||
hdma_update(i);
|
||||
}
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
void sCPU::hdma_init_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_init() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled)continue;
|
||||
channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
|
||||
|
||||
channel[i].hdma_addr = channel[i].srcaddr;
|
||||
hdma_update(i);
|
||||
}
|
||||
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
/*****
|
||||
* power / reset functions
|
||||
*****/
|
||||
|
||||
void sCPU::dma_power() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dmap = 0xff;
|
||||
channel[i].direction = 1;
|
||||
channel[i].hdma_indirect = true;
|
||||
channel[i].reversexfer = true;
|
||||
channel[i].fixedxfer = true;
|
||||
channel[i].xfermode = 7;
|
||||
|
||||
channel[i].destaddr = 0xff;
|
||||
|
||||
channel[i].srcaddr = 0xffff;
|
||||
channel[i].srcbank = 0xff;
|
||||
|
||||
channel[i].xfersize = 0xffff;
|
||||
//channel[i].hdma_iaddr = 0xffff; //union with xfersize
|
||||
channel[i].hdma_ibank = 0xff;
|
||||
|
||||
channel[i].hdma_addr = 0xffff;
|
||||
channel[i].hdma_line_counter = 0xff;
|
||||
channel[i].unknown = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::dma_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = false;
|
||||
channel[i].hdma_enabled = false;
|
||||
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,71 +1,71 @@
|
||||
struct {
|
||||
//$420b
|
||||
bool dma_enabled;
|
||||
struct {
|
||||
//$420b
|
||||
bool dma_enabled;
|
||||
|
||||
//$420c
|
||||
bool hdma_enabled;
|
||||
//$420c
|
||||
bool hdma_enabled;
|
||||
|
||||
//$43x0
|
||||
uint8 dmap;
|
||||
bool direction;
|
||||
bool hdma_indirect;
|
||||
bool reversexfer;
|
||||
bool fixedxfer;
|
||||
uint8 xfermode;
|
||||
//$43x0
|
||||
uint8 dmap;
|
||||
bool direction;
|
||||
bool hdma_indirect;
|
||||
bool reversexfer;
|
||||
bool fixedxfer;
|
||||
uint8 xfermode;
|
||||
|
||||
//$43x1
|
||||
uint8 destaddr;
|
||||
//$43x1
|
||||
uint8 destaddr;
|
||||
|
||||
//$43x2-$43x3
|
||||
uint16 srcaddr;
|
||||
//$43x2-$43x3
|
||||
uint16 srcaddr;
|
||||
|
||||
//$43x4
|
||||
uint8 srcbank;
|
||||
//$43x4
|
||||
uint8 srcbank;
|
||||
|
||||
//$43x5-$43x6
|
||||
union {
|
||||
uint16 xfersize;
|
||||
uint16 hdma_iaddr;
|
||||
};
|
||||
//$43x5-$43x6
|
||||
union {
|
||||
uint16 xfersize;
|
||||
uint16 hdma_iaddr;
|
||||
};
|
||||
|
||||
//$43x7
|
||||
uint8 hdma_ibank;
|
||||
//$43x7
|
||||
uint8 hdma_ibank;
|
||||
|
||||
//$43x8-$43x9
|
||||
uint16 hdma_addr;
|
||||
//$43x8-$43x9
|
||||
uint16 hdma_addr;
|
||||
|
||||
//$43xa
|
||||
uint8 hdma_line_counter;
|
||||
//$43xa
|
||||
uint8 hdma_line_counter;
|
||||
|
||||
//$43xb/$43xf
|
||||
uint8 unknown;
|
||||
//$43xb/$43xf
|
||||
uint8 unknown;
|
||||
|
||||
//internal variables
|
||||
bool hdma_completed;
|
||||
bool hdma_do_transfer;
|
||||
} channel[8];
|
||||
//internal variables
|
||||
bool hdma_completed;
|
||||
bool hdma_do_transfer;
|
||||
} channel[8];
|
||||
|
||||
void dma_add_clocks(uint clocks);
|
||||
void dma_transfer(bool direction, uint8 bbus, uint32 abus);
|
||||
void dma_add_clocks(uint clocks);
|
||||
void dma_transfer(bool direction, uint8 bbus, uint32 abus);
|
||||
|
||||
uint8 dma_bbus(uint8 i, uint8 index);
|
||||
uint8 dma_bbus(uint8 i, uint8 index);
|
||||
uint32 dma_addr(uint8 i);
|
||||
uint32 hdma_addr(uint8 i);
|
||||
uint32 hdma_iaddr(uint8 i);
|
||||
|
||||
void dma_transfertobusb(uint8 i, uint8 bbus);
|
||||
void dma_transfertobusa(uint8 i, uint8 bbus);
|
||||
void dma_write(uint8 i, uint8 index);
|
||||
void dma_run();
|
||||
void dma_transfertobusb(uint8 i, uint8 bbus);
|
||||
void dma_transfertobusa(uint8 i, uint8 bbus);
|
||||
void dma_write(uint8 i, uint8 index);
|
||||
void dma_run();
|
||||
|
||||
bool hdma_active(uint8 i);
|
||||
bool hdma_active_after(uint8 i);
|
||||
uint8 hdma_enabled_channels();
|
||||
uint8 hdma_active_channels();
|
||||
void hdma_update(uint8 i);
|
||||
void hdma_run();
|
||||
void hdma_init_reset();
|
||||
void hdma_init();
|
||||
bool hdma_active(uint8 i);
|
||||
bool hdma_active_after(uint8 i);
|
||||
uint8 hdma_enabled_channels();
|
||||
uint8 hdma_active_channels();
|
||||
void hdma_update(uint8 i);
|
||||
void hdma_run();
|
||||
void hdma_init_reset();
|
||||
void hdma_init();
|
||||
|
||||
void dma_power();
|
||||
void dma_reset();
|
||||
void dma_power();
|
||||
void dma_reset();
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
/*****
|
||||
* These 3 functions control bus timing for the CPU.
|
||||
* cpu_io is an I/O cycle, and always 6 clock cycles long.
|
||||
@@ -119,3 +121,5 @@ alwaysinline void sCPU::op_writedp(uint32 addr, uint8 data) {
|
||||
alwaysinline void sCPU::op_writesp(uint32 addr, uint8 data) {
|
||||
op_write((regs.s + (addr & 0xffff)) & 0xffff, data);
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,35 +1,35 @@
|
||||
/*****
|
||||
* CPU<>APU communication ports
|
||||
*****/
|
||||
uint8 apu_port[4];
|
||||
uint8 port_read (uint8 port) { return apu_port[port & 3]; }
|
||||
void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; }
|
||||
/*****
|
||||
* CPU<>APU communication ports
|
||||
*****/
|
||||
uint8 apu_port[4];
|
||||
uint8 port_read(uint8 port) { return apu_port[port & 3]; }
|
||||
void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; }
|
||||
|
||||
/*****
|
||||
* core CPU bus functions
|
||||
*****/
|
||||
void op_io();
|
||||
uint8 op_read (uint32 addr);
|
||||
void op_write(uint32 addr, uint8 data);
|
||||
/*****
|
||||
* core CPU bus functions
|
||||
*****/
|
||||
void op_io();
|
||||
uint8 op_read(uint32 addr);
|
||||
void op_write(uint32 addr, uint8 data);
|
||||
|
||||
/*****
|
||||
* helper memory addressing functions used by CPU core
|
||||
*****/
|
||||
uint8 op_readpc ();
|
||||
uint8 op_readstack ();
|
||||
uint8 op_readstackn();
|
||||
uint8 op_readaddr (uint32 addr);
|
||||
uint8 op_readlong (uint32 addr);
|
||||
uint8 op_readdbr (uint32 addr);
|
||||
uint8 op_readpbr (uint32 addr);
|
||||
uint8 op_readdp (uint32 addr);
|
||||
uint8 op_readsp (uint32 addr);
|
||||
/*****
|
||||
* helper memory addressing functions used by CPU core
|
||||
*****/
|
||||
uint8 op_readpc ();
|
||||
uint8 op_readstack ();
|
||||
uint8 op_readstackn();
|
||||
uint8 op_readaddr (uint32 addr);
|
||||
uint8 op_readlong (uint32 addr);
|
||||
uint8 op_readdbr (uint32 addr);
|
||||
uint8 op_readpbr (uint32 addr);
|
||||
uint8 op_readdp (uint32 addr);
|
||||
uint8 op_readsp (uint32 addr);
|
||||
|
||||
void op_writestack (uint8 data);
|
||||
void op_writestackn(uint8 data);
|
||||
void op_writeaddr (uint32 addr, uint8 data);
|
||||
void op_writelong (uint32 addr, uint8 data);
|
||||
void op_writedbr (uint32 addr, uint8 data);
|
||||
void op_writepbr (uint32 addr, uint8 data);
|
||||
void op_writedp (uint32 addr, uint8 data);
|
||||
void op_writesp (uint32 addr, uint8 data);
|
||||
void op_writestack (uint8 data);
|
||||
void op_writestackn(uint8 data);
|
||||
void op_writeaddr (uint32 addr, uint8 data);
|
||||
void op_writelong (uint32 addr, uint8 data);
|
||||
void op_writedbr (uint32 addr, uint8 data);
|
||||
void op_writepbr (uint32 addr, uint8 data);
|
||||
void op_writedp (uint32 addr, uint8 data);
|
||||
void op_writesp (uint32 addr, uint8 data);
|
||||
|
@@ -1,10 +1,12 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
uint8 sCPU::pio_status() {
|
||||
return status.pio;
|
||||
}
|
||||
|
||||
//WMDATA
|
||||
uint8 sCPU::mmio_r2180() {
|
||||
uint8 r = bus.read(0x7e0000 | status.wram_addr);
|
||||
uint8 r = bus.read(0x7e0000 | status.wram_addr);
|
||||
status.wram_addr = (status.wram_addr + 1) & 0x01ffff;
|
||||
return r;
|
||||
}
|
||||
@@ -41,7 +43,7 @@ void sCPU::mmio_w4016(uint8 data) {
|
||||
status.joypad_strobe_latch = !!(data & 1);
|
||||
|
||||
if(status.joypad_strobe_latch == 1) {
|
||||
snes.poll_input();
|
||||
snes.input.poll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +54,8 @@ void sCPU::mmio_w4016(uint8 data) {
|
||||
//TODO: test whether strobe latch of zero returns
|
||||
//realtime or buffered status of joypadN.b
|
||||
uint8 sCPU::mmio_r4016() {
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= (uint8)snes.port_read(0);
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= (uint8)snes.input.port_read(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -62,8 +64,8 @@ uint8 r = regs.mdr & 0xfc;
|
||||
//4-2 = Always 1 (pins are connected to GND)
|
||||
//1-0 = Joypad serial data
|
||||
uint8 sCPU::mmio_r4017() {
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= (uint8)snes.port_read(1);
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= (uint8)snes.input.port_read(1);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -167,7 +169,7 @@ void sCPU::mmio_w420d(uint8 data) {
|
||||
//6-4 = MDR
|
||||
//3-0 = CPU (5a22) version
|
||||
uint8 sCPU::mmio_r4210() {
|
||||
uint8 r = (regs.mdr & 0x70);
|
||||
uint8 r = (regs.mdr & 0x70);
|
||||
r |= (uint8)(rdnmi()) << 7;
|
||||
r |= (cpu_version & 0x0f);
|
||||
return r;
|
||||
@@ -177,7 +179,7 @@ uint8 r = (regs.mdr & 0x70);
|
||||
//7 = IRQ acknowledge
|
||||
//6-0 = MDR
|
||||
uint8 sCPU::mmio_r4211() {
|
||||
uint8 r = (regs.mdr & 0x7f);
|
||||
uint8 r = (regs.mdr & 0x7f);
|
||||
r |= (uint8)(timeup()) << 7;
|
||||
return r;
|
||||
}
|
||||
@@ -188,16 +190,16 @@ uint8 r = (regs.mdr & 0x7f);
|
||||
//5-1 = MDR
|
||||
//0 = JOYPAD acknowledge
|
||||
uint8 sCPU::mmio_r4212() {
|
||||
uint8 r = (regs.mdr & 0x3e);
|
||||
uint16 vs = !overscan() ? 225 : 240;
|
||||
uint8 r = (regs.mdr & 0x3e);
|
||||
uint16 vs = ppu.overscan() == false ? 225 : 240;
|
||||
|
||||
//auto joypad polling
|
||||
//auto joypad polling
|
||||
if(status.vcounter >= vs && status.vcounter <= (vs + 2))r |= 0x01;
|
||||
|
||||
//hblank
|
||||
if(status.hclock <= 2 || status.hclock >= 1096)r |= 0x40;
|
||||
//hblank
|
||||
if(status.hcounter <= 2 || status.hcounter >= 1096)r |= 0x40;
|
||||
|
||||
//vblank
|
||||
//vblank
|
||||
if(status.vcounter >= vs)r |= 0x80;
|
||||
|
||||
return r;
|
||||
@@ -375,40 +377,40 @@ void sCPU::mmio_power() {
|
||||
}
|
||||
|
||||
void sCPU::mmio_reset() {
|
||||
//$2181-$2183
|
||||
//$2181-$2183
|
||||
status.wram_addr = 0x000000;
|
||||
|
||||
//$4016-$4017
|
||||
//$4016-$4017
|
||||
status.joypad_strobe_latch = 0;
|
||||
status.joypad1_bits = ~0;
|
||||
status.joypad2_bits = ~0;
|
||||
|
||||
//$4200
|
||||
//$4200
|
||||
status.nmi_enabled = false;
|
||||
status.hirq_enabled = false;
|
||||
status.virq_enabled = false;
|
||||
status.auto_joypad_poll = false;
|
||||
|
||||
//$4201
|
||||
//$4201
|
||||
status.pio = 0xff;
|
||||
|
||||
//$4202-$4203
|
||||
//$4202-$4203
|
||||
status.mul_a = 0xff;
|
||||
status.mul_b = 0xff;
|
||||
|
||||
//$4204-$4206
|
||||
//$4204-$4206
|
||||
status.div_a = 0xffff;
|
||||
status.div_b = 0xff;
|
||||
|
||||
//$4207-$420a
|
||||
//$4207-$420a
|
||||
status.hirq_pos = 0x01ff;
|
||||
status.virq_pos = 0x01ff;
|
||||
|
||||
//$4214-$4217
|
||||
//$4214-$4217
|
||||
status.r4214 = 0x0000;
|
||||
status.r4216 = 0x0000;
|
||||
|
||||
//$4218-$421f
|
||||
//$4218-$421f
|
||||
status.joy1l = 0x00;
|
||||
status.joy1h = 0x00;
|
||||
status.joy2l = 0x00;
|
||||
@@ -422,55 +424,55 @@ void sCPU::mmio_reset() {
|
||||
uint8 sCPU::mmio_read(uint addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
//APU
|
||||
//APU
|
||||
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
|
||||
scheduler.sync_cpusmp();
|
||||
return smp.port_read(addr & 3);
|
||||
}
|
||||
|
||||
//DMA
|
||||
//DMA
|
||||
if((addr & 0xff80) == 0x4300) { //$4300-$437f
|
||||
uint i = (addr >> 4) & 7;
|
||||
uint i = (addr >> 4) & 7;
|
||||
switch(addr & 0xf) {
|
||||
case 0x0: return mmio_r43x0(i);
|
||||
case 0x1: return mmio_r43x1(i);
|
||||
case 0x2: return mmio_r43x2(i);
|
||||
case 0x3: return mmio_r43x3(i);
|
||||
case 0x4: return mmio_r43x4(i);
|
||||
case 0x5: return mmio_r43x5(i);
|
||||
case 0x6: return mmio_r43x6(i);
|
||||
case 0x7: return mmio_r43x7(i);
|
||||
case 0x8: return mmio_r43x8(i);
|
||||
case 0x9: return mmio_r43x9(i);
|
||||
case 0xa: return mmio_r43xa(i);
|
||||
case 0xb: return mmio_r43xb(i);
|
||||
case 0xc: return regs.mdr; //unmapped
|
||||
case 0xd: return regs.mdr; //unmapped
|
||||
case 0xe: return regs.mdr; //unmapped
|
||||
case 0xf: return mmio_r43xb(i); //mirror of $43xb
|
||||
case 0x0: return mmio_r43x0(i);
|
||||
case 0x1: return mmio_r43x1(i);
|
||||
case 0x2: return mmio_r43x2(i);
|
||||
case 0x3: return mmio_r43x3(i);
|
||||
case 0x4: return mmio_r43x4(i);
|
||||
case 0x5: return mmio_r43x5(i);
|
||||
case 0x6: return mmio_r43x6(i);
|
||||
case 0x7: return mmio_r43x7(i);
|
||||
case 0x8: return mmio_r43x8(i);
|
||||
case 0x9: return mmio_r43x9(i);
|
||||
case 0xa: return mmio_r43xa(i);
|
||||
case 0xb: return mmio_r43xb(i);
|
||||
case 0xc: return regs.mdr; //unmapped
|
||||
case 0xd: return regs.mdr; //unmapped
|
||||
case 0xe: return regs.mdr; //unmapped
|
||||
case 0xf: return mmio_r43xb(i); //mirror of $43xb
|
||||
}
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x2180: return mmio_r2180();
|
||||
case 0x4016: return mmio_r4016();
|
||||
case 0x4017: return mmio_r4017();
|
||||
case 0x4210: return mmio_r4210();
|
||||
case 0x4211: return mmio_r4211();
|
||||
case 0x4212: return mmio_r4212();
|
||||
case 0x4213: return mmio_r4213();
|
||||
case 0x4214: return mmio_r4214();
|
||||
case 0x4215: return mmio_r4215();
|
||||
case 0x4216: return mmio_r4216();
|
||||
case 0x4217: return mmio_r4217();
|
||||
case 0x4218: return mmio_r4218();
|
||||
case 0x4219: return mmio_r4219();
|
||||
case 0x421a: return mmio_r421a();
|
||||
case 0x421b: return mmio_r421b();
|
||||
case 0x421c: return mmio_r421c();
|
||||
case 0x421d: return mmio_r421d();
|
||||
case 0x421e: return mmio_r421e();
|
||||
case 0x421f: return mmio_r421f();
|
||||
case 0x2180: return mmio_r2180();
|
||||
case 0x4016: return mmio_r4016();
|
||||
case 0x4017: return mmio_r4017();
|
||||
case 0x4210: return mmio_r4210();
|
||||
case 0x4211: return mmio_r4211();
|
||||
case 0x4212: return mmio_r4212();
|
||||
case 0x4213: return mmio_r4213();
|
||||
case 0x4214: return mmio_r4214();
|
||||
case 0x4215: return mmio_r4215();
|
||||
case 0x4216: return mmio_r4216();
|
||||
case 0x4217: return mmio_r4217();
|
||||
case 0x4218: return mmio_r4218();
|
||||
case 0x4219: return mmio_r4219();
|
||||
case 0x421a: return mmio_r421a();
|
||||
case 0x421b: return mmio_r421b();
|
||||
case 0x421c: return mmio_r421c();
|
||||
case 0x421d: return mmio_r421d();
|
||||
case 0x421e: return mmio_r421e();
|
||||
case 0x421f: return mmio_r421f();
|
||||
}
|
||||
|
||||
return regs.mdr;
|
||||
@@ -479,56 +481,58 @@ uint8 sCPU::mmio_read(uint addr) {
|
||||
void sCPU::mmio_write(uint addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
//APU
|
||||
//APU
|
||||
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
|
||||
scheduler.sync_cpusmp();
|
||||
port_write(addr & 3, data);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA
|
||||
//DMA
|
||||
if((addr & 0xff80) == 0x4300) { //$4300-$437f
|
||||
uint i = (addr >> 4) & 7;
|
||||
switch(addr & 0xf) {
|
||||
case 0x0: mmio_w43x0(i, data); return;
|
||||
case 0x1: mmio_w43x1(i, data); return;
|
||||
case 0x2: mmio_w43x2(i, data); return;
|
||||
case 0x3: mmio_w43x3(i, data); return;
|
||||
case 0x4: mmio_w43x4(i, data); return;
|
||||
case 0x5: mmio_w43x5(i, data); return;
|
||||
case 0x6: mmio_w43x6(i, data); return;
|
||||
case 0x7: mmio_w43x7(i, data); return;
|
||||
case 0x8: mmio_w43x8(i, data); return;
|
||||
case 0x9: mmio_w43x9(i, data); return;
|
||||
case 0xa: mmio_w43xa(i, data); return;
|
||||
case 0xb: mmio_w43xb(i, data); return;
|
||||
case 0xc: return; //unmapped
|
||||
case 0xd: return; //unmapped
|
||||
case 0xe: return; //unmapped
|
||||
case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb
|
||||
case 0x0: mmio_w43x0(i, data); return;
|
||||
case 0x1: mmio_w43x1(i, data); return;
|
||||
case 0x2: mmio_w43x2(i, data); return;
|
||||
case 0x3: mmio_w43x3(i, data); return;
|
||||
case 0x4: mmio_w43x4(i, data); return;
|
||||
case 0x5: mmio_w43x5(i, data); return;
|
||||
case 0x6: mmio_w43x6(i, data); return;
|
||||
case 0x7: mmio_w43x7(i, data); return;
|
||||
case 0x8: mmio_w43x8(i, data); return;
|
||||
case 0x9: mmio_w43x9(i, data); return;
|
||||
case 0xa: mmio_w43xa(i, data); return;
|
||||
case 0xb: mmio_w43xb(i, data); return;
|
||||
case 0xc: return; //unmapped
|
||||
case 0xd: return; //unmapped
|
||||
case 0xe: return; //unmapped
|
||||
case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb
|
||||
}
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x2180: mmio_w2180(data); return;
|
||||
case 0x2181: mmio_w2181(data); return;
|
||||
case 0x2182: mmio_w2182(data); return;
|
||||
case 0x2183: mmio_w2183(data); return;
|
||||
case 0x4016: mmio_w4016(data); return;
|
||||
case 0x4017: return; //unmapped
|
||||
case 0x4200: mmio_w4200(data); return;
|
||||
case 0x4201: mmio_w4201(data); return;
|
||||
case 0x4202: mmio_w4202(data); return;
|
||||
case 0x4203: mmio_w4203(data); return;
|
||||
case 0x4204: mmio_w4204(data); return;
|
||||
case 0x4205: mmio_w4205(data); return;
|
||||
case 0x4206: mmio_w4206(data); return;
|
||||
case 0x4207: mmio_w4207(data); return;
|
||||
case 0x4208: mmio_w4208(data); return;
|
||||
case 0x4209: mmio_w4209(data); return;
|
||||
case 0x420a: mmio_w420a(data); return;
|
||||
case 0x420b: mmio_w420b(data); return;
|
||||
case 0x420c: mmio_w420c(data); return;
|
||||
case 0x420d: mmio_w420d(data); return;
|
||||
case 0x2180: mmio_w2180(data); return;
|
||||
case 0x2181: mmio_w2181(data); return;
|
||||
case 0x2182: mmio_w2182(data); return;
|
||||
case 0x2183: mmio_w2183(data); return;
|
||||
case 0x4016: mmio_w4016(data); return;
|
||||
case 0x4017: return; //unmapped
|
||||
case 0x4200: mmio_w4200(data); return;
|
||||
case 0x4201: mmio_w4201(data); return;
|
||||
case 0x4202: mmio_w4202(data); return;
|
||||
case 0x4203: mmio_w4203(data); return;
|
||||
case 0x4204: mmio_w4204(data); return;
|
||||
case 0x4205: mmio_w4205(data); return;
|
||||
case 0x4206: mmio_w4206(data); return;
|
||||
case 0x4207: mmio_w4207(data); return;
|
||||
case 0x4208: mmio_w4208(data); return;
|
||||
case 0x4209: mmio_w4209(data); return;
|
||||
case 0x420a: mmio_w420a(data); return;
|
||||
case 0x420b: mmio_w420b(data); return;
|
||||
case 0x420c: mmio_w420c(data); return;
|
||||
case 0x420d: mmio_w420d(data); return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,70 +1,70 @@
|
||||
void mmio_power();
|
||||
void mmio_reset();
|
||||
uint8 mmio_read (uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
void mmio_power();
|
||||
void mmio_reset();
|
||||
uint8 mmio_read(uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
uint8 pio_status();
|
||||
uint8 pio_status();
|
||||
|
||||
uint8 mmio_r2180();
|
||||
uint8 mmio_r4016();
|
||||
uint8 mmio_r4017();
|
||||
uint8 mmio_r4210();
|
||||
uint8 mmio_r4211();
|
||||
uint8 mmio_r4212();
|
||||
uint8 mmio_r4213();
|
||||
uint8 mmio_r4214();
|
||||
uint8 mmio_r4215();
|
||||
uint8 mmio_r4216();
|
||||
uint8 mmio_r4217();
|
||||
uint8 mmio_r4218();
|
||||
uint8 mmio_r4219();
|
||||
uint8 mmio_r421a();
|
||||
uint8 mmio_r421b();
|
||||
uint8 mmio_r421c();
|
||||
uint8 mmio_r421d();
|
||||
uint8 mmio_r421e();
|
||||
uint8 mmio_r421f();
|
||||
uint8 mmio_r43x0(uint8 i);
|
||||
uint8 mmio_r43x1(uint8 i);
|
||||
uint8 mmio_r43x2(uint8 i);
|
||||
uint8 mmio_r43x3(uint8 i);
|
||||
uint8 mmio_r43x4(uint8 i);
|
||||
uint8 mmio_r43x5(uint8 i);
|
||||
uint8 mmio_r43x6(uint8 i);
|
||||
uint8 mmio_r43x7(uint8 i);
|
||||
uint8 mmio_r43x8(uint8 i);
|
||||
uint8 mmio_r43x9(uint8 i);
|
||||
uint8 mmio_r43xa(uint8 i);
|
||||
uint8 mmio_r43xb(uint8 i);
|
||||
uint8 mmio_r2180();
|
||||
uint8 mmio_r4016();
|
||||
uint8 mmio_r4017();
|
||||
uint8 mmio_r4210();
|
||||
uint8 mmio_r4211();
|
||||
uint8 mmio_r4212();
|
||||
uint8 mmio_r4213();
|
||||
uint8 mmio_r4214();
|
||||
uint8 mmio_r4215();
|
||||
uint8 mmio_r4216();
|
||||
uint8 mmio_r4217();
|
||||
uint8 mmio_r4218();
|
||||
uint8 mmio_r4219();
|
||||
uint8 mmio_r421a();
|
||||
uint8 mmio_r421b();
|
||||
uint8 mmio_r421c();
|
||||
uint8 mmio_r421d();
|
||||
uint8 mmio_r421e();
|
||||
uint8 mmio_r421f();
|
||||
uint8 mmio_r43x0(uint8 i);
|
||||
uint8 mmio_r43x1(uint8 i);
|
||||
uint8 mmio_r43x2(uint8 i);
|
||||
uint8 mmio_r43x3(uint8 i);
|
||||
uint8 mmio_r43x4(uint8 i);
|
||||
uint8 mmio_r43x5(uint8 i);
|
||||
uint8 mmio_r43x6(uint8 i);
|
||||
uint8 mmio_r43x7(uint8 i);
|
||||
uint8 mmio_r43x8(uint8 i);
|
||||
uint8 mmio_r43x9(uint8 i);
|
||||
uint8 mmio_r43xa(uint8 i);
|
||||
uint8 mmio_r43xb(uint8 i);
|
||||
|
||||
void mmio_w2180(uint8 data);
|
||||
void mmio_w2181(uint8 data);
|
||||
void mmio_w2182(uint8 data);
|
||||
void mmio_w2183(uint8 data);
|
||||
void mmio_w4016(uint8 data);
|
||||
void mmio_w4200(uint8 data);
|
||||
void mmio_w4201(uint8 data);
|
||||
void mmio_w4202(uint8 data);
|
||||
void mmio_w4203(uint8 data);
|
||||
void mmio_w4204(uint8 data);
|
||||
void mmio_w4205(uint8 data);
|
||||
void mmio_w4206(uint8 data);
|
||||
void mmio_w4207(uint8 data);
|
||||
void mmio_w4208(uint8 data);
|
||||
void mmio_w4209(uint8 data);
|
||||
void mmio_w420a(uint8 data);
|
||||
void mmio_w420b(uint8 data);
|
||||
void mmio_w420c(uint8 data);
|
||||
void mmio_w420d(uint8 data);
|
||||
void mmio_w43x0(uint8 i, uint8 data);
|
||||
void mmio_w43x1(uint8 i, uint8 data);
|
||||
void mmio_w43x2(uint8 i, uint8 data);
|
||||
void mmio_w43x3(uint8 i, uint8 data);
|
||||
void mmio_w43x4(uint8 i, uint8 data);
|
||||
void mmio_w43x5(uint8 i, uint8 data);
|
||||
void mmio_w43x6(uint8 i, uint8 data);
|
||||
void mmio_w43x7(uint8 i, uint8 data);
|
||||
void mmio_w43x8(uint8 i, uint8 data);
|
||||
void mmio_w43x9(uint8 i, uint8 data);
|
||||
void mmio_w43xa(uint8 i, uint8 data);
|
||||
void mmio_w43xb(uint8 i, uint8 data);
|
||||
void mmio_w2180(uint8 data);
|
||||
void mmio_w2181(uint8 data);
|
||||
void mmio_w2182(uint8 data);
|
||||
void mmio_w2183(uint8 data);
|
||||
void mmio_w4016(uint8 data);
|
||||
void mmio_w4200(uint8 data);
|
||||
void mmio_w4201(uint8 data);
|
||||
void mmio_w4202(uint8 data);
|
||||
void mmio_w4203(uint8 data);
|
||||
void mmio_w4204(uint8 data);
|
||||
void mmio_w4205(uint8 data);
|
||||
void mmio_w4206(uint8 data);
|
||||
void mmio_w4207(uint8 data);
|
||||
void mmio_w4208(uint8 data);
|
||||
void mmio_w4209(uint8 data);
|
||||
void mmio_w420a(uint8 data);
|
||||
void mmio_w420b(uint8 data);
|
||||
void mmio_w420c(uint8 data);
|
||||
void mmio_w420d(uint8 data);
|
||||
void mmio_w43x0(uint8 i, uint8 data);
|
||||
void mmio_w43x1(uint8 i, uint8 data);
|
||||
void mmio_w43x2(uint8 i, uint8 data);
|
||||
void mmio_w43x3(uint8 i, uint8 data);
|
||||
void mmio_w43x4(uint8 i, uint8 data);
|
||||
void mmio_w43x5(uint8 i, uint8 data);
|
||||
void mmio_w43x6(uint8 i, uint8 data);
|
||||
void mmio_w43x7(uint8 i, uint8 data);
|
||||
void mmio_w43x8(uint8 i, uint8 data);
|
||||
void mmio_w43x9(uint8 i, uint8 data);
|
||||
void mmio_w43xa(uint8 i, uint8 data);
|
||||
void mmio_w43xb(uint8 i, uint8 data);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define SCPU_CPP
|
||||
|
||||
#include "core/core.cpp"
|
||||
#include "dma/dma.cpp"
|
||||
@@ -7,8 +8,6 @@
|
||||
#include "timing/timing.cpp"
|
||||
|
||||
void sCPU::power() {
|
||||
status.region = (bool)snes.region();
|
||||
|
||||
regs.a = regs.x = regs.y = 0x0000;
|
||||
regs.s = 0x01ff;
|
||||
|
||||
@@ -24,7 +23,7 @@ void sCPU::reset() {
|
||||
regs.pc.l = bus.read(0xfffc);
|
||||
regs.pc.h = bus.read(0xfffd);
|
||||
|
||||
//note: some registers are not fully reset by SNES
|
||||
//note: some registers are not fully reset by SNES
|
||||
regs.x.h = 0x00;
|
||||
regs.y.h = 0x00;
|
||||
regs.s.h = 0x01;
|
||||
|
@@ -1,138 +1,132 @@
|
||||
class sCPU : public CPU { public:
|
||||
class sCPU : public CPU {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
#include "core/core.h"
|
||||
#include "dma/dma.h"
|
||||
#include "memory/memory.h"
|
||||
#include "mmio/mmio.h"
|
||||
#include "timing/timing.h"
|
||||
#include "core/core.h"
|
||||
#include "dma/dma.h"
|
||||
#include "memory/memory.h"
|
||||
#include "mmio/mmio.h"
|
||||
#include "timing/timing.h"
|
||||
|
||||
struct {
|
||||
bool wai;
|
||||
bool irq;
|
||||
uint16 irq_vector;
|
||||
} event;
|
||||
struct {
|
||||
bool wai;
|
||||
bool irq;
|
||||
uint16 irq_vector;
|
||||
} event;
|
||||
|
||||
struct {
|
||||
uint nmi_hold;
|
||||
uint irq_hold;
|
||||
struct {
|
||||
uint nmi_hold;
|
||||
uint irq_hold;
|
||||
|
||||
uint nmi_fire;
|
||||
uint irq_fire;
|
||||
uint irq_delay;
|
||||
uint hw_math;
|
||||
uint nmi_fire;
|
||||
uint irq_fire;
|
||||
uint irq_delay;
|
||||
uint hw_math;
|
||||
|
||||
alwaysinline void set(uint &ctr, uint clocks) {
|
||||
if(clocks >= ctr) { ctr = clocks; }
|
||||
}
|
||||
|
||||
alwaysinline void sub(uint &ctr, uint clocks) {
|
||||
if(ctr >= clocks) {
|
||||
ctr -= clocks;
|
||||
} else {
|
||||
ctr = 0;
|
||||
alwaysinline void set(uint &ctr, uint clocks) {
|
||||
if(clocks >= ctr) { ctr = clocks; }
|
||||
}
|
||||
}
|
||||
} counter;
|
||||
|
||||
enum {
|
||||
DMASTATE_INACTIVE,
|
||||
DMASTATE_DMASYNC,
|
||||
DMASTATE_RUN,
|
||||
DMASTATE_CPUSYNC,
|
||||
};
|
||||
alwaysinline void sub(uint &ctr, uint clocks) {
|
||||
if(ctr >= clocks) {
|
||||
ctr -= clocks;
|
||||
} else {
|
||||
ctr = 0;
|
||||
}
|
||||
}
|
||||
} counter;
|
||||
|
||||
struct {
|
||||
//core
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
enum {
|
||||
DMASTATE_INACTIVE,
|
||||
DMASTATE_DMASYNC,
|
||||
DMASTATE_RUN,
|
||||
DMASTATE_CPUSYNC,
|
||||
};
|
||||
|
||||
uint clock_count;
|
||||
struct {
|
||||
//core
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
|
||||
//timing
|
||||
bool region;
|
||||
uint16 region_scanlines;
|
||||
uint16 vcounter, hcounter, hclock;
|
||||
bool interlace, interlace_field;
|
||||
bool overscan;
|
||||
uint16 field_lines, line_clocks;
|
||||
uint16 prev_field_lines, prev_line_clocks;
|
||||
uint16 vblstart;
|
||||
uint clock_count;
|
||||
|
||||
bool line_rendered;
|
||||
uint16 line_render_position;
|
||||
//timing
|
||||
uint16 vcounter, hcounter;
|
||||
uint16 field_lines, line_clocks;
|
||||
|
||||
bool dram_refreshed;
|
||||
uint16 dram_refresh_position;
|
||||
bool line_rendered;
|
||||
uint16 line_render_position;
|
||||
|
||||
bool hdmainit_triggered;
|
||||
uint16 hdmainit_trigger_position;
|
||||
bool dram_refreshed;
|
||||
uint16 dram_refresh_position;
|
||||
|
||||
bool hdma_triggered;
|
||||
bool hdmainit_triggered;
|
||||
uint16 hdmainit_trigger_position;
|
||||
|
||||
uint16 irq_delay;
|
||||
bool hdma_triggered;
|
||||
|
||||
uint16 vnmi_trigger_pos;
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
uint16 irq_delay;
|
||||
|
||||
uint16 virq_trigger_pos, hirq_trigger_pos;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
|
||||
//dma
|
||||
uint dma_counter;
|
||||
uint dma_clocks;
|
||||
uint dma_state;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdmainit_pending;
|
||||
uint16 virq_trigger_pos, hirq_trigger_pos;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
|
||||
//mmio
|
||||
//dma
|
||||
uint dma_counter;
|
||||
uint dma_clocks;
|
||||
uint dma_state;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdmainit_pending;
|
||||
|
||||
//$2181-$2183
|
||||
uint32 wram_addr;
|
||||
//mmio
|
||||
|
||||
//$4016-$4017
|
||||
bool joypad_strobe_latch;
|
||||
uint32 joypad1_bits;
|
||||
uint32 joypad2_bits;
|
||||
//$2181-$2183
|
||||
uint32 wram_addr;
|
||||
|
||||
//$4200
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
//$4016-$4017
|
||||
bool joypad_strobe_latch;
|
||||
uint32 joypad1_bits;
|
||||
uint32 joypad2_bits;
|
||||
|
||||
//$4201
|
||||
uint8 pio;
|
||||
//$4200
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
|
||||
//$4202-$4203
|
||||
uint8 mul_a, mul_b;
|
||||
//$4201
|
||||
uint8 pio;
|
||||
|
||||
//$4204-$4206
|
||||
uint16 div_a;
|
||||
uint8 div_b;
|
||||
//$4202-$4203
|
||||
uint8 mul_a, mul_b;
|
||||
|
||||
//$4207-$420a
|
||||
uint16 hirq_pos, virq_pos;
|
||||
//$4204-$4206
|
||||
uint16 div_a;
|
||||
uint8 div_b;
|
||||
|
||||
//$4214-$4217
|
||||
uint16 r4214;
|
||||
uint16 r4216;
|
||||
//$4207-$420a
|
||||
uint16 hirq_pos, virq_pos;
|
||||
|
||||
//$4218-$421f
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
} status;
|
||||
//$4214-$4217
|
||||
uint16 r4214;
|
||||
uint16 r4216;
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
//$4218-$421f
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
} status;
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
sCPU();
|
||||
~sCPU();
|
||||
|
@@ -1,60 +1,137 @@
|
||||
#include "irqtiming.cpp"
|
||||
|
||||
bool sCPU::irq_pos_valid() {
|
||||
uint vpos = status.virq_pos;
|
||||
uint hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
|
||||
uint vlimit = region_scanlines() >> 1;
|
||||
//positions that can never be latched
|
||||
//vlimit = 262/NTSC, 312/PAL
|
||||
//PAL results are unverified on hardware
|
||||
if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)return false;
|
||||
if(vpos == (vlimit - 1) && hpos == 339 && interlace() == false)return false;
|
||||
if(vpos == vlimit && interlace() == false)return false;
|
||||
if(vpos == vlimit && hpos == 339)return false;
|
||||
if(vpos > vlimit)return false;
|
||||
if(hpos > 339)return false;
|
||||
|
||||
return true;
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPU::update_interrupts() {
|
||||
if(irq_pos_valid() == true) {
|
||||
status.virq_trigger_pos = status.virq_pos;
|
||||
status.hirq_trigger_pos = 4 * ((status.hirq_enabled) ? (status.hirq_pos + 1) : 0);
|
||||
} else {
|
||||
status.virq_trigger_pos = IRQ_TRIGGER_NEVER;
|
||||
status.hirq_trigger_pos = IRQ_TRIGGER_NEVER;
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
bool sCPU::nmi_test() {
|
||||
if(status.nmi_transition == false) { return false; }
|
||||
status.nmi_transition = false;
|
||||
alwaysinline void sCPU::poll_interrupts() {
|
||||
uint16_t vpos, hpos;
|
||||
|
||||
event.wai = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
bool sCPU::irq_test() {
|
||||
if(status.irq_transition == false) { return false; }
|
||||
status.irq_transition = false;
|
||||
|
||||
event.wai = false;
|
||||
return (regs.p.i) ? false : true;
|
||||
}
|
||||
|
||||
/*
|
||||
if(status.irq_transition == 1)goto irq_trigger;
|
||||
|
||||
if(status.irq_read == 0) {
|
||||
if(status.irq_line == 1 && irq_edge()) {
|
||||
return false;
|
||||
//NMI hold
|
||||
if(counter.nmi_hold) {
|
||||
counter.nmi_hold -= 2;
|
||||
if(counter.nmi_hold == 0) {
|
||||
if(status.nmi_enabled == true) status.nmi_transition = true;
|
||||
}
|
||||
goto irq_trigger;
|
||||
}
|
||||
|
||||
if(status.irq_line == 0) {
|
||||
status.irq_line = 1;
|
||||
goto irq_trigger;
|
||||
//NMI test
|
||||
history.query(2, vpos, hpos);
|
||||
bool nmi_valid = (vpos >= (!ppu.overscan() ? 225 : 240));
|
||||
if(status.nmi_valid == false && nmi_valid == true) {
|
||||
//0->1 edge sensitive transition
|
||||
status.nmi_line = true;
|
||||
counter.nmi_hold = 4;
|
||||
} else if(status.nmi_valid == true && nmi_valid == false) {
|
||||
//1->0 edge sensitive transition
|
||||
status.nmi_line = false;
|
||||
}
|
||||
status.nmi_valid = nmi_valid;
|
||||
|
||||
//IRQ hold
|
||||
if(counter.irq_hold) counter.irq_hold -= 2;
|
||||
if(status.irq_line == true && counter.irq_hold == 0) {
|
||||
if(status.virq_enabled == true || status.hirq_enabled == true) status.irq_transition = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
irq_trigger:
|
||||
status.irq_transition = 0;
|
||||
event.wai = false;
|
||||
return (regs.p.i) ? false : true;
|
||||
//IRQ test
|
||||
history.query(10, vpos, hpos);
|
||||
bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true);
|
||||
if(irq_valid == true) {
|
||||
if(status.virq_enabled == true && vpos != status.virq_trigger_pos) irq_valid = false;
|
||||
if(status.hirq_enabled == true && hpos != status.hirq_trigger_pos) irq_valid = false;
|
||||
}
|
||||
if(status.irq_valid == false && irq_valid == true) {
|
||||
//0->1 edge sensitive transition
|
||||
status.irq_line = true;
|
||||
counter.irq_hold = 4;
|
||||
}
|
||||
status.irq_valid = irq_valid;
|
||||
}
|
||||
*/
|
||||
|
||||
void sCPU::nmitimen_update(uint8 data) {
|
||||
bool nmi_enabled = status.nmi_enabled;
|
||||
bool virq_enabled = status.virq_enabled;
|
||||
bool hirq_enabled = status.hirq_enabled;
|
||||
status.nmi_enabled = !!(data & 0x80);
|
||||
status.virq_enabled = !!(data & 0x20);
|
||||
status.hirq_enabled = !!(data & 0x10);
|
||||
|
||||
//0->1 edge sensitive transition
|
||||
if(nmi_enabled == false && status.nmi_enabled == true && status.nmi_line == true) {
|
||||
status.nmi_transition = true;
|
||||
}
|
||||
|
||||
//?->1 level sensitive transition
|
||||
if(status.virq_enabled == true && status.hirq_enabled == false && status.irq_line == true) {
|
||||
status.irq_transition = true;
|
||||
}
|
||||
|
||||
if(status.virq_enabled == false && status.hirq_enabled == false) {
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
}
|
||||
|
||||
update_interrupts();
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
void sCPU::hvtime_update(uint16 addr) {
|
||||
update_interrupts();
|
||||
}
|
||||
|
||||
bool sCPU::rdnmi() {
|
||||
bool result = status.nmi_line;
|
||||
if(counter.nmi_hold == 0) {
|
||||
status.nmi_line = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sCPU::timeup() {
|
||||
bool result = status.irq_line;
|
||||
if(counter.irq_hold == 0) {
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sCPU::irq_pos_valid() {
|
||||
uint vpos = status.virq_pos;
|
||||
uint hpos = (status.hirq_enabled) ? status.hirq_pos : 0;
|
||||
uint vlimit = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
//positions that can never be latched
|
||||
//vlimit = 262/NTSC, 312/PAL
|
||||
//PAL results are unverified on hardware
|
||||
if(vpos == 240 && hpos == 339 && ppu.interlace() == false && ppu.field() == 1) return false;
|
||||
if(vpos == (vlimit - 1) && hpos == 339 && ppu.interlace() == false) return false;
|
||||
if(vpos == vlimit && ppu.interlace() == false) return false;
|
||||
if(vpos == vlimit && hpos == 339) return false;
|
||||
if(vpos > vlimit) return false;
|
||||
if(hpos > 339) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
alwaysinline bool sCPU::nmi_test() {
|
||||
if(status.nmi_transition == false) return false;
|
||||
status.nmi_transition = false;
|
||||
event.wai = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
alwaysinline bool sCPU::irq_test() {
|
||||
if(status.irq_transition == false) return false;
|
||||
status.irq_transition = false;
|
||||
event.wai = false;
|
||||
return regs.p.i ? false : true;
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,105 +0,0 @@
|
||||
void sCPU::update_interrupts() {
|
||||
status.vnmi_trigger_pos = status.vblstart;
|
||||
|
||||
if(irq_pos_valid() == true) {
|
||||
status.virq_trigger_pos = status.virq_pos;
|
||||
status.hirq_trigger_pos = 4 * ((status.hirq_enabled) ? (status.hirq_pos + 1) : 0);
|
||||
} else {
|
||||
status.virq_trigger_pos = IRQ_TRIGGER_NEVER;
|
||||
status.hirq_trigger_pos = IRQ_TRIGGER_NEVER;
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
void sCPU::poll_interrupts() {
|
||||
uint vpos = status.vcounter, hpos = status.hclock;
|
||||
|
||||
//NMI test
|
||||
timeshift_backward(2, vpos, hpos);
|
||||
bool nmi_valid = (vpos >= status.vnmi_trigger_pos);
|
||||
if(status.nmi_valid == false && nmi_valid == true) {
|
||||
//0->1 edge sensitive transition
|
||||
status.nmi_line = true;
|
||||
counter.nmi_hold = 6;
|
||||
} else if(status.nmi_valid == true && nmi_valid == false) {
|
||||
//1->0 edge sensitive transition
|
||||
status.nmi_line = false;
|
||||
}
|
||||
status.nmi_valid = nmi_valid;
|
||||
|
||||
//NMI hold
|
||||
if(counter.nmi_hold) {
|
||||
counter.nmi_hold -= 2;
|
||||
if(counter.nmi_hold == 0) {
|
||||
if(status.nmi_enabled == true) { status.nmi_transition = true; }
|
||||
}
|
||||
}
|
||||
|
||||
//IRQ test
|
||||
timeshift_backward(8, vpos, hpos);
|
||||
bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true);
|
||||
if(irq_valid == true) {
|
||||
if(status.virq_enabled == true && vpos != status.virq_trigger_pos) { irq_valid = false; }
|
||||
if(status.hirq_enabled == true && hpos != status.hirq_trigger_pos) { irq_valid = false; }
|
||||
}
|
||||
if(status.irq_valid == false && irq_valid == true) {
|
||||
//0->1 edge sensitive transition
|
||||
status.irq_line = true;
|
||||
counter.irq_hold = 6;
|
||||
}
|
||||
status.irq_valid = irq_valid;
|
||||
|
||||
//IRQ hold
|
||||
if(counter.irq_hold) { counter.irq_hold -= 2; }
|
||||
if(status.irq_line == true && counter.irq_hold == 0) {
|
||||
if(status.virq_enabled == true || status.hirq_enabled == true) { status.irq_transition = true; }
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::nmitimen_update(uint8 data) {
|
||||
bool nmi_enabled = status.nmi_enabled;
|
||||
bool virq_enabled = status.virq_enabled;
|
||||
bool hirq_enabled = status.hirq_enabled;
|
||||
status.nmi_enabled = !!(data & 0x80);
|
||||
status.virq_enabled = !!(data & 0x20);
|
||||
status.hirq_enabled = !!(data & 0x10);
|
||||
|
||||
//0->1 edge sensitive transition
|
||||
if(nmi_enabled == false && status.nmi_enabled == true && status.nmi_line == true) {
|
||||
status.nmi_transition = true;
|
||||
}
|
||||
|
||||
//?->1 level sensitive transition
|
||||
if(status.virq_enabled == true && status.hirq_enabled == false && status.irq_line == true) {
|
||||
status.irq_transition = true;
|
||||
}
|
||||
|
||||
if(status.virq_enabled == false && status.hirq_enabled == false) {
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
}
|
||||
|
||||
update_interrupts();
|
||||
counter.set(counter.irq_delay, 2);
|
||||
}
|
||||
|
||||
void sCPU::hvtime_update(uint16 addr) {
|
||||
update_interrupts();
|
||||
}
|
||||
|
||||
bool sCPU::rdnmi() {
|
||||
bool result = status.nmi_line;
|
||||
if(counter.nmi_hold == 0) {
|
||||
status.nmi_line = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sCPU::timeup() {
|
||||
bool result = status.irq_line;
|
||||
if(counter.irq_hold == 0) {
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
}
|
||||
return result;
|
||||
}
|
@@ -1,8 +1,10 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPU::run_auto_joypad_poll() {
|
||||
uint16 joy1 = 0, joy2 = 0;
|
||||
for(int i = 0; i < 16; i++) {
|
||||
joy1 |= (uint16)snes.port_read(0) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (uint16)snes.port_read(1) ? (0x8000 >> i) : 0;
|
||||
uint16_t joy1 = 0, joy2 = 0;
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
joy1 |= (uint16_t)snes.input.port_read(0) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (uint16_t)snes.input.port_read(1) ? (0x8000 >> i) : 0;
|
||||
}
|
||||
|
||||
status.joy1l = joy1;
|
||||
@@ -17,3 +19,5 @@ uint16 joy1 = 0, joy2 = 0;
|
||||
status.joy4l = 0x00;
|
||||
status.joy4h = 0x00;
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,22 +0,0 @@
|
||||
alwaysinline void sCPU::timeshift_forward(uint clocks, uint &vtime, uint &htime) {
|
||||
htime += clocks;
|
||||
if(htime >= status.line_clocks) {
|
||||
htime -= status.line_clocks;
|
||||
if(++vtime >= status.field_lines) {
|
||||
vtime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline void sCPU::timeshift_backward(uint clocks, uint &vtime, uint &htime) {
|
||||
if(htime >= clocks) {
|
||||
htime -= clocks;
|
||||
} else {
|
||||
htime += status.prev_line_clocks - clocks;
|
||||
if(vtime > 0) {
|
||||
vtime--;
|
||||
} else {
|
||||
vtime = status.prev_field_lines - 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,31 +1,16 @@
|
||||
#define ntsc_color_burst_phase_shift_scanline() \
|
||||
(status.region == SNES::NTSC && status.vcounter == 240 && \
|
||||
status.interlace == false && status.interlace_field == 1)
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||
ppu.interlace() == false && ppu.field() == 1 \
|
||||
)
|
||||
|
||||
#include "timeshift.cpp"
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
|
||||
uint16 sCPU::vcounter() { return status.vcounter; }
|
||||
uint16 sCPU::hclock() { return status.hclock; }
|
||||
|
||||
bool sCPU::interlace() { return status.interlace; }
|
||||
bool sCPU::interlace_field() { return status.interlace_field; }
|
||||
bool sCPU::overscan() { return status.overscan; }
|
||||
uint16 sCPU::region_scanlines() { return status.region_scanlines; }
|
||||
|
||||
uint sCPU::dma_counter() { return (status.dma_counter + status.hclock) & 7; }
|
||||
|
||||
void sCPU::set_interlace(bool r) {
|
||||
status.interlace = r;
|
||||
update_interrupts();
|
||||
}
|
||||
|
||||
void sCPU::set_overscan (bool r) {
|
||||
status.overscan = r;
|
||||
status.vblstart = (status.overscan == false) ? 225 : 240;
|
||||
update_interrupts();
|
||||
}
|
||||
uint16 sCPU::vcounter() { return status.vcounter; }
|
||||
uint16 sCPU::hcounter() { return status.hcounter; }
|
||||
uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
|
||||
|
||||
/*****
|
||||
* One PPU dot = 4 CPU clocks
|
||||
@@ -37,16 +22,14 @@ void sCPU::set_overscan (bool r) {
|
||||
* Dot 323 range = { 1292, 1294, 1296 }
|
||||
* Dot 327 range = { 1310, 1312, 1314 }
|
||||
*****/
|
||||
uint16 sCPU::hcounter() {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == true) {
|
||||
return (status.hclock >> 2);
|
||||
}
|
||||
return (status.hclock - ((status.hclock > 1292) << 1) - ((status.hclock > 1310) << 1)) >> 2;
|
||||
uint16 sCPU::hdot() {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||
}
|
||||
|
||||
void sCPU::add_clocks(uint clocks) {
|
||||
if(status.dram_refreshed == false) {
|
||||
if(status.hclock + clocks >= status.dram_refresh_position) {
|
||||
if(status.hcounter + clocks >= status.dram_refresh_position) {
|
||||
status.dram_refreshed = true;
|
||||
clocks += 40;
|
||||
}
|
||||
@@ -56,25 +39,21 @@ void sCPU::add_clocks(uint clocks) {
|
||||
scheduler.addclocks_cpu(clocks);
|
||||
|
||||
clocks >>= 1;
|
||||
while(clocks--) {
|
||||
status.hclock += 2;
|
||||
if(status.hclock >= status.line_clocks) { scanline(); }
|
||||
while(clocks--) {
|
||||
history.enqueue(status.vcounter, status.hcounter);
|
||||
status.hcounter += 2;
|
||||
if(status.hcounter >= status.line_clocks) scanline();
|
||||
poll_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::scanline() {
|
||||
status.hclock = 0;
|
||||
status.hcounter = 0;
|
||||
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
|
||||
|
||||
if(++status.vcounter >= status.field_lines) {
|
||||
frame();
|
||||
}
|
||||
|
||||
status.prev_line_clocks = status.line_clocks;
|
||||
if(++status.vcounter >= status.field_lines) frame();
|
||||
status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360;
|
||||
|
||||
//dram refresh occurs once every scanline
|
||||
//dram refresh occurs once every scanline
|
||||
status.dram_refreshed = false;
|
||||
if(cpu_version == 2) {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == false) {
|
||||
@@ -86,29 +65,30 @@ void sCPU::scanline() {
|
||||
}
|
||||
}
|
||||
|
||||
//hdma triggers once every visible scanline
|
||||
//hdma triggers once every visible scanline
|
||||
status.line_rendered = false;
|
||||
status.hdma_triggered = (status.vcounter <= (!overscan() ? 224 : 239)) ? false : true;
|
||||
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
|
||||
|
||||
ppu.scanline();
|
||||
snes.scanline();
|
||||
|
||||
update_interrupts();
|
||||
|
||||
if(status.auto_joypad_poll == true && status.vcounter == (!overscan() ? 227 : 242)) {
|
||||
snes.poll_input();
|
||||
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
|
||||
snes.input.poll();
|
||||
run_auto_joypad_poll();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::frame() {
|
||||
void sCPU::frame() {
|
||||
ppu.frame();
|
||||
snes.frame();
|
||||
|
||||
status.vcounter = 0;
|
||||
status.interlace_field ^= 1;
|
||||
status.prev_field_lines = status.field_lines;
|
||||
status.field_lines = (status.region_scanlines >> 1);
|
||||
//interlaced even fields have one extra scanline
|
||||
//(263+262=525 NTSC, 313+312=625 PAL)
|
||||
if(status.interlace == true && status.interlace_field == 0)status.field_lines++;
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
//interlaced even fields have one extra scanline
|
||||
//(263+262=525 NTSC, 313+312=625 PAL)
|
||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
if(cpu_version == 1) {
|
||||
@@ -116,9 +96,6 @@ void sCPU::frame() {
|
||||
} else {
|
||||
status.hdmainit_trigger_position = 12 + dma_counter();
|
||||
}
|
||||
|
||||
ppu.frame();
|
||||
snes.frame();
|
||||
}
|
||||
|
||||
/*****
|
||||
@@ -129,7 +106,7 @@ void sCPU::frame() {
|
||||
alwaysinline void sCPU::precycle_edge() {
|
||||
if(status.dma_state == DMASTATE_CPUSYNC) {
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
uint n = status.clock_count - (status.dma_clocks % status.clock_count);
|
||||
uint n = status.clock_count - (status.dma_clocks % status.clock_count);
|
||||
add_clocks(n ? n : status.clock_count);
|
||||
}
|
||||
}
|
||||
@@ -141,34 +118,33 @@ alwaysinline void sCPU::precycle_edge() {
|
||||
*****/
|
||||
void sCPU::cycle_edge() {
|
||||
if(status.line_rendered == false) {
|
||||
if(status.hclock >= status.line_render_position) {
|
||||
if(status.hcounter >= status.line_render_position) {
|
||||
status.line_rendered = true;
|
||||
ppu.render_scanline();
|
||||
}
|
||||
}
|
||||
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_INACTIVE:
|
||||
break;
|
||||
case DMASTATE_INACTIVE: break;
|
||||
|
||||
case DMASTATE_DMASYNC:
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
break;
|
||||
case DMASTATE_DMASYNC: {
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
} break;
|
||||
|
||||
case DMASTATE_RUN:
|
||||
status.dma_state = DMASTATE_CPUSYNC;
|
||||
status.dma_clocks = 8 - dma_counter() + 8;
|
||||
add_clocks(status.dma_clocks);
|
||||
case DMASTATE_RUN: {
|
||||
status.dma_state = DMASTATE_CPUSYNC;
|
||||
status.dma_clocks = 8 - dma_counter() + 8;
|
||||
add_clocks(status.dma_clocks);
|
||||
|
||||
if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; }
|
||||
if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; }
|
||||
if(status.dma_pending) { dma_run(); status.dma_pending = false; }
|
||||
if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; }
|
||||
if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; }
|
||||
if(status.dma_pending) { dma_run(); status.dma_pending = false; }
|
||||
|
||||
break;
|
||||
} break;
|
||||
}
|
||||
|
||||
if(status.hdmainit_triggered == false) {
|
||||
if(status.hclock >= status.hdmainit_trigger_position || status.vcounter) {
|
||||
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
|
||||
status.hdmainit_triggered = true;
|
||||
hdma_init_reset();
|
||||
if(hdma_enabled_channels()) {
|
||||
@@ -185,7 +161,7 @@ void sCPU::cycle_edge() {
|
||||
}
|
||||
|
||||
if(status.hdma_triggered == false) {
|
||||
if(status.hclock >= 1106) {
|
||||
if(status.hcounter >= 1106) {
|
||||
status.hdma_triggered = true;
|
||||
if(hdma_active_channels()) {
|
||||
add_clocks(18);
|
||||
@@ -211,7 +187,7 @@ void sCPU::cycle_edge() {
|
||||
* trigger during certain events (immediately after DMA, writes to $4200, etc)
|
||||
*****/
|
||||
void sCPU::last_cycle() {
|
||||
if(counter.irq_delay) { return; }
|
||||
if(counter.irq_delay) return;
|
||||
|
||||
status.nmi_pending |= nmi_test();
|
||||
status.irq_pending |= irq_test();
|
||||
@@ -235,27 +211,17 @@ void sCPU::timing_reset() {
|
||||
|
||||
status.vcounter = 0;
|
||||
status.hcounter = 0;
|
||||
status.hclock = 0;
|
||||
|
||||
status.interlace = 0;
|
||||
status.interlace_field = 0;
|
||||
status.overscan = false;
|
||||
status.region_scanlines = (status.region == SNES::NTSC) ? 525 : 625;
|
||||
status.vblstart = 225;
|
||||
|
||||
status.field_lines = status.region_scanlines >> 1;
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
status.line_clocks = 1364;
|
||||
|
||||
status.prev_field_lines = status.region_scanlines >> 1;
|
||||
status.prev_line_clocks = 1364;
|
||||
|
||||
status.line_rendered = false;
|
||||
status.line_rendered = false;
|
||||
status.line_render_position = min(1112U, (uint)config::ppu.hack.render_scanline_position);
|
||||
|
||||
status.dram_refreshed = false;
|
||||
status.dram_refreshed = false;
|
||||
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
status.hdmainit_triggered = false;
|
||||
status.hdmainit_trigger_position = 0;
|
||||
|
||||
status.hdma_triggered = false;
|
||||
@@ -278,12 +244,16 @@ void sCPU::timing_reset() {
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdmainit_pending = false;
|
||||
status.hdmainit_pending = false;
|
||||
|
||||
history.reset();
|
||||
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
//add_clocks(186);
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
add_clocks(186);
|
||||
}
|
||||
|
||||
#undef ntsc_color_burst_phase_shift_scanline
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
@@ -1,45 +1,57 @@
|
||||
uint16 vcounter();
|
||||
uint16 hcounter();
|
||||
uint16 hclock();
|
||||
uint16 hdot();
|
||||
uint dma_counter();
|
||||
|
||||
bool interlace();
|
||||
bool interlace_field();
|
||||
bool overscan();
|
||||
uint16 region_scanlines();
|
||||
void add_clocks(uint clocks);
|
||||
void scanline();
|
||||
void frame();
|
||||
|
||||
void set_interlace(bool r);
|
||||
void set_overscan(bool r);
|
||||
|
||||
uint dma_counter();
|
||||
|
||||
void add_clocks(uint clocks);
|
||||
void scanline();
|
||||
void frame();
|
||||
|
||||
void precycle_edge();
|
||||
void cycle_edge();
|
||||
void last_cycle();
|
||||
void precycle_edge();
|
||||
void cycle_edge();
|
||||
void last_cycle();
|
||||
uint32 clocks_executed();
|
||||
|
||||
void timing_power();
|
||||
void timing_reset();
|
||||
void timing_power();
|
||||
void timing_reset();
|
||||
|
||||
//timeshifting -- needed by NMI and IRQ timing
|
||||
struct History {
|
||||
struct Time {
|
||||
uint16 vcounter;
|
||||
uint16 hcounter;
|
||||
} time[32];
|
||||
unsigned index;
|
||||
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
|
||||
Time &t = time[index++];
|
||||
index &= 31;
|
||||
t.vcounter = vcounter;
|
||||
t.hcounter = hcounter;
|
||||
}
|
||||
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
|
||||
Time &t = time[(index - (offset >> 1)) & 31];
|
||||
vcounter = t.vcounter;
|
||||
hcounter = t.hcounter;
|
||||
}
|
||||
void reset() {
|
||||
index = 0;
|
||||
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
|
||||
}
|
||||
History() { reset(); }
|
||||
} history;
|
||||
|
||||
//timeshift.cpp
|
||||
void timeshift_forward (uint clocks, uint &v, uint &h);
|
||||
void timeshift_backward(uint clocks, uint &v, uint &h);
|
||||
//irq.cpp
|
||||
enum { IRQ_TRIGGER_NEVER = 0x3fff };
|
||||
void update_interrupts();
|
||||
void poll_interrupts();
|
||||
void nmitimen_update(uint8 data);
|
||||
void hvtime_update(uint16 addr);
|
||||
bool rdnmi();
|
||||
bool timeup();
|
||||
|
||||
//irq.cpp
|
||||
enum { IRQ_TRIGGER_NEVER = 0x3fff };
|
||||
void update_interrupts();
|
||||
void poll_interrupts();
|
||||
void nmitimen_update(uint8 data);
|
||||
void hvtime_update(uint16 addr);
|
||||
bool rdnmi();
|
||||
bool timeup();
|
||||
bool irq_pos_valid();
|
||||
bool nmi_test();
|
||||
bool irq_test();
|
||||
|
||||
bool irq_pos_valid();
|
||||
bool nmi_test();
|
||||
bool irq_test();
|
||||
|
||||
//joypad.cpp
|
||||
void run_auto_joypad_poll();
|
||||
//joypad.cpp
|
||||
void run_auto_joypad_poll();
|
||||
|
1013
src/data/controller.h
Normal file
1013
src/data/controller.h
Normal file
File diff suppressed because it is too large
Load Diff
40
src/data/icon48.h
Normal file
40
src/data/icon48.h
Normal file
@@ -0,0 +1,40 @@
|
||||
static char enc_icon48[] = {
|
||||
"_gAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHw"
|
||||
"AfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB"
|
||||
"8AHwAfD_AfAB8AHwAfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHw"
|
||||
"AfAB8AHwAfD_AfAB8AHwAfAB8AHwAfABgFD_oRYPBAA1BABWVQQAWwQAQQQAGgQA"
|
||||
"Av8u8AHwAfAB8AHwAfAB8AHwVbSAAQQALQQAsAQA9FUEAP8EwPsEAM4EAFP9BAAE"
|
||||
"QvAB8AHwAfAB8AHwqwHwvOAEBACFBAD5uPDVBOD-BAC9BAAUSvAB8L8B8AHwAfAB"
|
||||
"8AHwvGADBACuoqjwBPAEwNsEABZS8H8B8AHwAfAB8AHwAfDAIHuvnPAE8ATwBCDH"
|
||||
"BAAFVvC_AfAB8AHwAfAB8LzAIAQA3vaY8ATwBPAEYGZa8AHw3wHwAfAB8AHwwMCX"
|
||||
"kPAE8OsE8ASg3wQABl7wAfAB8K8B8AHwAfC8QAYEAOeQ8PcE8ATw0OMvYvAB8AHw"
|
||||
"AfDXAfAB8MBAHAQA_JDwBPALBPAE4GRi8AAAPz9U_wEEAA0EAB0EAB59BAAPBABE"
|
||||
"9gHwAfDA8BVVBABxBAB3BAChUET9p5zwBPAE8BaGuMALBACqbAQAzgQA9AQA-wQA"
|
||||
"qvwEAPYEANYEAHoEAL4SlvAB8AHwAfAEhjIEAF68FEKo8ATwBFCBcIFHtQQA5AQA"
|
||||
"_wTwBKDuBAB-XDQQjvAB8AHwAfAQhmhHzPYE8ARgpQ5YwEBor2xBsPAE8AQQ_QQA"
|
||||
"hFTyfwHwAfAB8MjA-Bgg8wTQowAQ-_-rACMuNrz_SrzwBPAE8MQgZ4LwbwHwAfAB"
|
||||
"8BDJkbTwBMCnAAfLfV-0ETU6fP_mnPAE8ATwBCAkExxfhvAB8AHwAfDEgBEEAO0D"
|
||||
"uPAEQKQO_f-tAIBCLDX_cTw-mPA3BPAE8ASAlYLwAUDNAKoBBAAFBAAUBAAiBAB-"
|
||||
"JQhAEBAYEETz5PYE8KgABZxKRfIENju8_9eQ8ATwBPAEoOq0wKoDBAAsBACGBADM"
|
||||
"BADq7AQA_AQA_gSAEBAYEOrKBACDBAAqBABMo6gAFkC88AQgrvgCBy83wP8VPj7_"
|
||||
"-JDwBPCvBPAIs7yAuBDEoED_BPBXBPAEQDAQwAQAKcCAHRf8S7hQMBl_uFk4O_--"
|
||||
"KYzwBPAE8ATwvEBhBABe95zwBPAE8ARg9gQAW1HAIJ8XFKwA8uhIkFUEAB-sxhkE"
|
||||
"AH0EAJL1BADDBADznPAE8ATwwADozQBIBAD5kPAE8ATwAQTg-ADQACn_nrwYAshD"
|
||||
"pPUB8NAQEQQA3nGgRqjwBPDAkOKI8ATw9wTwBPAEIM8UAwHwAfAB8HvIEPQYzKjw"
|
||||
"BPAEQDQS0t-I8ATwBPAE8ARAeprwAfB3AfDEYLAZyqzwBPAEAMX1cAAvBADxiPAE"
|
||||
"8ATwBOB68AQAGZbwAfAB8MSgHK0EAPGw8ASg_gQAScBDvj4URZTwBPAE8AQw6gQA"
|
||||
"RjwN9mwJ__8IBAAT_QQAGgRADBAUEET2xHDgE6u48ARwuwQAA0CFGQQAup0EAPOc"
|
||||
"8ATwBIDyBACgmADLABe0gAkEAKpFBACaBADSBADsBABa-QQA-wRADBDtBADTVQQA"
|
||||
"nQQASAQACsDAGK0EAPW48AQg3QQAFfbwVcggEQQAUwQAoRxD6b0EAPRcJNwwEBAY"
|
||||
"EJ8EAKpRBAAQuIAGBABuBAC66AQA_wTwBPAEgOoEAEpzBAAHSxBBOFgryF-88BQg"
|
||||
"LHEB8NDQBgQAC10EAA0IQLj-vDAQBADFr5jwBPAE8ASgywQAFMRA6pm4gOcEAHAE"
|
||||
"AOAgAfCvAfAB8AHwvEACBAC1jPAvBPAE8ATwBAC_VBBDMtT_TrAAeQQAPAQAYKLf"
|
||||
"AfAB8AHwAfDAQBnYQozw1wTwBPAE8P0EAB1p8AHwrwHwAfAB8MDgGAQA-ojw7wTw"
|
||||
"BPAE8AQA_AQA3PsB8N8B8AHwAfDA8CQVsYjwBPD3BPAE8AQAulz1AfAB8AHw1wHw"
|
||||
"AfDEQA0EAL2M8ATw9wTwBKAEFBFh8AHwAfAB8FcB8AHwxIAFBABoBADjXQQA_pzw"
|
||||
"BPAEgOUEAG3_hCUB8AHwAfAB8AHwAfAB8KvIEFwXPQQAkgQAz8BGXveYQwQQDBAU"
|
||||
"ENAEAJT9BABB9CYB8AHwAfAB8AHwrwHwAfAB8NBwBAQADtgl_QQwDxRA5PIB8AHw"
|
||||
"AfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHwAfAB"
|
||||
"8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHw_wHwAfAB8AHwAfAB8AHwAfD_AfAB8AHw"
|
||||
"AfAB8AHwAfAB8P8B8AHwAfAB8AHwAfAB8AHwfwHwAfAB8AHwAfAB8AGA"
|
||||
};
|
@@ -1,4 +1,6 @@
|
||||
#include "../../base.h"
|
||||
#include "../../base.h"
|
||||
#define ADSP_CPP
|
||||
|
||||
#include "adsp_tables.cpp"
|
||||
|
||||
void aDSP::enter() { loop:
|
||||
@@ -578,8 +580,8 @@ int32 fir_samplel, fir_sampler;
|
||||
msampler = sclamp<16>(msampler);
|
||||
}
|
||||
|
||||
snes.audio_update(msamplel, msampler);
|
||||
scheduler.addclocks_dsp(32 * 3);
|
||||
snes.audio.update(msamplel, msampler);
|
||||
scheduler.addclocks_dsp(32 * 3 * 8);
|
||||
}
|
||||
|
||||
aDSP::aDSP() {}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef ADSP_CPP
|
||||
|
||||
const uint16 aDSP::rate_table[32] = {
|
||||
0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C,
|
||||
0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180,
|
||||
@@ -71,3 +73,5 @@ const int16 aDSP::gaussian_table[512] = {
|
||||
0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517,
|
||||
0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519
|
||||
};
|
||||
|
||||
#endif //ifdef ADSP_CPP
|
||||
|
@@ -469,7 +469,7 @@ void bDSP::enter()
|
||||
{
|
||||
// n is currently ignored
|
||||
#define NEXT_CLOCK( n ) \
|
||||
scheduler.addclocks_dsp( 3 );
|
||||
scheduler.addclocks_dsp( 3 * 8 );
|
||||
|
||||
// Execute clock for a particular voice
|
||||
#define V( clock, voice ) voice_##clock( &m.voices [voice] );
|
||||
@@ -563,7 +563,7 @@ void bDSP::enter()
|
||||
}
|
||||
|
||||
// Output sample to DAC
|
||||
snes.audio_update( main_out_l, main_out_r );
|
||||
snes.audio.update( main_out_l, main_out_r );
|
||||
|
||||
m.t_main_out [0] = 0;
|
||||
m.t_main_out [1] = 0;
|
||||
|
@@ -1,11 +1,12 @@
|
||||
class DSP { public:
|
||||
virtual void enter() = 0;
|
||||
class DSP {
|
||||
public:
|
||||
virtual void enter() = 0;
|
||||
|
||||
virtual uint8 read (uint8 addr) = 0;
|
||||
virtual void write(uint8 addr, uint8 data) = 0;
|
||||
virtual uint8 read(uint8 addr) = 0;
|
||||
virtual void write(uint8 addr, uint8 data) = 0;
|
||||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
DSP() {}
|
||||
virtual ~DSP() {}
|
||||
|
63
src/dsp/sdsp/brr.cpp
Normal file
63
src/dsp/sdsp/brr.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
void sDSP::brr_decode(voice_t &v) {
|
||||
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
|
||||
int nybbles = (state.t_brr_byte << 8) + ram[(uint16)(v.brr_addr + v.brr_offset + 1)];
|
||||
|
||||
const int filter = (state.t_brr_header >> 2) & 3;
|
||||
const int scale = (state.t_brr_header >> 4);
|
||||
|
||||
//write to next four samples in circular buffer
|
||||
int *pos = &v.buf[v.buf_pos];
|
||||
v.buf_pos += 4;
|
||||
if(v.buf_pos >= brr_buf_size) v.buf_pos = 0;
|
||||
|
||||
//decode four samples
|
||||
for(int *end = pos + 4; pos < end; pos++) {
|
||||
int s = sclip<4>(nybbles >> 12); //extract upper nybble and sign extend
|
||||
nybbles <<= 4; //advance nybble position
|
||||
if(scale <= 12) {
|
||||
s <<= scale;
|
||||
s >>= 1;
|
||||
} else {
|
||||
s &= ~0x7ff;
|
||||
}
|
||||
|
||||
//apply IIR filter (2 is the most commonly used)
|
||||
const int p1 = pos[brr_buf_size - 1];
|
||||
const int p2 = pos[brr_buf_size - 2] >> 1;
|
||||
|
||||
switch(filter) {
|
||||
case 0: break; //no filter
|
||||
|
||||
case 1: {
|
||||
//s += p1 * 0.46875
|
||||
s += p1 >> 1;
|
||||
s += (-p1) >> 5;
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
//s += p1 * 0.953125 - p2 * 0.46875
|
||||
s += p1;
|
||||
s -= p2;
|
||||
s += p2 >> 4;
|
||||
s += (p1 * -3) >> 6;
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
//s += p1 * 0.8984375 - p2 * 0.40625
|
||||
s += p1;
|
||||
s -= p2;
|
||||
s += (p1 * -13) >> 7;
|
||||
s += (p2 * 3) >> 4;
|
||||
} break;
|
||||
}
|
||||
|
||||
//adjust and write sample
|
||||
s = sclamp<16>(s);
|
||||
s = sclip<16>(s << 1);
|
||||
pos[brr_buf_size] = pos[0] = s; //second copy simplifies wrap-around
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
52
src/dsp/sdsp/counter.cpp
Normal file
52
src/dsp/sdsp/counter.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
//counter_rate = number of samples per counter event
|
||||
//all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3)
|
||||
//note that rate[0] is a special case, which never triggers
|
||||
|
||||
const uint16 sDSP::counter_rate[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,
|
||||
};
|
||||
|
||||
//counter_offset = counter offset from zero
|
||||
//counters do not appear to be aligned at zero for all rates
|
||||
|
||||
const uint16 sDSP::counter_offset[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,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
inline void sDSP::counter_tick() {
|
||||
state.counter--;
|
||||
if(state.counter < 0) state.counter = counter_range - 1;
|
||||
}
|
||||
|
||||
//return true if counter event should trigger
|
||||
|
||||
inline bool sDSP::counter_poll(unsigned rate) {
|
||||
if(rate == 0) return false;
|
||||
return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0;
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
138
src/dsp/sdsp/echo.cpp
Normal file
138
src/dsp/sdsp/echo.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
//current echo buffer pointer for left/right channel
|
||||
#define ECHO_PTR(ch) (&ram[state.t_echo_ptr + ch * 2])
|
||||
|
||||
//sample in echo history buffer, where 0 is the oldest
|
||||
#define ECHO_FIR(i) state.echo_hist_pos[i]
|
||||
|
||||
//calculate FIR point for left/right channel
|
||||
#define CALC_FIR(i, ch) ((ECHO_FIR(i + 1)[ch] * (int8)REG(fir + i * 0x10)) >> 6)
|
||||
|
||||
void sDSP::echo_read(bool channel) {
|
||||
uint8 *in = ECHO_PTR(channel);
|
||||
int s = (int8)in[1] * 0x100 + in[0];
|
||||
//second copy simplifies wrap-around handling
|
||||
ECHO_FIR(0)[channel] = ECHO_FIR(8)[channel] = s >> 1;
|
||||
}
|
||||
|
||||
int sDSP::echo_output(bool channel) {
|
||||
int output = sclip<16>((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7)
|
||||
+ sclip<16>((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7);
|
||||
return sclamp<16>(output);
|
||||
}
|
||||
|
||||
void sDSP::echo_write(bool channel) {
|
||||
if(!(state.t_echo_enabled & 0x20)) {
|
||||
uint8 *out = ECHO_PTR(channel);
|
||||
int s = state.t_echo_out[channel];
|
||||
out[0] = (uint8)s;
|
||||
out[1] = (uint8)(s >> 8);
|
||||
}
|
||||
|
||||
state.t_echo_out[channel] = 0;
|
||||
}
|
||||
|
||||
void sDSP::echo_22() {
|
||||
//history
|
||||
state.echo_hist_pos++;
|
||||
if(state.echo_hist_pos >= &state.echo_hist[8]) state.echo_hist_pos = state.echo_hist;
|
||||
|
||||
state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset);
|
||||
echo_read(0);
|
||||
|
||||
//FIR
|
||||
int l = CALC_FIR(0, 0);
|
||||
int r = CALC_FIR(0, 1);
|
||||
|
||||
state.t_echo_in[0] = l;
|
||||
state.t_echo_in[1] = r;
|
||||
}
|
||||
|
||||
void sDSP::echo_23() {
|
||||
int l = CALC_FIR(1, 0) + CALC_FIR(2, 0);
|
||||
int r = CALC_FIR(1, 1) + CALC_FIR(2, 1);
|
||||
|
||||
state.t_echo_in[0] += l;
|
||||
state.t_echo_in[1] += r;
|
||||
|
||||
echo_read(1);
|
||||
}
|
||||
|
||||
void sDSP::echo_24() {
|
||||
int l = CALC_FIR(3, 0) + CALC_FIR(4, 0) + CALC_FIR(5, 0);
|
||||
int r = CALC_FIR(3, 1) + CALC_FIR(4, 1) + CALC_FIR(5, 1);
|
||||
|
||||
state.t_echo_in[0] += l;
|
||||
state.t_echo_in[1] += r;
|
||||
}
|
||||
|
||||
void sDSP::echo_25() {
|
||||
int l = state.t_echo_in[0] + CALC_FIR(6, 0);
|
||||
int r = state.t_echo_in[1] + CALC_FIR(6, 1);
|
||||
|
||||
l = sclip<16>(l);
|
||||
r = sclip<16>(r);
|
||||
|
||||
l += (int16)CALC_FIR(7, 0);
|
||||
r += (int16)CALC_FIR(7, 1);
|
||||
|
||||
state.t_echo_in[0] = sclamp<16>(l) & ~1;
|
||||
state.t_echo_in[1] = sclamp<16>(r) & ~1;
|
||||
}
|
||||
|
||||
void sDSP::echo_26() {
|
||||
//left output volumes
|
||||
//(save sample for next clock so we can output both together)
|
||||
state.t_main_out[0] = echo_output(0);
|
||||
|
||||
//echo feedback
|
||||
int l = state.t_echo_out[0] + sclip<16>((state.t_echo_in[0] * (int8)REG(efb)) >> 7);
|
||||
int r = state.t_echo_out[1] + sclip<16>((state.t_echo_in[1] * (int8)REG(efb)) >> 7);
|
||||
|
||||
state.t_echo_out[0] = sclamp<16>(l) & ~1;
|
||||
state.t_echo_out[1] = sclamp<16>(r) & ~1;
|
||||
}
|
||||
|
||||
void sDSP::echo_27() {
|
||||
//output
|
||||
int outl = state.t_main_out[0];
|
||||
int outr = echo_output(1);
|
||||
state.t_main_out[0] = 0;
|
||||
state.t_main_out[1] = 0;
|
||||
|
||||
//TODO: global muting isn't this simple
|
||||
//(turns DAC on and off or something, causing small ~37-sample pulse when first muted)
|
||||
if(REG(flg) & 0x40) {
|
||||
outl = 0;
|
||||
outr = 0;
|
||||
}
|
||||
|
||||
//output sample to DAC
|
||||
snes.audio.update(outl, outr);
|
||||
}
|
||||
|
||||
void sDSP::echo_28() {
|
||||
state.t_echo_enabled = REG(flg);
|
||||
}
|
||||
|
||||
void sDSP::echo_29() {
|
||||
state.t_esa = REG(esa);
|
||||
|
||||
if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11;
|
||||
|
||||
state.echo_offset += 4;
|
||||
if(state.echo_offset >= state.echo_length) state.echo_offset = 0;
|
||||
|
||||
//write left echo
|
||||
echo_write(0);
|
||||
|
||||
state.t_echo_enabled = REG(flg);
|
||||
}
|
||||
|
||||
void sDSP::echo_30() {
|
||||
//write right echo
|
||||
echo_write(1);
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
62
src/dsp/sdsp/envelope.cpp
Normal file
62
src/dsp/sdsp/envelope.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
void sDSP::envelope_run(voice_t &v) {
|
||||
int env = v.env;
|
||||
|
||||
if(v.env_mode == env_release) { //60%
|
||||
env -= 0x8;
|
||||
if(env < 0) env = 0;
|
||||
v.env = env;
|
||||
return;
|
||||
}
|
||||
|
||||
int rate;
|
||||
int env_data = VREG(adsr1);
|
||||
if(state.t_adsr0 & 0x80) { //99% ADSR
|
||||
if(v.env_mode >= env_decay) { //99%
|
||||
env--;
|
||||
env -= env >> 8;
|
||||
rate = env_data & 0x1f;
|
||||
if(v.env_mode == env_decay) { //1%
|
||||
rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10;
|
||||
}
|
||||
} else { //env_attack
|
||||
rate = ((state.t_adsr0 & 0x0f) << 1) + 1;
|
||||
env += rate < 31 ? 0x20 : 0x400;
|
||||
}
|
||||
} else { //GAIN
|
||||
env_data = VREG(gain);
|
||||
int mode = env_data >> 5;
|
||||
if(mode < 4) { //direct
|
||||
env = env_data << 4;
|
||||
rate = 31;
|
||||
} else {
|
||||
rate = env_data & 0x1f;
|
||||
if(mode == 4) { //4: linear decrease
|
||||
env -= 0x20;
|
||||
} else if(mode < 6) { //5: exponential decrease
|
||||
env--;
|
||||
env -= env >> 8;
|
||||
} else { //6, 7: linear increase
|
||||
env += 0x20;
|
||||
if(mode > 6 && (unsigned)v.hidden_env >= 0x600) {
|
||||
env += 0x8 - 0x20; //7: two-slope linear increase
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sustain level
|
||||
if((env >> 8) == (env_data >> 5) && v.env_mode == env_decay) v.env_mode = env_sustain;
|
||||
v.hidden_env = env;
|
||||
|
||||
//unsigned cast because linear decrease underflowing also triggers this
|
||||
if((unsigned)env > 0x7ff) {
|
||||
env = (env < 0 ? 0 : 0x7ff);
|
||||
if(v.env_mode == env_attack) v.env_mode = env_decay;
|
||||
}
|
||||
|
||||
if(counter_poll(rate) == true) v.env = env;
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
54
src/dsp/sdsp/gaussian.cpp
Normal file
54
src/dsp/sdsp/gaussian.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
const int16 sDSP::gaussian_table[512] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
|
||||
11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
|
||||
18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
|
||||
28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
||||
58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
|
||||
78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
|
||||
104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
|
||||
134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
|
||||
171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
|
||||
212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
|
||||
260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
|
||||
314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
|
||||
374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
|
||||
439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
|
||||
508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
|
||||
582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
|
||||
659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
|
||||
737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
|
||||
816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
|
||||
894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
|
||||
969, 974, 978, 983, 988, 992, 997, 1001, 1005, 1010, 1014, 1019, 1023, 1027, 1032, 1036,
|
||||
1040, 1045, 1049, 1053, 1057, 1061, 1066, 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102,
|
||||
1106, 1109, 1113, 1117, 1121, 1125, 1128, 1132, 1136, 1139, 1143, 1146, 1150, 1153, 1157, 1160,
|
||||
1164, 1167, 1170, 1174, 1177, 1180, 1183, 1186, 1190, 1193, 1196, 1199, 1202, 1205, 1207, 1210,
|
||||
1213, 1216, 1219, 1221, 1224, 1227, 1229, 1232, 1234, 1237, 1239, 1241, 1244, 1246, 1248, 1251,
|
||||
1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
|
||||
1282, 1283, 1284, 1286, 1287, 1288, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1297, 1298,
|
||||
1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305,
|
||||
};
|
||||
|
||||
int sDSP::gaussian_interpolate(const voice_t &v) {
|
||||
//make pointers into gaussian table based on fractional position between samples
|
||||
int offset = (v.interp_pos >> 4) & 0xff;
|
||||
const int16 *fwd = gaussian_table + 255 - offset;
|
||||
const int16 *rev = gaussian_table + offset; //mirror left half of gaussian table
|
||||
|
||||
const int* in = &v.buf[(v.interp_pos >> 12) + v.buf_pos];
|
||||
int output;
|
||||
output = (fwd[ 0] * in[0]) >> 11;
|
||||
output += (fwd[256] * in[1]) >> 11;
|
||||
output += (rev[256] * in[2]) >> 11;
|
||||
output = sclip<16>(output);
|
||||
output += (rev[ 0] * in[3]) >> 11;
|
||||
return sclamp<16>(output) & ~1;
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
35
src/dsp/sdsp/misc.cpp
Normal file
35
src/dsp/sdsp/misc.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
void sDSP::misc_27() {
|
||||
state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON
|
||||
}
|
||||
|
||||
void sDSP::misc_28() {
|
||||
state.t_non = REG(non);
|
||||
state.t_eon = REG(eon);
|
||||
state.t_dir = REG(dir);
|
||||
}
|
||||
|
||||
void sDSP::misc_29() {
|
||||
state.every_other_sample ^= 1;
|
||||
if(state.every_other_sample) {
|
||||
state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read
|
||||
}
|
||||
}
|
||||
|
||||
void sDSP::misc_30() {
|
||||
if(state.every_other_sample) {
|
||||
state.kon = state.new_kon;
|
||||
state.t_koff = REG(koff);
|
||||
}
|
||||
|
||||
counter_tick();
|
||||
|
||||
//noise
|
||||
if(counter_poll(REG(flg) & 0x1f) == true) {
|
||||
int feedback = (state.noise << 13) ^ (state.noise << 14);
|
||||
state.noise = (feedback & 0x4000) ^ (state.noise >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
272
src/dsp/sdsp/sdsp.cpp
Normal file
272
src/dsp/sdsp/sdsp.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
S-DSP emulator
|
||||
license: LGPLv2
|
||||
|
||||
Note: this is basically a C++ cothreaded implementation of Shay Green's (blargg's) S-DSP emulator.
|
||||
The actual algorithms, timing information, tables, variable names, etc were all from him.
|
||||
*/
|
||||
|
||||
#include "../../base.h"
|
||||
#define SDSP_CPP
|
||||
|
||||
#define REG(n) state.regs[r_##n]
|
||||
#define VREG(n) state.regs[v.vidx + v_##n]
|
||||
|
||||
#include "gaussian.cpp"
|
||||
#include "counter.cpp"
|
||||
#include "envelope.cpp"
|
||||
#include "brr.cpp"
|
||||
#include "misc.cpp"
|
||||
#include "voice.cpp"
|
||||
#include "echo.cpp"
|
||||
|
||||
/* timing */
|
||||
|
||||
void sDSP::enter() {
|
||||
#define tick() scheduler.addclocks_dsp(3 * 8)
|
||||
|
||||
tick(); //temporary to sync with bDSP timing
|
||||
|
||||
loop:
|
||||
|
||||
// 0
|
||||
voice_5(voice[0]);
|
||||
voice_2(voice[1]);
|
||||
tick();
|
||||
|
||||
// 1
|
||||
voice_6(voice[0]);
|
||||
voice_3(voice[1]);
|
||||
tick();
|
||||
|
||||
// 2
|
||||
voice_7(voice[0]);
|
||||
voice_4(voice[1]);
|
||||
voice_1(voice[3]);
|
||||
tick();
|
||||
|
||||
// 3
|
||||
voice_8(voice[0]);
|
||||
voice_5(voice[1]);
|
||||
voice_2(voice[2]);
|
||||
tick();
|
||||
|
||||
// 4
|
||||
voice_9(voice[0]);
|
||||
voice_6(voice[1]);
|
||||
voice_3(voice[2]);
|
||||
tick();
|
||||
|
||||
// 5
|
||||
voice_7(voice[1]);
|
||||
voice_4(voice[2]);
|
||||
voice_1(voice[4]);
|
||||
tick();
|
||||
|
||||
// 6
|
||||
voice_8(voice[1]);
|
||||
voice_5(voice[2]);
|
||||
voice_2(voice[3]);
|
||||
tick();
|
||||
|
||||
// 7
|
||||
voice_9(voice[1]);
|
||||
voice_6(voice[2]);
|
||||
voice_3(voice[3]);
|
||||
tick();
|
||||
|
||||
// 8
|
||||
voice_7(voice[2]);
|
||||
voice_4(voice[3]);
|
||||
voice_1(voice[5]);
|
||||
tick();
|
||||
|
||||
// 9
|
||||
voice_8(voice[2]);
|
||||
voice_5(voice[3]);
|
||||
voice_2(voice[4]);
|
||||
tick();
|
||||
|
||||
//10
|
||||
voice_9(voice[2]);
|
||||
voice_6(voice[3]);
|
||||
voice_3(voice[4]);
|
||||
tick();
|
||||
|
||||
//11
|
||||
voice_7(voice[3]);
|
||||
voice_4(voice[4]);
|
||||
voice_1(voice[6]);
|
||||
tick();
|
||||
|
||||
//12
|
||||
voice_8(voice[3]);
|
||||
voice_5(voice[4]);
|
||||
voice_2(voice[5]);
|
||||
tick();
|
||||
|
||||
//13
|
||||
voice_9(voice[3]);
|
||||
voice_6(voice[4]);
|
||||
voice_3(voice[5]);
|
||||
tick();
|
||||
|
||||
//14
|
||||
voice_7(voice[4]);
|
||||
voice_4(voice[5]);
|
||||
voice_1(voice[7]);
|
||||
tick();
|
||||
|
||||
//15
|
||||
voice_8(voice[4]);
|
||||
voice_5(voice[5]);
|
||||
voice_2(voice[6]);
|
||||
tick();
|
||||
|
||||
//16
|
||||
voice_9(voice[4]);
|
||||
voice_6(voice[5]);
|
||||
voice_3(voice[6]);
|
||||
tick();
|
||||
|
||||
//17
|
||||
voice_1(voice[0]);
|
||||
voice_7(voice[5]);
|
||||
voice_4(voice[6]);
|
||||
tick();
|
||||
|
||||
//18
|
||||
voice_8(voice[5]);
|
||||
voice_5(voice[6]);
|
||||
voice_2(voice[7]);
|
||||
tick();
|
||||
|
||||
//19
|
||||
voice_9(voice[5]);
|
||||
voice_6(voice[6]);
|
||||
voice_3(voice[7]);
|
||||
tick();
|
||||
|
||||
//20
|
||||
voice_1(voice[1]);
|
||||
voice_7(voice[6]);
|
||||
voice_4(voice[7]);
|
||||
tick();
|
||||
|
||||
//21
|
||||
voice_8(voice[6]);
|
||||
voice_5(voice[7]);
|
||||
voice_2(voice[0]);
|
||||
tick();
|
||||
|
||||
//22
|
||||
voice_3a(voice[0]);
|
||||
voice_9(voice[6]);
|
||||
voice_6(voice[7]);
|
||||
echo_22();
|
||||
tick();
|
||||
|
||||
//23
|
||||
voice_7(voice[7]);
|
||||
echo_23();
|
||||
tick();
|
||||
|
||||
//24
|
||||
voice_8(voice[7]);
|
||||
echo_24();
|
||||
tick();
|
||||
|
||||
//25
|
||||
voice_3b(voice[0]);
|
||||
voice_9(voice[7]);
|
||||
echo_25();
|
||||
tick();
|
||||
|
||||
//26
|
||||
echo_26();
|
||||
tick();
|
||||
|
||||
//27
|
||||
misc_27();
|
||||
echo_27();
|
||||
tick();
|
||||
|
||||
//28
|
||||
misc_28();
|
||||
echo_28();
|
||||
tick();
|
||||
|
||||
//29
|
||||
misc_29();
|
||||
echo_29();
|
||||
tick();
|
||||
|
||||
//30
|
||||
misc_30();
|
||||
voice_3c(voice[0]);
|
||||
echo_30();
|
||||
tick();
|
||||
|
||||
//31
|
||||
voice_4(voice[0]);
|
||||
voice_1(voice[2]);
|
||||
tick();
|
||||
|
||||
goto loop;
|
||||
|
||||
#undef tick
|
||||
}
|
||||
|
||||
/* register interface for S-SMP $00f2,$00f3 */
|
||||
|
||||
uint8 sDSP::read(uint8 addr) {
|
||||
return state.regs[addr];
|
||||
}
|
||||
|
||||
void sDSP::write(uint8 addr, uint8 data) {
|
||||
state.regs[addr] = data;
|
||||
|
||||
if((addr & 0x0f) == v_envx) {
|
||||
state.envx_buf = data;
|
||||
} else if((addr & 0x0f) == v_outx) {
|
||||
state.outx_buf = data;
|
||||
} else if(addr == r_kon) {
|
||||
state.new_kon = data;
|
||||
} else if(addr == r_endx) {
|
||||
//always cleared, regardless of data written
|
||||
state.endx_buf = 0;
|
||||
state.regs[r_endx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialization */
|
||||
|
||||
void sDSP::power() {
|
||||
ram = (uint8*)smp.get_spcram_handle(); //TODO: move to sMemory
|
||||
memset(&state, 0, sizeof(state_t));
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
memset(&voice[i], 0, sizeof(voice_t));
|
||||
voice[i].vbit = 1 << i;
|
||||
voice[i].vidx = i * 0x10;
|
||||
voice[i].brr_offset = 1;
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void sDSP::reset() {
|
||||
REG(flg) = 0xe0;
|
||||
|
||||
state.noise = 0x4000;
|
||||
state.echo_hist_pos = state.echo_hist;
|
||||
state.every_other_sample = 1;
|
||||
state.echo_offset = 0;
|
||||
state.counter = 0;
|
||||
}
|
||||
|
||||
sDSP::sDSP() {
|
||||
}
|
||||
|
||||
sDSP::~sDSP() {
|
||||
}
|
165
src/dsp/sdsp/sdsp.h
Normal file
165
src/dsp/sdsp/sdsp.h
Normal file
@@ -0,0 +1,165 @@
|
||||
class sDSP : public DSP {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
uint8 read(uint8 addr);
|
||||
void write(uint8 addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
sDSP();
|
||||
~sDSP();
|
||||
|
||||
private:
|
||||
//external
|
||||
uint8 *ram;
|
||||
|
||||
//global registers
|
||||
enum global_reg_t {
|
||||
r_mvoll = 0x0c, r_mvolr = 0x1c,
|
||||
r_evoll = 0x2c, r_evolr = 0x3c,
|
||||
r_kon = 0x4c, r_koff = 0x5c,
|
||||
r_flg = 0x6c, r_endx = 0x7c,
|
||||
r_efb = 0x0d, r_pmon = 0x2d,
|
||||
r_non = 0x3d, r_eon = 0x4d,
|
||||
r_dir = 0x5d, r_esa = 0x6d,
|
||||
r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f
|
||||
};
|
||||
|
||||
//voice registers
|
||||
enum voice_reg_t {
|
||||
v_voll = 0x00, v_volr = 0x01,
|
||||
v_pitchl = 0x02, v_pitchh = 0x03,
|
||||
v_srcn = 0x04, v_adsr0 = 0x05,
|
||||
v_adsr1 = 0x06, v_gain = 0x07,
|
||||
v_envx = 0x08, v_outx = 0x09,
|
||||
};
|
||||
|
||||
//internal envelope modes
|
||||
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
|
||||
|
||||
//internal voice state
|
||||
enum { brr_buf_size = 12 };
|
||||
enum { brr_block_size = 9 };
|
||||
|
||||
//global state
|
||||
struct state_t {
|
||||
uint8 regs[128];
|
||||
|
||||
//echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
|
||||
int echo_hist[8 * 2][2];
|
||||
int (*echo_hist_pos)[2]; //&echo_hist[0 to 7]
|
||||
|
||||
bool every_other_sample; //toggles every sample
|
||||
int kon; //KON value when last checked
|
||||
int noise;
|
||||
int counter;
|
||||
int echo_offset; //offset from ESA in echo buffer
|
||||
int echo_length; //number of bytes that echo_offset will stop at
|
||||
|
||||
//hidden registers also written to when main register is written to
|
||||
int new_kon;
|
||||
int endx_buf;
|
||||
int envx_buf;
|
||||
int outx_buf;
|
||||
|
||||
//temporary state between clocks
|
||||
|
||||
//read once per sample
|
||||
int t_pmon;
|
||||
int t_non;
|
||||
int t_eon;
|
||||
int t_dir;
|
||||
int t_koff;
|
||||
|
||||
//read a few clocks ahead before used
|
||||
int t_brr_next_addr;
|
||||
int t_adsr0;
|
||||
int t_brr_header;
|
||||
int t_brr_byte;
|
||||
int t_srcn;
|
||||
int t_esa;
|
||||
int t_echo_enabled;
|
||||
|
||||
//internal state that is recalculated every sample
|
||||
int t_dir_addr;
|
||||
int t_pitch;
|
||||
int t_output;
|
||||
int t_looped;
|
||||
int t_echo_ptr;
|
||||
|
||||
//left/right sums
|
||||
int t_main_out[2];
|
||||
int t_echo_out[2];
|
||||
int t_echo_in [2];
|
||||
} state;
|
||||
|
||||
//voice state
|
||||
struct voice_t {
|
||||
int buf[brr_buf_size * 2]; //decoded samples (twice the size to simplify wrap handling)
|
||||
int buf_pos; //place in buffer where next samples will be decoded
|
||||
int interp_pos; //relative fractional position in sample (0x1000 = 1.0)
|
||||
int brr_addr; //address of current BRR block
|
||||
int brr_offset; //current decoding offset in BRR block
|
||||
int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc
|
||||
int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc
|
||||
int kon_delay; //KON delay/current setup phase
|
||||
env_mode_t env_mode;
|
||||
int env; //current envelope level
|
||||
int t_envx_out;
|
||||
int hidden_env; //used by GAIN mode 7, very obscure quirk
|
||||
} voice[8];
|
||||
|
||||
//gaussian
|
||||
static const int16 gaussian_table[512];
|
||||
int gaussian_interpolate(const voice_t &v);
|
||||
|
||||
//counter
|
||||
enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800)
|
||||
static const uint16 counter_rate[32];
|
||||
static const uint16 counter_offset[32];
|
||||
void counter_tick();
|
||||
bool counter_poll(unsigned rate);
|
||||
|
||||
//envelope
|
||||
void envelope_run(voice_t &v);
|
||||
|
||||
//brr
|
||||
void brr_decode(voice_t &v);
|
||||
|
||||
//misc
|
||||
void misc_27();
|
||||
void misc_28();
|
||||
void misc_29();
|
||||
void misc_30();
|
||||
|
||||
//voice
|
||||
void voice_output(voice_t &v, bool channel);
|
||||
void voice_1 (voice_t &v);
|
||||
void voice_2 (voice_t &v);
|
||||
void voice_3 (voice_t &v);
|
||||
void voice_3a(voice_t &v);
|
||||
void voice_3b(voice_t &v);
|
||||
void voice_3c(voice_t &v);
|
||||
void voice_4 (voice_t &v);
|
||||
void voice_5 (voice_t &v);
|
||||
void voice_6 (voice_t &v);
|
||||
void voice_7 (voice_t &v);
|
||||
void voice_8 (voice_t &v);
|
||||
void voice_9 (voice_t &v);
|
||||
|
||||
//echo
|
||||
void echo_read(bool channel);
|
||||
int echo_output(bool channel);
|
||||
void echo_write(bool channel);
|
||||
void echo_22();
|
||||
void echo_23();
|
||||
void echo_24();
|
||||
void echo_25();
|
||||
void echo_26();
|
||||
void echo_27();
|
||||
void echo_28();
|
||||
void echo_29();
|
||||
void echo_30();
|
||||
};
|
172
src/dsp/sdsp/voice.cpp
Normal file
172
src/dsp/sdsp/voice.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#ifdef SDSP_CPP
|
||||
|
||||
inline void sDSP::voice_output(voice_t &v, bool channel) {
|
||||
//apply left/right volume
|
||||
int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7;
|
||||
|
||||
//add to output total
|
||||
state.t_main_out[channel] += amp;
|
||||
state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]);
|
||||
|
||||
//optionally add to echo total
|
||||
if(state.t_eon & v.vbit) {
|
||||
state.t_echo_out[channel] += amp;
|
||||
state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]);
|
||||
}
|
||||
}
|
||||
|
||||
void sDSP::voice_1(voice_t &v) {
|
||||
state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2);
|
||||
state.t_srcn = VREG(srcn);
|
||||
}
|
||||
|
||||
void sDSP::voice_2(voice_t &v) {
|
||||
//read sample pointer (ignored if not needed)
|
||||
const uint8 *entry = &ram[state.t_dir_addr];
|
||||
if(!v.kon_delay) entry += 2;
|
||||
state.t_brr_next_addr = (entry[1] << 8) + entry[0];
|
||||
|
||||
state.t_adsr0 = VREG(adsr0);
|
||||
|
||||
//read pitch, spread over two clocks
|
||||
state.t_pitch = VREG(pitchl);
|
||||
}
|
||||
|
||||
void sDSP::voice_3(voice_t &v) {
|
||||
voice_3a(v);
|
||||
voice_3b(v);
|
||||
voice_3c(v);
|
||||
}
|
||||
|
||||
void sDSP::voice_3a(voice_t &v) {
|
||||
state.t_pitch += (VREG(pitchh) & 0x3f) << 8;
|
||||
}
|
||||
|
||||
void sDSP::voice_3b(voice_t &v) {
|
||||
state.t_brr_byte = ram[(uint16)(v.brr_addr + v.brr_offset)];
|
||||
state.t_brr_header = ram[(uint16)(v.brr_addr)];
|
||||
}
|
||||
|
||||
void sDSP::voice_3c(voice_t &v) {
|
||||
//pitch modulation using previous voice's output
|
||||
|
||||
if(state.t_pmon & v.vbit) {
|
||||
state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10;
|
||||
}
|
||||
|
||||
if(v.kon_delay) {
|
||||
//get ready to start BRR decoding on next sample
|
||||
if(v.kon_delay == 5) {
|
||||
v.brr_addr = state.t_brr_next_addr;
|
||||
v.brr_offset = 1;
|
||||
v.buf_pos = 0;
|
||||
state.t_brr_header = 0; //header is ignored on this sample
|
||||
}
|
||||
|
||||
//envelope is never run during KON
|
||||
v.env = 0;
|
||||
v.hidden_env = 0;
|
||||
|
||||
//disable BRR decoding until last three samples
|
||||
v.interp_pos = 0;
|
||||
v.kon_delay--;
|
||||
if(v.kon_delay & 3) v.interp_pos = 0x4000;
|
||||
|
||||
//pitch is never added during KON
|
||||
state.t_pitch = 0;
|
||||
}
|
||||
|
||||
//gaussian interpolation
|
||||
int output = gaussian_interpolate(v);
|
||||
|
||||
//noise
|
||||
if(state.t_non & v.vbit) {
|
||||
output = (int16)(state.noise << 1);
|
||||
}
|
||||
|
||||
//apply envelope
|
||||
state.t_output = ((output * v.env) >> 11) & ~1;
|
||||
v.t_envx_out = v.env >> 4;
|
||||
|
||||
//immediate silence due to end of sample or soft reset
|
||||
if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) {
|
||||
v.env_mode = env_release;
|
||||
v.env = 0;
|
||||
}
|
||||
|
||||
if(state.every_other_sample) {
|
||||
//KOFF
|
||||
if(state.t_koff & v.vbit) {
|
||||
v.env_mode = env_release;
|
||||
}
|
||||
|
||||
//KON
|
||||
if(state.kon & v.vbit) {
|
||||
v.kon_delay = 5;
|
||||
v.env_mode = env_attack;
|
||||
}
|
||||
}
|
||||
|
||||
//run envelope for next sample
|
||||
if(!v.kon_delay) envelope_run(v);
|
||||
}
|
||||
|
||||
void sDSP::voice_4(voice_t &v) {
|
||||
//decode BRR
|
||||
state.t_looped = 0;
|
||||
if(v.interp_pos >= 0x4000) {
|
||||
brr_decode(v);
|
||||
v.brr_offset += 2;
|
||||
if(v.brr_offset >= 9) {
|
||||
//start decoding next BRR block
|
||||
v.brr_addr = (uint16)(v.brr_addr + 9);
|
||||
if(state.t_brr_header & 1) {
|
||||
v.brr_addr = state.t_brr_next_addr;
|
||||
state.t_looped = v.vbit;
|
||||
}
|
||||
v.brr_offset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//apply pitch
|
||||
v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch;
|
||||
|
||||
//keep from getting too far ahead (when using pitch modulation)
|
||||
if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff;
|
||||
|
||||
//output left
|
||||
voice_output(v, 0);
|
||||
}
|
||||
|
||||
void sDSP::voice_5(voice_t &v) {
|
||||
//output right
|
||||
voice_output(v, 1);
|
||||
|
||||
//ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier
|
||||
state.endx_buf = REG(endx) | state.t_looped;
|
||||
|
||||
//clear bit in ENDX if KON just began
|
||||
if(v.kon_delay == 5) state.endx_buf &= ~v.vbit;
|
||||
}
|
||||
|
||||
void sDSP::voice_6(voice_t &v) {
|
||||
state.outx_buf = state.t_output >> 8;
|
||||
}
|
||||
|
||||
void sDSP::voice_7(voice_t &v) {
|
||||
//update ENDX
|
||||
REG(endx) = (uint8)state.endx_buf;
|
||||
state.envx_buf = v.t_envx_out;
|
||||
}
|
||||
|
||||
void sDSP::voice_8(voice_t &v) {
|
||||
//update OUTX
|
||||
VREG(outx) = (uint8)state.outx_buf;
|
||||
}
|
||||
|
||||
void sDSP::voice_9(voice_t &v) {
|
||||
//update ENVX
|
||||
VREG(envx) = (uint8)state.envx_buf;
|
||||
}
|
||||
|
||||
#endif //ifdef SDSP_CPP
|
@@ -14,7 +14,7 @@
|
||||
#include "smp/ssmp/ssmp.h"
|
||||
|
||||
#include "dsp/dsp.h"
|
||||
#include "dsp/bdsp/bdsp.h"
|
||||
#include "dsp/sdsp/sdsp.h"
|
||||
|
||||
#include "ppu/ppu.h"
|
||||
#include "ppu/bppu/bppu.h"
|
||||
|
@@ -1,9 +1,9 @@
|
||||
void hiro_pcanvas_expose(pCanvas *p) {
|
||||
uint32_t *f = p->fbuffer;
|
||||
uint32_t *r = p->rbuffer;
|
||||
uint32_t *f = p->fbuffer;
|
||||
uint32_t *r = p->rbuffer;
|
||||
for(uint y = p->canvas->allocation.height; y; y--) {
|
||||
for(uint x = p->canvas->allocation.width; x; x--) {
|
||||
uint32_t p = *f++;
|
||||
uint32_t p = *f++;
|
||||
*r++ = ((p << 16) & 0xff0000) + (p & 0x00ff00) + ((p >> 16) & 0x0000ff);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ void pCanvas::create(uint style, uint width, uint height) {
|
||||
void pCanvas::redraw() {
|
||||
if(!canvas || !canvas->window) return;
|
||||
|
||||
GdkRectangle rect;
|
||||
GdkRectangle rect;
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = canvas->allocation.width;
|
@@ -38,12 +38,24 @@ void pHiro::init() {
|
||||
gtk_init(&argc, &argv);
|
||||
free(argv[0]);
|
||||
free(argv);
|
||||
|
||||
is_composited = false;
|
||||
screen = gdk_screen_get_default();
|
||||
if(gdk_screen_is_composited(screen)) {
|
||||
colormap = gdk_screen_get_rgba_colormap(screen);
|
||||
if(colormap) is_composited = true;
|
||||
else colormap = gdk_screen_get_rgb_colormap(screen); //fallback
|
||||
} else {
|
||||
colormap = gdk_screen_get_rgb_colormap(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void pHiro::term() {
|
||||
enable_screensaver();
|
||||
}
|
||||
|
||||
bool pHiro::run() {
|
||||
if(is_screensaver_enabled == false) screensaver_tick();
|
||||
gtk_main_iteration_do(false);
|
||||
return pending();
|
||||
}
|
||||
@@ -52,11 +64,34 @@ bool pHiro::pending() {
|
||||
return gtk_events_pending();
|
||||
}
|
||||
|
||||
bool pHiro::file_load(Window *focus, char *filename, const char *filter, const char *path) {
|
||||
bool pHiro::folder_select(Window *focus, char *filename, const char *path) {
|
||||
if(!filename) return false;
|
||||
strcpy(filename, "");
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load File",
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new("Select Folder",
|
||||
focus ? GTK_WINDOW(focus->p.gtk_handle()) : (GtkWindow*)0,
|
||||
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
||||
(const gchar*)0);
|
||||
|
||||
if(path && *path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
|
||||
|
||||
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
||||
char *fn = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
||||
strcpy(filename, fn);
|
||||
g_free(fn);
|
||||
}
|
||||
|
||||
gtk_widget_destroy(dialog);
|
||||
return strcmp(filename, ""); //return true if filename exists
|
||||
}
|
||||
|
||||
bool pHiro::file_open(Window *focus, char *filename, const char *path, const char *filter) {
|
||||
if(!filename) return false;
|
||||
strcpy(filename, "");
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new("Open File",
|
||||
focus ? GTK_WINDOW(focus->p.gtk_handle()) : (GtkWindow*)0,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
@@ -75,7 +110,7 @@ bool pHiro::file_load(Window *focus, char *filename, const char *filter, const c
|
||||
return strcmp(filename, ""); //return true if filename exists
|
||||
}
|
||||
|
||||
bool pHiro::file_save(Window *focus, char *filename, const char *filter, const char *path) {
|
||||
bool pHiro::file_save(Window *focus, char *filename, const char *path, const char *filter) {
|
||||
if(!filename) return false;
|
||||
strcpy(filename, "");
|
||||
|
||||
@@ -107,15 +142,50 @@ uint pHiro::screen_height() {
|
||||
return gdk_screen_height();
|
||||
}
|
||||
|
||||
void pHiro::enable_screensaver() {
|
||||
if(is_screensaver_enabled == true) return;
|
||||
is_screensaver_enabled = true;
|
||||
DPMSDisable(GDK_DISPLAY());
|
||||
}
|
||||
|
||||
void pHiro::disable_screensaver() {
|
||||
if(is_screensaver_enabled == false) return;
|
||||
is_screensaver_enabled = false;
|
||||
DPMSEnable(GDK_DISPLAY());
|
||||
}
|
||||
|
||||
pHiro& pHiro::handle() {
|
||||
return hiro().p;
|
||||
}
|
||||
|
||||
pHiro::pHiro(Hiro &self_) : self(self_) {
|
||||
is_screensaver_enabled = true;
|
||||
}
|
||||
|
||||
pHiro& phiro() {
|
||||
return pHiro::handle();
|
||||
}
|
||||
|
||||
/* internal */
|
||||
|
||||
void pHiro::screensaver_tick() {
|
||||
static clock_t delta_x = 0, delta_y = 0;
|
||||
|
||||
delta_y = clock();
|
||||
if(delta_y - delta_x < CLOCKS_PER_SEC * 20) return;
|
||||
|
||||
//XSetScreenSaver(timeout = 0) does not work
|
||||
//XResetScreenSaver() does not work
|
||||
//XScreenSaverSuspend() does not work
|
||||
//DPMSDisable() does not work
|
||||
//XSendEvent(KeyPressMask) does not work
|
||||
//use XTest extension to send fake keypress every ~20 seconds.
|
||||
//keycode of 255 does not map to any actual key, but it will block screensaver.
|
||||
delta_x = delta_y;
|
||||
XTestFakeKeyEvent(GDK_DISPLAY(), 255, True, 0);
|
||||
XSync(GDK_DISPLAY(), False);
|
||||
XTestFakeKeyEvent(GDK_DISPLAY(), 255, False, 0);
|
||||
XSync(GDK_DISPLAY(), False);
|
||||
}
|
||||
|
||||
} //namespace libhiro
|
@@ -3,7 +3,10 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <cairo.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <X11/extensions/dpms.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
namespace libhiro {
|
||||
|
||||
@@ -36,16 +39,26 @@ public:
|
||||
bool run();
|
||||
bool pending();
|
||||
|
||||
bool file_load(Window *focus, char *filename, const char *filter, const char *path);
|
||||
bool file_save(Window *focus, char *filename, const char *filter, const char *path);
|
||||
bool folder_select(Window *focus, char *filename, const char *path = "");
|
||||
bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
|
||||
uint screen_width();
|
||||
uint screen_height();
|
||||
|
||||
void enable_screensaver();
|
||||
void disable_screensaver();
|
||||
|
||||
static pHiro& handle();
|
||||
pHiro(Hiro&);
|
||||
|
||||
/* internal */
|
||||
GdkScreen *screen;
|
||||
GdkColormap *colormap;
|
||||
bool is_composited;
|
||||
|
||||
bool is_screensaver_enabled;
|
||||
void screensaver_tick();
|
||||
uint16_t translate_key(uint key);
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user