Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
97a3a28d86 | ||
|
6ec765f2c4 | ||
|
54c7b4692d | ||
|
66067f0015 | ||
|
4c66de6f27 | ||
|
6a17b5ed4f | ||
|
8135dfdac9 |
46
src/Makefile
@@ -26,7 +26,7 @@ ifeq ($(platform),x)
|
||||
link += -s
|
||||
|
||||
ruby := video.glx video.xv video.qtraster video.sdl
|
||||
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao
|
||||
ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
|
||||
ruby += input.sdl input.x
|
||||
|
||||
link += $(if $(findstring audio.openal,$(ruby)),-lopenal)
|
||||
@@ -37,8 +37,8 @@ else ifeq ($(platform),osx)
|
||||
|
||||
link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
|
||||
else ifeq ($(platform),win)
|
||||
link += -mwindows
|
||||
# link += -mconsole
|
||||
link += -mwindows -mthreads
|
||||
# link += -mconsole -mthreads
|
||||
link += -s -luuid -lkernel32 -luser32 -lgdi32 -lshell32
|
||||
# statically link Qt for Windows build
|
||||
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
@@ -76,11 +76,12 @@ link += $(call ifhas,input.rawinput,$(ruby),-ldinput8 -ldxguid)
|
||||
### objects ###
|
||||
###############
|
||||
|
||||
objects := libco ruby libfilter
|
||||
objects := libco ruby
|
||||
objects += system cartridge cheat
|
||||
objects += memory smemory cpu cpucore scpu smp smpcore ssmp sdsp ppu bppu
|
||||
objects += sgb superfx sa1
|
||||
objects += supergameboy superfx sa1
|
||||
objects += bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 st011 st018
|
||||
objects += 21fx
|
||||
|
||||
######################
|
||||
### implicit rules ###
|
||||
@@ -111,8 +112,6 @@ rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
|
||||
obj/ruby.o: lib/ruby/ruby.cpp $(call rwildcard,lib/ruby/*)
|
||||
$(call compile,$(rubydef) $(rubyflags))
|
||||
obj/libco.o: lib/libco/libco.c lib/libco/*
|
||||
$(c) -O3 -fomit-frame-pointer -static -Ilib -c $< -o $@
|
||||
obj/libfilter.o: lib/libfilter/libfilter.cpp lib/libfilter/*
|
||||
|
||||
#################
|
||||
### utilities ###
|
||||
@@ -168,22 +167,23 @@ obj/system.o: system/system.cpp $(call rwildcard,system/)
|
||||
### special chips ###
|
||||
#####################
|
||||
|
||||
obj/sgb.o : chip/sgb/sgb.cpp $(call rwildcard,chip/sgb/)
|
||||
obj/superfx.o: chip/superfx/superfx.cpp $(call rwildcard,chip/superfx/)
|
||||
obj/sa1.o : chip/sa1/sa1.cpp $(call rwildcard,chip/sa1/)
|
||||
obj/bsx.o : chip/bsx/bsx.cpp chip/bsx/*
|
||||
obj/srtc.o : chip/srtc/srtc.cpp chip/srtc/*
|
||||
obj/sdd1.o : chip/sdd1/sdd1.cpp chip/sdd1/*
|
||||
obj/spc7110.o: chip/spc7110/spc7110.cpp chip/spc7110/*
|
||||
obj/cx4.o : chip/cx4/cx4.cpp chip/cx4/*
|
||||
obj/dsp1.o : chip/dsp1/dsp1.cpp chip/dsp1/*
|
||||
obj/dsp2.o : chip/dsp2/dsp2.cpp chip/dsp2/*
|
||||
obj/dsp3.o : chip/dsp3/dsp3.cpp chip/dsp3/*
|
||||
obj/dsp4.o : chip/dsp4/dsp4.cpp chip/dsp4/*
|
||||
obj/obc1.o : chip/obc1/obc1.cpp chip/obc1/*
|
||||
obj/st010.o : chip/st010/st010.cpp chip/st010/*
|
||||
obj/st011.o : chip/st011/st011.cpp chip/st011/*
|
||||
obj/st018.o : chip/st018/st018.cpp chip/st018/*
|
||||
obj/supergameboy.o: chip/supergameboy/supergameboy.cpp $(call rwildcard,chip/supergameboy/)
|
||||
obj/superfx.o : chip/superfx/superfx.cpp $(call rwildcard,chip/superfx/)
|
||||
obj/sa1.o : chip/sa1/sa1.cpp $(call rwildcard,chip/sa1/)
|
||||
obj/bsx.o : chip/bsx/bsx.cpp chip/bsx/*
|
||||
obj/srtc.o : chip/srtc/srtc.cpp chip/srtc/*
|
||||
obj/sdd1.o : chip/sdd1/sdd1.cpp chip/sdd1/*
|
||||
obj/spc7110.o : chip/spc7110/spc7110.cpp chip/spc7110/*
|
||||
obj/cx4.o : chip/cx4/cx4.cpp chip/cx4/*
|
||||
obj/dsp1.o : chip/dsp1/dsp1.cpp chip/dsp1/*
|
||||
obj/dsp2.o : chip/dsp2/dsp2.cpp chip/dsp2/*
|
||||
obj/dsp3.o : chip/dsp3/dsp3.cpp chip/dsp3/*
|
||||
obj/dsp4.o : chip/dsp4/dsp4.cpp chip/dsp4/*
|
||||
obj/obc1.o : chip/obc1/obc1.cpp chip/obc1/*
|
||||
obj/st010.o : chip/st010/st010.cpp chip/st010/*
|
||||
obj/st011.o : chip/st011/st011.cpp chip/st011/*
|
||||
obj/st018.o : chip/st018/st018.cpp chip/st018/*
|
||||
obj/21fx.o : chip/21fx/21fx.cpp chip/21fx/*
|
||||
|
||||
###############
|
||||
### targets ###
|
||||
|
@@ -1,6 +1,6 @@
|
||||
static const char bsnesVersion[] = "0.052";
|
||||
static const char bsnesVersion[] = "0.059";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSaveStateVersion = 3;
|
||||
static const unsigned bsnesSerializerVersion = 4;
|
||||
|
||||
//S-DSP can be encapsulated into a state machine using #define magic
|
||||
//this avoids ~2.048m co_switch() calls per second (~5% speedup)
|
||||
@@ -15,6 +15,7 @@ static const unsigned bsnesSaveStateVersion = 3;
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
@@ -23,7 +24,6 @@ static const unsigned bsnesSaveStateVersion = 3;
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/moduloarray.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/priorityqueue.hpp>
|
||||
#include <nall/property.hpp>
|
||||
|
@@ -1,48 +1,54 @@
|
||||
#include <../base.hpp>
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/sha256.hpp>
|
||||
|
||||
#define CARTRIDGE_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "header.cpp"
|
||||
#include "gameboyheader.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
namespace memory {
|
||||
MappedRAM cartrom, cartram, cartrtc;
|
||||
MappedRAM bsxflash, bsxram, bsxpram;
|
||||
MappedRAM stArom, stAram;
|
||||
MappedRAM stBrom, stBram;
|
||||
MappedRAM gbrom, gbram;
|
||||
MappedRAM gbrom, gbram, gbrtc;
|
||||
};
|
||||
|
||||
Cartridge cartridge;
|
||||
|
||||
void Cartridge::load(Mode cartridge_mode) {
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, memory::cartrom.data(), memory::cartrom.size());
|
||||
set_cartinfo(cartinfo);
|
||||
mode = cartridge_mode;
|
||||
read_header(memory::cartrom.data(), memory::cartrom.size());
|
||||
|
||||
set(mode, cartridge_mode);
|
||||
|
||||
if(cartinfo.ram_size > 0) {
|
||||
memory::cartram.map(new(zeromemory) uint8_t[cartinfo.ram_size], cartinfo.ram_size);
|
||||
if(ram_size > 0) {
|
||||
memory::cartram.map(allocate<uint8_t>(ram_size, 0xff), ram_size);
|
||||
}
|
||||
|
||||
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||
memory::cartrtc.map(new(zeromemory) uint8_t[20], 20);
|
||||
if(has_srtc || has_spc7110rtc) {
|
||||
memory::cartrtc.map(allocate<uint8_t>(20, 0xff), 20);
|
||||
}
|
||||
|
||||
if(mode() == ModeBsx) {
|
||||
memory::bsxram.map (new(zeromemory) uint8_t[ 32 * 1024], 32 * 1024);
|
||||
memory::bsxpram.map(new(zeromemory) uint8_t[512 * 1024], 512 * 1024);
|
||||
if(mode == ModeBsx) {
|
||||
memory::bsxram.map (allocate<uint8_t>( 32 * 1024, 0xff), 32 * 1024);
|
||||
memory::bsxpram.map(allocate<uint8_t>(512 * 1024, 0xff), 512 * 1024);
|
||||
}
|
||||
|
||||
if(mode() == ModeSufamiTurbo) {
|
||||
if(memory::stArom.data()) memory::stAram.map(new(zeromemory) uint8_t[128 * 1024], 128 * 1024);
|
||||
if(memory::stBrom.data()) memory::stBram.map(new(zeromemory) uint8_t[128 * 1024], 128 * 1024);
|
||||
if(mode == ModeSufamiTurbo) {
|
||||
if(memory::stArom.data()) memory::stAram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
|
||||
if(memory::stBrom.data()) memory::stBram.map(allocate<uint8_t>(128 * 1024, 0xff), 128 * 1024);
|
||||
}
|
||||
|
||||
if(mode() == ModeSuperGameBoy) {
|
||||
if(memory::gbrom.data()) memory::gbram.map(new(zeromemory) uint8_t[64 * 1024], 64 * 1024);
|
||||
if(mode == ModeSuperGameBoy) {
|
||||
if(memory::gbrom.data()) {
|
||||
unsigned ram_size = gameboy_ram_size();
|
||||
unsigned rtc_size = gameboy_rtc_size();
|
||||
|
||||
if(ram_size) memory::gbram.map(allocate<uint8_t>(ram_size, 0xff), ram_size);
|
||||
if(rtc_size) memory::gbrtc.map(allocate<uint8_t>(rtc_size, 0x00), rtc_size);
|
||||
}
|
||||
}
|
||||
|
||||
memory::cartrom.write_protect(true);
|
||||
@@ -57,6 +63,7 @@ void Cartridge::load(Mode cartridge_mode) {
|
||||
memory::stBram.write_protect(false);
|
||||
memory::gbrom.write_protect(true);
|
||||
memory::gbram.write_protect(false);
|
||||
memory::gbrtc.write_protect(false);
|
||||
|
||||
unsigned checksum = ~0;
|
||||
for(unsigned n = 0; n < memory::cartrom.size(); n++) checksum = crc32_adjust(checksum, memory::cartrom[n]);
|
||||
@@ -68,11 +75,26 @@ void Cartridge::load(Mode cartridge_mode) {
|
||||
for(unsigned n = 0; n < memory::stBrom.size(); n++) checksum = crc32_adjust(checksum, memory::stBrom[n]);
|
||||
if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0)
|
||||
for(unsigned n = 0; n < memory::gbrom.size(); n++) checksum = crc32_adjust(checksum, memory::gbrom[n]);
|
||||
set(crc32, ~checksum);
|
||||
crc32 = ~checksum;
|
||||
|
||||
#if 0
|
||||
fprintf(stdout, "crc32 = %.8x\n", (unsigned)crc32);
|
||||
|
||||
sha256_ctx sha;
|
||||
uint8_t shahash[32];
|
||||
sha256_init(&sha);
|
||||
sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size());
|
||||
sha256_final(&sha);
|
||||
sha256_hash(&sha, shahash);
|
||||
|
||||
fprintf(stdout, "sha256 = ");
|
||||
for(unsigned i = 0; i < 32; i++) fprintf(stdout, "%.2x", shahash[i]);
|
||||
fprintf(stdout, "\n");
|
||||
#endif
|
||||
|
||||
bus.load_cart();
|
||||
system.serialize_init();
|
||||
set(loaded, true);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
@@ -88,50 +110,19 @@ void Cartridge::unload() {
|
||||
memory::stBram.reset();
|
||||
memory::gbrom.reset();
|
||||
memory::gbram.reset();
|
||||
memory::gbrtc.reset();
|
||||
|
||||
if(loaded() == false) return;
|
||||
if(loaded == false) return;
|
||||
bus.unload_cart();
|
||||
set(loaded, false);
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
Cartridge::Type Cartridge::detect_image_type(uint8_t *data, unsigned size) const {
|
||||
cartinfo_t info;
|
||||
read_header(info, data, size);
|
||||
return info.type;
|
||||
}
|
||||
|
||||
void Cartridge::serialize(serializer &s) {
|
||||
if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) {
|
||||
s.array(memory::cartram.data(), memory::cartram.size());
|
||||
}
|
||||
|
||||
if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) {
|
||||
s.array(memory::cartrtc.data(), memory::cartrtc.size());
|
||||
}
|
||||
|
||||
if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) {
|
||||
s.array(memory::bsxram.data(), memory::bsxram.size());
|
||||
}
|
||||
|
||||
if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) {
|
||||
s.array(memory::bsxpram.data(), memory::bsxpram.size());
|
||||
}
|
||||
|
||||
if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) {
|
||||
s.array(memory::stAram.data(), memory::stAram.size());
|
||||
}
|
||||
|
||||
if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) {
|
||||
s.array(memory::stBram.data(), memory::stBram.size());
|
||||
}
|
||||
|
||||
if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) {
|
||||
s.array(memory::gbram.data(), memory::gbram.size());
|
||||
}
|
||||
bool Cartridge::has_21fx() const {
|
||||
return s21fx.exists();
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
set(loaded, false);
|
||||
loaded = false;
|
||||
unload();
|
||||
}
|
||||
|
||||
@@ -139,63 +130,4 @@ Cartridge::~Cartridge() {
|
||||
unload();
|
||||
}
|
||||
|
||||
void Cartridge::set_cartinfo(const Cartridge::cartinfo_t &source) {
|
||||
set(region, source.region);
|
||||
set(mapper, source.mapper);
|
||||
set(dsp1_mapper, source.dsp1_mapper);
|
||||
|
||||
set(has_bsx_slot, source.bsx_slot);
|
||||
set(has_superfx, source.superfx);
|
||||
set(has_sa1, source.sa1);
|
||||
set(has_srtc, source.srtc);
|
||||
set(has_sdd1, source.sdd1);
|
||||
set(has_spc7110, source.spc7110);
|
||||
set(has_spc7110rtc, source.spc7110rtc);
|
||||
set(has_cx4, source.cx4);
|
||||
set(has_dsp1, source.dsp1);
|
||||
set(has_dsp2, source.dsp2);
|
||||
set(has_dsp3, source.dsp3);
|
||||
set(has_dsp4, source.dsp4);
|
||||
set(has_obc1, source.obc1);
|
||||
set(has_st010, source.st010);
|
||||
set(has_st011, source.st011);
|
||||
set(has_st018, source.st018);
|
||||
}
|
||||
|
||||
//==========
|
||||
//cartinfo_t
|
||||
//==========
|
||||
|
||||
void Cartridge::cartinfo_t::reset() {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
|
||||
rom_size = 0;
|
||||
ram_size = 0;
|
||||
|
||||
bsx_slot = false;
|
||||
superfx = false;
|
||||
sa1 = false;
|
||||
srtc = false;
|
||||
sdd1 = false;
|
||||
spc7110 = false;
|
||||
spc7110rtc = false;
|
||||
cx4 = false;
|
||||
dsp1 = false;
|
||||
dsp2 = false;
|
||||
dsp3 = false;
|
||||
dsp4 = false;
|
||||
obc1 = false;
|
||||
st010 = false;
|
||||
st011 = false;
|
||||
st018 = false;
|
||||
}
|
||||
|
||||
Cartridge::cartinfo_t::cartinfo_t() {
|
||||
reset();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
class Cartridge : public property {
|
||||
class Cartridge : property<Cartridge> {
|
||||
public:
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoyBios,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
@@ -46,60 +47,41 @@ public:
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
//properties can be read via operator(), eg "if(cartridge.loaded() == true)";
|
||||
//warning: if loaded() == false, no other property is considered valid!
|
||||
readonly<bool> loaded; //is a base cartridge inserted?
|
||||
readonly<unsigned> crc32; //crc32 of all cartridges (base+slot(s))
|
||||
|
||||
property_t<bool> loaded; //is a base cartridge inserted?
|
||||
property_t<unsigned> crc32; //crc32 of all files sans headers
|
||||
readonly<Mode> mode;
|
||||
readonly<Type> type;
|
||||
readonly<Region> region;
|
||||
readonly<MemoryMapper> mapper;
|
||||
readonly<DSP1MemoryMapper> dsp1_mapper;
|
||||
|
||||
property_t<Mode> mode;
|
||||
property_t<Region> region;
|
||||
property_t<MemoryMapper> mapper;
|
||||
property_t<DSP1MemoryMapper> dsp1_mapper;
|
||||
readonly<bool> has_bsx_slot;
|
||||
readonly<bool> has_superfx;
|
||||
readonly<bool> has_sa1;
|
||||
readonly<bool> has_srtc;
|
||||
readonly<bool> has_sdd1;
|
||||
readonly<bool> has_spc7110;
|
||||
readonly<bool> has_spc7110rtc;
|
||||
readonly<bool> has_cx4;
|
||||
readonly<bool> has_dsp1;
|
||||
readonly<bool> has_dsp2;
|
||||
readonly<bool> has_dsp3;
|
||||
readonly<bool> has_dsp4;
|
||||
readonly<bool> has_obc1;
|
||||
readonly<bool> has_st010;
|
||||
readonly<bool> has_st011;
|
||||
readonly<bool> has_st018;
|
||||
bool has_21fx() const;
|
||||
|
||||
property_t<bool> has_bsx_slot;
|
||||
property_t<bool> has_superfx;
|
||||
property_t<bool> has_sa1;
|
||||
property_t<bool> has_srtc;
|
||||
property_t<bool> has_sdd1;
|
||||
property_t<bool> has_spc7110, has_spc7110rtc;
|
||||
property_t<bool> has_cx4;
|
||||
property_t<bool> has_dsp1, has_dsp2, has_dsp3, has_dsp4;
|
||||
property_t<bool> has_obc1;
|
||||
property_t<bool> has_st010, has_st011, has_st018;
|
||||
|
||||
//main interface
|
||||
void load(Mode);
|
||||
void unload();
|
||||
Type detect_image_type(uint8_t *data, unsigned size) const;
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
|
||||
private:
|
||||
struct cartinfo_t {
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
unsigned rom_size, ram_size;
|
||||
|
||||
bool bsx_slot;
|
||||
bool superfx;
|
||||
bool sa1;
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110, spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1, dsp2, dsp3, dsp4;
|
||||
bool obc1;
|
||||
bool st010, st011, st018;
|
||||
|
||||
void reset();
|
||||
cartinfo_t();
|
||||
};
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
@@ -114,10 +96,13 @@ private:
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const;
|
||||
unsigned ram_size;
|
||||
void read_header(const uint8_t *data, unsigned size);
|
||||
unsigned find_header(const uint8_t *data, unsigned size) const;
|
||||
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr) const;
|
||||
void set_cartinfo(const cartinfo_t&);
|
||||
|
||||
unsigned gameboy_ram_size() const;
|
||||
unsigned gameboy_rtc_size() const;
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
@@ -125,7 +110,7 @@ namespace memory {
|
||||
extern MappedRAM bsxflash, bsxram, bsxpram;
|
||||
extern MappedRAM stArom, stAram;
|
||||
extern MappedRAM stBrom, stBram;
|
||||
extern MappedRAM gbrom, gbram;
|
||||
extern MappedRAM gbrom, gbram, gbrtc;
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
||||
|
22
src/cartridge/gameboyheader.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
unsigned Cartridge::gameboy_ram_size() const {
|
||||
if(memory::gbrom.size() < 512) return 0;
|
||||
switch(memory::gbrom[0x0149]) {
|
||||
case 0x00: return 0 * 1024;
|
||||
case 0x01: return 8 * 1024;
|
||||
case 0x02: return 8 * 1024;
|
||||
case 0x03: return 32 * 1024;
|
||||
case 0x04: return 128 * 1024;
|
||||
case 0x05: return 128 * 1024;
|
||||
default: return 128 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Cartridge::gameboy_rtc_size() const {
|
||||
if(memory::gbrom.size() < 512) return 0;
|
||||
if(memory::gbrom[0x0147] == 0x0f || memory::gbrom[0x0147] == 0x10) return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,7 +1,28 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) const {
|
||||
info.reset();
|
||||
void Cartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_srtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_spc7110rtc = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
@@ -10,23 +31,23 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
info.type = TypeGameBoy;
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8 mapper = data[index + Mapper];
|
||||
const uint8 mapperid = data[index + Mapper];
|
||||
const uint8 rom_type = data[index + RomType];
|
||||
const uint8 rom_size = data[index + RomSize];
|
||||
const uint8 company = data[index + Company];
|
||||
const uint8 region = data[index + CartRegion] & 0x7f;
|
||||
const uint8 regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
info.ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(info.ram_size == 1024) info.ram_size = 0; //no RAM present, eg RamSize == 0
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
@@ -37,9 +58,9 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
info.type = TypeBsx;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC; //BS-X only released in Japan
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -52,21 +73,26 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
info.type = TypeSufamiTurboBios;
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
info.type = TypeSufamiTurbo;
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
info.mapper = STROM;
|
||||
info.region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled internally by load_cart_st();
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
info.type = TypeSuperGameBoyBios;
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,118 +106,119 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size
|
||||
uint8 n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
info.bsx_slot = true;
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(info.bsx_slot == true) {
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
info.type = TypeBsxBios;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
info.type = TypeBsxSlotted;
|
||||
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
info.type = TypeNormal;
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapper == 0x32) {
|
||||
info.mapper = ExLoROM;
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
info.mapper = LoROM;
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
info.mapper = HiROM;
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
info.mapper = ExHiROM;
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
info.superfx = true;
|
||||
info.mapper = SuperFXROM;
|
||||
info.ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(info.ram_size == 1024) info.ram_size = 0;
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapper == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
info.sa1 = true;
|
||||
info.mapper = SA1ROM;
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapper == 0x35 && rom_type == 0x55) {
|
||||
info.srtc = true;
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_srtc = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
info.sdd1 = true;
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
info.spc7110 = true;
|
||||
info.spc7110rtc = (rom_type == 0xf9);
|
||||
info.mapper = SPC7110ROM;
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_spc7110rtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0xf3) {
|
||||
info.cx4 = true;
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapper == 0x20 || mapper == 0x21) && rom_type == 0x03) {
|
||||
info.dsp1 = true;
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
info.dsp1 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
info.dsp1 = true;
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(info.dsp1 == true) {
|
||||
if((mapper & 0x2f) == 0x20 && 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(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0x05) {
|
||||
info.dsp2 = true;
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
info.dsp3 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x03) {
|
||||
info.dsp4 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0x25) {
|
||||
info.obc1 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
info.st010 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
info.st011 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf5) {
|
||||
info.st018 = true;
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
37
src/cartridge/serialization.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
void Cartridge::serialize(serializer &s) {
|
||||
if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) {
|
||||
s.array(memory::cartram.data(), memory::cartram.size());
|
||||
}
|
||||
|
||||
if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) {
|
||||
s.array(memory::cartrtc.data(), memory::cartrtc.size());
|
||||
}
|
||||
|
||||
if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) {
|
||||
s.array(memory::bsxram.data(), memory::bsxram.size());
|
||||
}
|
||||
|
||||
if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) {
|
||||
s.array(memory::bsxpram.data(), memory::bsxpram.size());
|
||||
}
|
||||
|
||||
if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) {
|
||||
s.array(memory::stAram.data(), memory::stAram.size());
|
||||
}
|
||||
|
||||
if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) {
|
||||
s.array(memory::stBram.data(), memory::stBram.size());
|
||||
}
|
||||
|
||||
if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) {
|
||||
s.array(memory::gbram.data(), memory::gbram.size());
|
||||
}
|
||||
|
||||
if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) {
|
||||
s.array(memory::gbrtc.data(), memory::gbrtc.size());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,3 +1,2 @@
|
||||
unsigned Cheat::count() const { return code.size(); }
|
||||
bool Cheat::active() const { return cheat_enabled; }
|
||||
bool Cheat::exists(unsigned addr) const { return mask[addr >> 3] & 1 << (addr & 7); }
|
||||
bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); }
|
||||
|
@@ -5,230 +5,73 @@ namespace SNES {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) {
|
||||
enabled = source.enabled;
|
||||
code = source.code;
|
||||
desc = source.desc;
|
||||
count = source.count;
|
||||
|
||||
addr.reset();
|
||||
data.reset();
|
||||
for(unsigned n = 0; n < count; n++) {
|
||||
addr[n] = source.addr[n];
|
||||
data[n] = source.data[n];
|
||||
}
|
||||
|
||||
return *this;
|
||||
bool Cheat::enabled() const {
|
||||
return system_enabled;
|
||||
}
|
||||
|
||||
//used to sort cheat code list by description
|
||||
bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) {
|
||||
return strcmp(desc, source.desc) < 0;
|
||||
void Cheat::enable(bool state) {
|
||||
system_enabled = state;
|
||||
cheat_enabled = system_enabled && code_enabled;
|
||||
}
|
||||
|
||||
//parse item ("0123-4567+89AB-CDEF"), return cheat_t item
|
||||
//return true if code is valid, false otherwise
|
||||
bool Cheat::decode(const char *s, Cheat::cheat_t &item) const {
|
||||
item.enabled = false;
|
||||
item.count = 0;
|
||||
void Cheat::synchronize() {
|
||||
memset(bitmask, 0x00, sizeof bitmask);
|
||||
code_enabled = false;
|
||||
|
||||
string code = s;
|
||||
code.replace(" ", "");
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
const CheatCode &code = operator[](i);
|
||||
if(code.enabled == false) continue;
|
||||
|
||||
lstring list;
|
||||
list.split("+", code);
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
code_enabled = true;
|
||||
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
unsigned addr;
|
||||
uint8_t data;
|
||||
type_t type;
|
||||
if(decode(list[n], addr, data, type) == false) {
|
||||
item.count = 0;
|
||||
return false;
|
||||
unsigned addr = mirror(code.addr[n]);
|
||||
bitmask[addr >> 3] |= 1 << (addr & 7);
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||
unsigned mirroraddr;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
||||
|
||||
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.addr[item.count] = addr;
|
||||
item.data[item.count] = data;
|
||||
item.count++;
|
||||
}
|
||||
|
||||
return true;
|
||||
cheat_enabled = system_enabled && code_enabled;
|
||||
}
|
||||
|
||||
//read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code.
|
||||
//returns true if cheat code was found, false if it was not.
|
||||
//when true, cheat code substitution value is stored in data.
|
||||
bool Cheat::read(unsigned addr, uint8_t &data) const {
|
||||
addr = mirror_address(addr);
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
if(enabled(i) == false) continue;
|
||||
bool Cheat::read(unsigned addr, uint8 &data) const {
|
||||
addr = mirror(addr);
|
||||
|
||||
for(unsigned n = 0; n < code[i].count; n++) {
|
||||
if(addr == mirror_address(code[i].addr[n])) {
|
||||
data = code[i].data[n];
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
const CheatCode &code = operator[](i);
|
||||
if(code.enabled == false) continue;
|
||||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
if(addr == mirror(code.addr[n])) {
|
||||
data = code.data[n];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//code not found, or code is disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============
|
||||
//master control
|
||||
//==============
|
||||
|
||||
//global cheat system enable/disable:
|
||||
//if disabled, *all* cheat codes are disabled;
|
||||
//otherwise only individually disabled codes are.
|
||||
|
||||
bool Cheat::enabled() const {
|
||||
return cheat_system_enabled;
|
||||
Cheat::Cheat() {
|
||||
system_enabled = true;
|
||||
synchronize();
|
||||
}
|
||||
|
||||
void Cheat::enable() {
|
||||
cheat_system_enabled = true;
|
||||
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
|
||||
}
|
||||
//===============
|
||||
//encode / decode
|
||||
//===============
|
||||
|
||||
void Cheat::disable() {
|
||||
cheat_system_enabled = false;
|
||||
cheat_enabled = false;
|
||||
}
|
||||
|
||||
//================================
|
||||
//cheat list manipulation routines
|
||||
//================================
|
||||
|
||||
void Cheat::add(bool enable, const char *code_, const char *desc_) {
|
||||
cheat_t item;
|
||||
decode(code_, item);
|
||||
|
||||
unsigned i = code.size();
|
||||
code[i] = item;
|
||||
code[i].enabled = enable;
|
||||
code[i].desc = desc_;
|
||||
code[i].code = code_;
|
||||
encode_description(code[i].desc);
|
||||
update(code[i]);
|
||||
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
void Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) {
|
||||
cheat_t item;
|
||||
decode(code_, item);
|
||||
|
||||
//disable current code and clear from code lookup table
|
||||
code[i].enabled = false;
|
||||
update(code[i]);
|
||||
|
||||
code[i] = item;
|
||||
code[i].enabled = enable;
|
||||
code[i].desc = desc_;
|
||||
code[i].code = code_;
|
||||
encode_description(code[i].desc);
|
||||
update(code[i]);
|
||||
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
bool Cheat::remove(unsigned i) {
|
||||
unsigned size = code.size();
|
||||
if(i >= size) return false; //also verifies size cannot be < 1
|
||||
|
||||
for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1];
|
||||
code.resize(size - 1);
|
||||
|
||||
update_cheat_status();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cheat::get(unsigned i, cheat_t &item) const {
|
||||
if(i >= code.size()) return false;
|
||||
|
||||
item = code[i];
|
||||
decode_description(item.desc);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================
|
||||
//cheat status modifier routines
|
||||
//==============================
|
||||
|
||||
bool Cheat::enabled(unsigned i) const {
|
||||
return (i < code.size() ? code[i].enabled : false);
|
||||
}
|
||||
|
||||
void Cheat::enable(unsigned i) {
|
||||
if(i >= code.size()) return;
|
||||
|
||||
code[i].enabled = true;
|
||||
update(code[i]);
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
void Cheat::disable(unsigned i) {
|
||||
if(i >= code.size()) return;
|
||||
|
||||
code[i].enabled = false;
|
||||
update(code[i]);
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
//===============================
|
||||
//cheat file load / save routines
|
||||
//
|
||||
//file format:
|
||||
//"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n
|
||||
//...
|
||||
//===============================
|
||||
|
||||
void Cheat::load(string data) {
|
||||
data.replace("\r", "");
|
||||
data.qreplace(" ", "");
|
||||
|
||||
lstring line;
|
||||
line.split("\n", data);
|
||||
for(unsigned i = 0; i < line.size(); i++) {
|
||||
lstring part;
|
||||
part.qsplit(",", line[i]);
|
||||
if(part.size() != 3) continue;
|
||||
trim(part[2], "\"");
|
||||
add(part[0] == "enabled", /* code = */ part[1], /* desc = */ part[2]);
|
||||
}
|
||||
}
|
||||
|
||||
string Cheat::save() const {
|
||||
string data;
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
data << (code[i].enabled ? "enabled," : "disabled,")
|
||||
<< code[i].code << ","
|
||||
<< "\"" << code[i].desc << "\"\r\n";
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void Cheat::clear() {
|
||||
cheat_enabled_code_exists = false;
|
||||
memset(mask, 0, 0x200000);
|
||||
code.reset();
|
||||
}
|
||||
|
||||
Cheat::Cheat() : cheat_system_enabled(true) {
|
||||
clear();
|
||||
}
|
||||
|
||||
//==================
|
||||
//internal functions
|
||||
//==================
|
||||
|
||||
//string <> binary code translation routines
|
||||
//decode() "7e123456" -> 0x7e123456
|
||||
//encode() 0x7e123456 -> "7e123456"
|
||||
|
||||
bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const {
|
||||
bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
|
||||
string t = s;
|
||||
strlower(t);
|
||||
|
||||
@@ -274,9 +117,11 @@ bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) c
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef ischr
|
||||
}
|
||||
|
||||
bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
|
||||
bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) {
|
||||
char t[16];
|
||||
|
||||
if(type == ProActionReplay) {
|
||||
@@ -306,90 +151,46 @@ bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
|
||||
}
|
||||
}
|
||||
|
||||
//speed up S-CPU memory reads by disabling cheat code lookup when either:
|
||||
//a) cheat system is disabled by user, or b) no enabled cheat codes exist
|
||||
void Cheat::update_cheat_status() {
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
if(code[i].enabled) {
|
||||
cheat_enabled_code_exists = true;
|
||||
cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
|
||||
return;
|
||||
//========
|
||||
//internal
|
||||
//========
|
||||
|
||||
unsigned Cheat::mirror(unsigned addr) const {
|
||||
//$00-3f|80-bf:0000-1fff -> $7e:0000-1fff
|
||||
if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff));
|
||||
return addr;
|
||||
}
|
||||
|
||||
//=========
|
||||
//CheatCode
|
||||
//=========
|
||||
|
||||
bool CheatCode::operator=(string s) {
|
||||
addr.reset();
|
||||
data.reset();
|
||||
|
||||
lstring list;
|
||||
list.split("+", s.replace(" ", ""));
|
||||
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
unsigned addr_;
|
||||
uint8 data_;
|
||||
Cheat::Type type_;
|
||||
if(Cheat::decode(list[i], addr_, data_, type_) == false) {
|
||||
addr.reset();
|
||||
data.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
addr.add(addr_);
|
||||
data.add(data_);
|
||||
}
|
||||
cheat_enabled_code_exists = false;
|
||||
cheat_enabled = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//address lookup table manipulation and mirroring
|
||||
//mirror_address() 0x000000 -> 0x7e0000
|
||||
//set() enable specified address, mirror accordingly
|
||||
//clear() disable specified address, mirror accordingly
|
||||
unsigned Cheat::mirror_address(unsigned addr) const {
|
||||
if((addr & 0x40e000) != 0x0000) return addr;
|
||||
//8k WRAM mirror
|
||||
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
||||
return (0x7e0000 + (addr & 0x1fff));
|
||||
CheatCode::CheatCode() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
//updates mask[] table enabled bits;
|
||||
//must be called after modifying item.enabled state.
|
||||
void Cheat::update(const cheat_t &item) {
|
||||
for(unsigned n = 0; n < item.count; n++) {
|
||||
(item.enabled) ? set(item.addr[n]) : clear(item.addr[n]);
|
||||
}
|
||||
}
|
||||
|
||||
void Cheat::set(unsigned addr) {
|
||||
addr = mirror_address(addr);
|
||||
|
||||
mask[addr >> 3] |= 1 << (addr & 7);
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
unsigned mirror;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] |= 1 << (mirror & 7);
|
||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] |= 1 << (mirror & 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cheat::clear(unsigned addr) {
|
||||
addr = mirror_address(addr);
|
||||
|
||||
//if there is more than one cheat code using the same address,
|
||||
//(eg with a different override value) then do not clear code
|
||||
//lookup table entry.
|
||||
uint8_t 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]
|
||||
unsigned mirror;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//these two functions are used to safely store description text inside .cfg file format.
|
||||
|
||||
string& Cheat::encode_description(string &desc) const {
|
||||
desc.replace("\"", "\\q");
|
||||
desc.replace("\n", "\\n");
|
||||
return desc;
|
||||
}
|
||||
|
||||
string& Cheat::decode_description(string &desc) const {
|
||||
desc.replace("\\q", "\"");
|
||||
desc.replace("\\n", "\n");
|
||||
return desc;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
@@ -1,69 +1,35 @@
|
||||
class Cheat {
|
||||
struct CheatCode {
|
||||
bool enabled;
|
||||
array<unsigned> addr;
|
||||
array<uint8> data;
|
||||
|
||||
bool operator=(string);
|
||||
CheatCode();
|
||||
};
|
||||
|
||||
class Cheat : public vector<CheatCode> {
|
||||
public:
|
||||
enum type_t {
|
||||
ProActionReplay,
|
||||
GameGenie,
|
||||
};
|
||||
|
||||
struct cheat_t {
|
||||
bool enabled;
|
||||
string code;
|
||||
string desc;
|
||||
|
||||
unsigned count;
|
||||
array<unsigned> addr;
|
||||
array<uint8_t> data;
|
||||
|
||||
cheat_t& operator=(const cheat_t&);
|
||||
bool operator<(const cheat_t&);
|
||||
};
|
||||
|
||||
bool decode(const char *s, cheat_t &item) const;
|
||||
bool read(unsigned addr, uint8_t &data) const;
|
||||
enum Type { ProActionReplay, GameGenie };
|
||||
|
||||
bool enabled() const;
|
||||
void enable();
|
||||
void disable();
|
||||
void enable(bool);
|
||||
void synchronize();
|
||||
bool read(unsigned, uint8&) const;
|
||||
|
||||
inline unsigned count() const;
|
||||
inline bool active() const;
|
||||
inline bool exists(unsigned addr) const;
|
||||
|
||||
void add(bool enable, const char *code, const char *desc);
|
||||
void edit(unsigned i, bool enable, const char *code, const char *desc);
|
||||
bool remove(unsigned i);
|
||||
bool get(unsigned i, cheat_t &item) const;
|
||||
|
||||
bool enabled(unsigned i) const;
|
||||
void enable(unsigned i);
|
||||
void disable(unsigned i);
|
||||
|
||||
void load(string data);
|
||||
string save() const;
|
||||
void clear();
|
||||
|
||||
Cheat();
|
||||
|
||||
static bool decode(const char*, unsigned&, uint8&, Type&);
|
||||
static bool encode(string&, unsigned, uint8, Type);
|
||||
|
||||
private:
|
||||
bool cheat_enabled; //cheat_enabled == (cheat_enabled_code_exists && cheat_system_enabled);
|
||||
bool cheat_enabled_code_exists;
|
||||
bool cheat_system_enabled;
|
||||
|
||||
uint8_t mask[0x200000];
|
||||
vector<cheat_t> code;
|
||||
|
||||
bool decode(const char *str, unsigned &addr, uint8_t &data, type_t &type) const;
|
||||
bool encode(string &str, unsigned addr, uint8_t data, type_t type) const;
|
||||
|
||||
void update_cheat_status();
|
||||
unsigned mirror_address(unsigned addr) const;
|
||||
|
||||
void update(const cheat_t& item);
|
||||
void set(unsigned addr);
|
||||
void clear(unsigned addr);
|
||||
|
||||
string& encode_description(string &desc) const;
|
||||
string& decode_description(string &desc) const;
|
||||
uint8 bitmask[0x200000];
|
||||
bool system_enabled;
|
||||
bool code_enabled;
|
||||
bool cheat_enabled;
|
||||
unsigned mirror(unsigned) const;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
|
195
src/chip/21fx/21fx.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include <../base.hpp>
|
||||
|
||||
//B-bus interface
|
||||
|
||||
//$21f0 command port (r/w)
|
||||
//-------------------------
|
||||
//$00 set data port address (sr[3-0] = address)
|
||||
//$01 set audio track number (sr[1-0] = track number)
|
||||
//$02 set volume (sr[1] = left, sr[0] = right)
|
||||
//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat)
|
||||
//
|
||||
//d7 = data port busy
|
||||
//d6 = audio port busy
|
||||
//d5 = audio playing
|
||||
//d4 = reserved (0)
|
||||
//d3-d0 = version (0)
|
||||
//
|
||||
//
|
||||
//$21f1 parameter port (w)
|
||||
//-------------------------
|
||||
//(shift register)
|
||||
//
|
||||
//
|
||||
//$21f2 data port (r)
|
||||
//--------------------
|
||||
//(auto-increment read port)
|
||||
|
||||
//A-bus interface
|
||||
|
||||
//$2200 command port (r/w)
|
||||
//-------------------------
|
||||
//$00 set data port address (sr[3-0] = address)
|
||||
//$01 set audio track number (sr[1-0] = track number)
|
||||
//$02 set volume (sr[1] = left, sr[0] = right)
|
||||
//$03 set audio state (sr[0].d1 = pause, sr[0].d0 = repeat)
|
||||
//
|
||||
//d7 = data port busy
|
||||
//d6 = audio port busy
|
||||
//d5 = audio playing
|
||||
//d4 = reserved (0)
|
||||
//d3-d0 = version (0)
|
||||
//
|
||||
//$2201 data port (r/w)
|
||||
//----------------------
|
||||
//(shift register)
|
||||
//
|
||||
//(auto-increment read port)
|
||||
|
||||
#define S21FX_CPP
|
||||
namespace SNES {
|
||||
|
||||
S21fx s21fx;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
void S21fx::enter() {
|
||||
scheduler.clock.cop_freq = 44100;
|
||||
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) {
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
int16 left = 0, right = 0;
|
||||
|
||||
if((mmio.status & AudioPlaying) && !mmio.audio_pause) {
|
||||
if(audiofile.open()) {
|
||||
if(audiofile.end()) {
|
||||
if(!mmio.audio_repeat) mmio.status &= ~AudioPlaying;
|
||||
audiofile.seek(mmio.audio_offset = 58);
|
||||
} else {
|
||||
mmio.audio_offset += 4;
|
||||
left = audiofile.readl(2);
|
||||
right = audiofile.readl(2);
|
||||
}
|
||||
} else {
|
||||
mmio.status &= ~AudioPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
left = sclamp<16>((double)left * (double)mmio.audio_volume_left / 255.0);
|
||||
right = sclamp<16>((double)right * (double)mmio.audio_volume_right / 255.0);
|
||||
|
||||
audio.coprocessor_sample(left, right);
|
||||
scheduler.addclocks_cop(1);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
}
|
||||
|
||||
void S21fx::init() {
|
||||
}
|
||||
|
||||
void S21fx::enable() {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(44100.0);
|
||||
|
||||
for(unsigned i = 0x21f0; i <= 0x21f7; i++) {
|
||||
memory::mmio.map(i, *this);
|
||||
}
|
||||
|
||||
memory::mmio.map(0x2200, *this);
|
||||
memory::mmio.map(0x2201, *this);
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
datafile.open(string() << basepath << "21fx.bin", file::mode_read);
|
||||
}
|
||||
|
||||
void S21fx::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void S21fx::reset() {
|
||||
mmio.status = DataPortBusy | AudioBusy;
|
||||
mmio.shift_register = 0;
|
||||
|
||||
mmio.data_offset = 0;
|
||||
mmio.audio_offset = 0;
|
||||
mmio.audio_track = 0;
|
||||
mmio.audio_volume_left = 255;
|
||||
mmio.audio_volume_right = 255;
|
||||
mmio.audio_repeat = false;
|
||||
mmio.audio_pause = false;
|
||||
}
|
||||
|
||||
uint8 S21fx::mmio_read(unsigned addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if((addr == 0x21f0) || (addr == 0x2200)) {
|
||||
return mmio.status | 0x00;
|
||||
}
|
||||
|
||||
if((addr == 0x21f2) || (addr == 0x2201)) {
|
||||
if(mmio.status & DataPortBusy) return 0x00;
|
||||
mmio.data_offset++;
|
||||
if(datafile.open()) return datafile.read();
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void S21fx::mmio_write(unsigned addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if((addr == 0x21f0) || (addr == 0x2200)) {
|
||||
if(data == 0x00) {
|
||||
mmio.data_offset = mmio.shift_register & 0xffffffff;
|
||||
if(datafile.open()) {
|
||||
datafile.seek(mmio.data_offset);
|
||||
}
|
||||
mmio.status &= ~DataPortBusy;
|
||||
}
|
||||
|
||||
if(data == 0x01) {
|
||||
mmio.audio_track = mmio.shift_register & 0xffff;
|
||||
if(audiofile.open()) audiofile.close();
|
||||
char track[16];
|
||||
sprintf(track, "%.5u", mmio.audio_track);
|
||||
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
|
||||
audiofile.seek(mmio.audio_offset = 58); //skip WAV header
|
||||
}
|
||||
mmio.status &= ~(AudioBusy | AudioPlaying);
|
||||
}
|
||||
|
||||
if(data == 0x02) {
|
||||
mmio.audio_volume_left = mmio.shift_register >> 8;
|
||||
mmio.audio_volume_right = mmio.shift_register >> 0;
|
||||
}
|
||||
|
||||
if(data == 0x03) {
|
||||
mmio.status |= AudioPlaying;
|
||||
mmio.audio_repeat = mmio.shift_register & 1;
|
||||
mmio.audio_pause = mmio.shift_register & 2;
|
||||
}
|
||||
|
||||
mmio.shift_register = 0;
|
||||
}
|
||||
|
||||
if((addr == 0x21f1) || (addr == 0x2201)) {
|
||||
mmio.shift_register = (mmio.shift_register << 8) | data;
|
||||
}
|
||||
}
|
||||
|
||||
void S21fx::base(const string& path) {
|
||||
basepath = path;
|
||||
}
|
||||
|
||||
bool S21fx::exists() {
|
||||
return file::exists(string() << basepath << "21fx.bin");
|
||||
}
|
||||
|
||||
S21fx::S21fx() {
|
||||
}
|
||||
|
||||
}
|
44
src/chip/21fx/21fx.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
class S21fx : public MMIO {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
void base(const string &path);
|
||||
bool exists();
|
||||
|
||||
void serialize(serializer&);
|
||||
S21fx();
|
||||
|
||||
private:
|
||||
string basepath;
|
||||
file datafile;
|
||||
file audiofile;
|
||||
|
||||
enum Flag {
|
||||
DataPortBusy = 0x80,
|
||||
AudioBusy = 0x40,
|
||||
AudioPlaying = 0x20,
|
||||
};
|
||||
|
||||
struct MMIO {
|
||||
uint8 status;
|
||||
uint64 shift_register;
|
||||
|
||||
uint32 data_offset;
|
||||
uint32 audio_offset;
|
||||
uint16 audio_track;
|
||||
uint8 audio_volume_left;
|
||||
uint8 audio_volume_right;
|
||||
bool audio_repeat;
|
||||
bool audio_pause;
|
||||
} mmio;
|
||||
};
|
||||
|
||||
extern S21fx s21fx;
|
31
src/chip/21fx/serialization.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifdef S21FX_CPP
|
||||
|
||||
void S21fx::serialize(serializer &s) {
|
||||
s.integer(mmio.status);
|
||||
s.integer(mmio.shift_register);
|
||||
|
||||
s.integer(mmio.data_offset);
|
||||
s.integer(mmio.audio_offset);
|
||||
s.integer(mmio.audio_track);
|
||||
s.integer(mmio.audio_volume_left);
|
||||
s.integer(mmio.audio_volume_right);
|
||||
s.integer(mmio.audio_repeat);
|
||||
s.integer(mmio.audio_pause);
|
||||
|
||||
//flush file handles and indices, as a different track may be playing,
|
||||
//or the file offsets may be at the wrong location ...
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
if(datafile.open(string() << basepath << "21fx.bin", file::mode_read)) {
|
||||
datafile.seek(mmio.data_offset);
|
||||
}
|
||||
|
||||
if(audiofile.open()) audiofile.close();
|
||||
char track[16];
|
||||
sprintf(track, "%.5u", mmio.audio_track);
|
||||
if(audiofile.open(string() << basepath << "audio" << track << ".wav", file::mode_read)) {
|
||||
audiofile.seek(mmio.audio_offset);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "sgb/sgb.hpp"
|
||||
#include "supergameboy/supergameboy.hpp"
|
||||
#include "superfx/superfx.hpp"
|
||||
#include "sa1/sa1.hpp"
|
||||
#include "bsx/bsx.hpp"
|
||||
@@ -14,3 +14,4 @@
|
||||
#include "st010/st010.hpp"
|
||||
#include "st011/st011.hpp"
|
||||
#include "st018/st018.hpp"
|
||||
#include "21fx/21fx.hpp"
|
||||
|
@@ -4,14 +4,14 @@ VBRBus vbrbus;
|
||||
SA1Bus sa1bus;
|
||||
|
||||
namespace memory {
|
||||
static StaticRAM iram(2048);
|
||||
//accessed by:
|
||||
static VectorSelectionPage vectorsp; //S-CPU + SA-1
|
||||
static CPUIRAM cpuiram; //S-CPU
|
||||
static SA1IRAM sa1iram; //SA-1
|
||||
static SA1BWRAM sa1bwram; //SA-1
|
||||
static CC1BWRAM cc1bwram; //S-CPU
|
||||
static BitmapRAM bitmapram; //SA-1
|
||||
StaticRAM iram(2048);
|
||||
//accessed by:
|
||||
VectorSelectionPage vectorsp; //S-CPU + SA-1
|
||||
CPUIRAM cpuiram; //S-CPU
|
||||
SA1IRAM sa1iram; //SA-1
|
||||
SA1BWRAM sa1bwram; //SA-1
|
||||
CC1BWRAM cc1bwram; //S-CPU
|
||||
BitmapRAM bitmapram; //SA-1
|
||||
}
|
||||
|
||||
//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data.
|
||||
|
@@ -43,3 +43,14 @@ struct BitmapRAM : Memory {
|
||||
alwaysinline uint8 read(unsigned);
|
||||
alwaysinline void write(unsigned, uint8);
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
extern StaticRAM iram;
|
||||
|
||||
extern VectorSelectionPage vectorsp;
|
||||
extern CPUIRAM cpuiram;
|
||||
extern SA1IRAM sa1iram;
|
||||
extern SA1BWRAM sa1bwram;
|
||||
extern CC1BWRAM cc1bwram;
|
||||
extern BitmapRAM bitmapram;
|
||||
};
|
||||
|
@@ -5,6 +5,7 @@ namespace SNES {
|
||||
|
||||
SA1 sa1;
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "bus/bus.cpp"
|
||||
#include "dma/dma.cpp"
|
||||
#include "memory/memory.cpp"
|
||||
@@ -12,10 +13,15 @@ SA1 sa1;
|
||||
|
||||
void SA1::enter() {
|
||||
while(true) {
|
||||
while(mmio.sa1_rdyb || mmio.sa1_resb) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) {
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(mmio.sa1_rdyb || mmio.sa1_resb) {
|
||||
//SA-1 co-processor is asleep
|
||||
tick();
|
||||
scheduler.sync_copcpu();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(status.interrupt_pending) {
|
||||
@@ -139,7 +145,7 @@ void SA1::reset() {
|
||||
regs.e = 1;
|
||||
regs.mdr = 0x00;
|
||||
regs.wai = false;
|
||||
update_table();
|
||||
CPUcore::update_table();
|
||||
|
||||
status.tick_counter = 0;
|
||||
|
||||
|
@@ -30,6 +30,7 @@ public:
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
SA1();
|
||||
};
|
||||
|
||||
|
150
src/chip/sa1/serialization.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifdef SA1_CPP
|
||||
|
||||
void SA1::serialize(serializer &s) {
|
||||
CPUcore::core_serialize(s);
|
||||
|
||||
//sa1.hpp
|
||||
s.integer(status.tick_counter);
|
||||
|
||||
s.integer(status.interrupt_pending);
|
||||
s.integer(status.interrupt_vector);
|
||||
|
||||
s.integer(status.scanlines);
|
||||
s.integer(status.vcounter);
|
||||
s.integer(status.hcounter);
|
||||
|
||||
//bus/bus.hpp
|
||||
s.array(memory::iram.data(), memory::iram.size());
|
||||
|
||||
memory::vectorsp.sync();
|
||||
|
||||
s.integer(memory::cc1bwram.dma);
|
||||
|
||||
//dma/dma.hpp
|
||||
s.integer(dma.line);
|
||||
|
||||
//mmio/mmio.hpp
|
||||
s.integer(mmio.sa1_irq);
|
||||
s.integer(mmio.sa1_rdyb);
|
||||
s.integer(mmio.sa1_resb);
|
||||
s.integer(mmio.sa1_nmi);
|
||||
s.integer(mmio.smeg);
|
||||
|
||||
s.integer(mmio.cpu_irqen);
|
||||
s.integer(mmio.chdma_irqen);
|
||||
|
||||
s.integer(mmio.cpu_irqcl);
|
||||
s.integer(mmio.chdma_irqcl);
|
||||
|
||||
s.integer(mmio.crv);
|
||||
|
||||
s.integer(mmio.cnv);
|
||||
|
||||
s.integer(mmio.civ);
|
||||
|
||||
s.integer(mmio.cpu_irq);
|
||||
s.integer(mmio.cpu_ivsw);
|
||||
s.integer(mmio.cpu_nvsw);
|
||||
s.integer(mmio.cmeg);
|
||||
|
||||
s.integer(mmio.sa1_irqen);
|
||||
s.integer(mmio.timer_irqen);
|
||||
s.integer(mmio.dma_irqen);
|
||||
s.integer(mmio.sa1_nmien);
|
||||
|
||||
s.integer(mmio.sa1_irqcl);
|
||||
s.integer(mmio.timer_irqcl);
|
||||
s.integer(mmio.dma_irqcl);
|
||||
s.integer(mmio.sa1_nmicl);
|
||||
|
||||
s.integer(mmio.snv);
|
||||
|
||||
s.integer(mmio.siv);
|
||||
|
||||
s.integer(mmio.hvselb);
|
||||
s.integer(mmio.ven);
|
||||
s.integer(mmio.hen);
|
||||
|
||||
s.integer(mmio.hcnt);
|
||||
|
||||
s.integer(mmio.vcnt);
|
||||
|
||||
s.integer(mmio.cbmode);
|
||||
s.integer(mmio.cb);
|
||||
|
||||
s.integer(mmio.dbmode);
|
||||
s.integer(mmio.db);
|
||||
|
||||
s.integer(mmio.ebmode);
|
||||
s.integer(mmio.eb);
|
||||
|
||||
s.integer(mmio.fbmode);
|
||||
s.integer(mmio.fb);
|
||||
|
||||
s.integer(mmio.sbm);
|
||||
|
||||
s.integer(mmio.sw46);
|
||||
s.integer(mmio.cbm);
|
||||
|
||||
s.integer(mmio.swen);
|
||||
|
||||
s.integer(mmio.cwen);
|
||||
|
||||
s.integer(mmio.bwp);
|
||||
|
||||
s.integer(mmio.siwp);
|
||||
|
||||
s.integer(mmio.ciwp);
|
||||
|
||||
s.integer(mmio.dmaen);
|
||||
s.integer(mmio.dprio);
|
||||
s.integer(mmio.cden);
|
||||
s.integer(mmio.cdsel);
|
||||
s.integer(mmio.dd);
|
||||
s.integer(mmio.sd);
|
||||
|
||||
s.integer(mmio.chdend);
|
||||
s.integer(mmio.dmasize);
|
||||
s.integer(mmio.dmacb);
|
||||
|
||||
s.integer(mmio.dsa);
|
||||
|
||||
s.integer(mmio.dda);
|
||||
|
||||
s.integer(mmio.dtc);
|
||||
|
||||
s.integer(mmio.bbf);
|
||||
|
||||
s.array(mmio.brf);
|
||||
|
||||
s.integer(mmio.acm);
|
||||
s.integer(mmio.md);
|
||||
|
||||
s.integer(mmio.ma);
|
||||
|
||||
s.integer(mmio.mb);
|
||||
|
||||
s.integer(mmio.hl);
|
||||
s.integer(mmio.vb);
|
||||
|
||||
s.integer(mmio.va);
|
||||
s.integer(mmio.vbit);
|
||||
|
||||
s.integer(mmio.cpu_irqfl);
|
||||
s.integer(mmio.chdma_irqfl);
|
||||
|
||||
s.integer(mmio.sa1_irqfl);
|
||||
s.integer(mmio.timer_irqfl);
|
||||
s.integer(mmio.dma_irqfl);
|
||||
s.integer(mmio.sa1_nmifl);
|
||||
|
||||
s.integer(mmio.hcr);
|
||||
|
||||
s.integer(mmio.vcr);
|
||||
|
||||
s.integer(mmio.mr);
|
||||
|
||||
s.integer(mmio.overflow);
|
||||
}
|
||||
|
||||
#endif
|
@@ -55,10 +55,10 @@ uint8 SDD1::mmio_read(unsigned addr) {
|
||||
}
|
||||
|
||||
switch(addr) {
|
||||
case 0x4804: return (mmc[0] >> 20) & 7;
|
||||
case 0x4805: return (mmc[1] >> 20) & 7;
|
||||
case 0x4806: return (mmc[2] >> 20) & 7;
|
||||
case 0x4807: return (mmc[3] >> 20) & 7;
|
||||
case 0x4804: return mmc[0] >> 20;
|
||||
case 0x4805: return mmc[1] >> 20;
|
||||
case 0x4806: return mmc[2] >> 20;
|
||||
case 0x4807: return mmc[3] >> 20;
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
@@ -84,10 +84,10 @@ void SDD1::mmio_write(unsigned addr, uint8 data) {
|
||||
case 0x4800: sdd1_enable = data; break;
|
||||
case 0x4801: xfer_enable = data; break;
|
||||
|
||||
case 0x4804: mmc[0] = (data & 7) << 20; break;
|
||||
case 0x4805: mmc[1] = (data & 7) << 20; break;
|
||||
case 0x4806: mmc[2] = (data & 7) << 20; break;
|
||||
case 0x4807: mmc[3] = (data & 7) << 20; break;
|
||||
case 0x4804: mmc[0] = data << 20; break;
|
||||
case 0x4805: mmc[1] = data << 20; break;
|
||||
case 0x4806: mmc[2] = data << 20; break;
|
||||
case 0x4807: mmc[3] = data << 20; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,65 +0,0 @@
|
||||
#include <../base.hpp>
|
||||
|
||||
#define SGB_CPP
|
||||
namespace SNES {
|
||||
|
||||
SuperGameBoy sgb;
|
||||
|
||||
void SuperGameBoy::enter() {
|
||||
while(true) {
|
||||
if(sgb_run) {
|
||||
unsigned samples = sgb_run(samplebuffer, 16);
|
||||
scheduler.addclocks_cop(samples * 10);
|
||||
scheduler.sync_copcpu();
|
||||
} else {
|
||||
scheduler.addclocks_cop(64 * 1024 * 1024);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t SuperGameBoy::read(unsigned addr) {
|
||||
addr &= 0xffff;
|
||||
if(sgb_read) return sgb_read(addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void SuperGameBoy::write(unsigned addr, uint8_t data) {
|
||||
addr &= 0xffff;
|
||||
if(sgb_write) return sgb_write(addr, data);
|
||||
}
|
||||
|
||||
void SuperGameBoy::init() {
|
||||
if(libsgb.open("SuperGameBoy")) {
|
||||
sgb_init = libsgb.sym("sgb_init");
|
||||
sgb_term = libsgb.sym("sgb_term");
|
||||
sgb_power = libsgb.sym("sgb_power");
|
||||
sgb_reset = libsgb.sym("sgb_reset");
|
||||
sgb_read = libsgb.sym("sgb_read");
|
||||
sgb_write = libsgb.sym("sgb_write");
|
||||
sgb_run = libsgb.sym("sgb_run");
|
||||
}
|
||||
}
|
||||
|
||||
void SuperGameBoy::enable() {
|
||||
}
|
||||
|
||||
void SuperGameBoy::power() {
|
||||
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
|
||||
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
|
||||
|
||||
if(sgb_init) {
|
||||
sgb_init(SGB2,
|
||||
memory::gbrom.data(), memory::gbrom.size(),
|
||||
memory::gbram.data(), memory::gbram.size()
|
||||
);
|
||||
}
|
||||
|
||||
if(sgb_power) sgb_power();
|
||||
}
|
||||
|
||||
void SuperGameBoy::reset() {
|
||||
if(sgb_reset) sgb_reset();
|
||||
}
|
||||
|
||||
};
|
@@ -1,27 +0,0 @@
|
||||
class SuperGameBoy : public Memory {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
uint8_t read(unsigned addr);
|
||||
void write(unsigned addr, uint8_t data);
|
||||
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
library libsgb;
|
||||
uint32_t samplebuffer[4096];
|
||||
|
||||
enum { SGB1 = 0, SGB2 = 1 };
|
||||
function<bool (bool, uint8_t*, unsigned, uint8_t*, unsigned)> sgb_init;
|
||||
function<void ()> sgb_term;
|
||||
function<void ()> sgb_power;
|
||||
function<void ()> sgb_reset;
|
||||
function<uint8_t (unsigned)> sgb_read;
|
||||
function<void (unsigned, uint8_t)> sgb_write;
|
||||
function<unsigned (uint32_t*, unsigned)> sgb_run;
|
||||
};
|
||||
|
||||
extern SuperGameBoy sgb;
|
@@ -535,17 +535,17 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
|
||||
|
||||
case 0x4831: {
|
||||
r4831 = data;
|
||||
dx_offset = datarom_addr((data & 7) * 0x100000);
|
||||
dx_offset = datarom_addr(data * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4832: {
|
||||
r4832 = data;
|
||||
ex_offset = datarom_addr((data & 7) * 0x100000);
|
||||
ex_offset = datarom_addr(data * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4833: {
|
||||
r4833 = data;
|
||||
fx_offset = datarom_addr((data & 7) * 0x100000);
|
||||
fx_offset = datarom_addr(data * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4834: r4834 = data; break;
|
||||
|
@@ -3,11 +3,11 @@
|
||||
SuperFXBus superfxbus;
|
||||
|
||||
namespace memory {
|
||||
static SuperFXGSUROM gsurom;
|
||||
static SuperFXGSURAM gsuram;
|
||||
static SuperFXCPUROM fxrom;
|
||||
static SuperFXCPURAM fxram;
|
||||
};
|
||||
SuperFXGSUROM gsurom;
|
||||
SuperFXGSURAM gsuram;
|
||||
SuperFXCPUROM fxrom;
|
||||
SuperFXCPURAM fxram;
|
||||
}
|
||||
|
||||
void SuperFXBus::init() {
|
||||
map(MapDirect, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped);
|
||||
@@ -34,7 +34,7 @@ unsigned SuperFXGSUROM::size() const {
|
||||
}
|
||||
|
||||
uint8 SuperFXGSUROM::read(unsigned addr) {
|
||||
while(!superfx.regs.scmr.ron) {
|
||||
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
@@ -42,7 +42,7 @@ uint8 SuperFXGSUROM::read(unsigned addr) {
|
||||
}
|
||||
|
||||
void SuperFXGSUROM::write(unsigned addr, uint8 data) {
|
||||
while(!superfx.regs.scmr.ron) {
|
||||
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SyncAll) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
@@ -54,7 +54,7 @@ unsigned SuperFXGSURAM::size() const {
|
||||
}
|
||||
|
||||
uint8 SuperFXGSURAM::read(unsigned addr) {
|
||||
while(!superfx.regs.scmr.ran) {
|
||||
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ uint8 SuperFXGSURAM::read(unsigned addr) {
|
||||
}
|
||||
|
||||
void SuperFXGSURAM::write(unsigned addr, uint8 data) {
|
||||
while(!superfx.regs.scmr.ran) {
|
||||
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SyncAll) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
|
@@ -25,3 +25,10 @@ struct SuperFXCPURAM : Memory {
|
||||
uint8 read(unsigned);
|
||||
void write(unsigned, uint8);
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
extern SuperFXGSUROM gsurom;
|
||||
extern SuperFXGSURAM gsuram;
|
||||
extern SuperFXCPUROM fxrom;
|
||||
extern SuperFXCPURAM fxram;
|
||||
}
|
||||
|
@@ -114,7 +114,7 @@ void SuperFX::op_bvs() {
|
||||
//$10-1f(b1): move rN
|
||||
template<int n> void SuperFX::op_to_r() {
|
||||
if(regs.sfr.b == 0) {
|
||||
regs.dreg = ®s.r[n];
|
||||
regs.dreg = n;
|
||||
} else {
|
||||
regs.r[n] = regs.sr();
|
||||
regs.reset();
|
||||
@@ -123,8 +123,8 @@ template<int n> void SuperFX::op_to_r() {
|
||||
|
||||
//$20-2f: with rN
|
||||
template<int n> void SuperFX::op_with_r() {
|
||||
regs.sreg = ®s.r[n];
|
||||
regs.dreg = ®s.r[n];
|
||||
regs.sreg = n;
|
||||
regs.dreg = n;
|
||||
regs.sfr.b = 1;
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ template<int n> void SuperFX::op_sms_r() {
|
||||
//$b0-bf(b1): moves rN
|
||||
template<int n> void SuperFX::op_from_r() {
|
||||
if(regs.sfr.b == 0) {
|
||||
regs.sreg = ®s.r[n];
|
||||
regs.sreg = n;
|
||||
} else {
|
||||
regs.dr() = regs.r[n];
|
||||
regs.sfr.ov = (regs.dr() & 0x80);
|
||||
|
@@ -149,17 +149,17 @@ struct regs_t {
|
||||
uint16 ramar; //RAM buffer address register
|
||||
uint8 ramdr; //RAM buffer data register
|
||||
|
||||
reg16_t *sreg, *dreg;
|
||||
reg16_t& sr() { return *sreg; } //source register (from)
|
||||
reg16_t& dr() { return *dreg; } //destination register (to)
|
||||
unsigned sreg, dreg;
|
||||
reg16_t& sr() { return r[sreg]; } //source register (from)
|
||||
reg16_t& dr() { return r[dreg]; } //destination register (to)
|
||||
|
||||
void reset() {
|
||||
sfr.b = 0;
|
||||
sfr.alt1 = 0;
|
||||
sfr.alt2 = 0;
|
||||
|
||||
sreg = &r[0];
|
||||
dreg = &r[0];
|
||||
sreg = 0;
|
||||
dreg = 0;
|
||||
}
|
||||
} regs;
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SUPERFX_CPP
|
||||
|
||||
void SuperFX::disassemble_opcode(char *output) {
|
||||
*output = 0;
|
||||
|
||||
@@ -273,3 +275,5 @@ void SuperFX::disassemble_alt3(char *output) {
|
||||
#undef op0
|
||||
#undef op1
|
||||
#undef op2
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SUPERFX_CPP
|
||||
|
||||
uint8 SuperFX::op_read(uint16 addr) {
|
||||
uint16 offset = addr - regs.cbr;
|
||||
if(offset < 512) {
|
||||
@@ -65,3 +67,5 @@ void SuperFX::memory_reset() {
|
||||
pixelcache[n].bitpend = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
94
src/chip/superfx/serialization.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifdef SUPERFX_CPP
|
||||
|
||||
void SuperFX::serialize(serializer &s) {
|
||||
//superfx.hpp
|
||||
s.integer(clockmode);
|
||||
s.integer(instruction_counter);
|
||||
|
||||
//core/registers.hpp
|
||||
s.integer(regs.pipeline);
|
||||
s.integer(regs.ramaddr);
|
||||
|
||||
s.integer(regs.r[ 0].data);
|
||||
s.integer(regs.r[ 1].data);
|
||||
s.integer(regs.r[ 2].data);
|
||||
s.integer(regs.r[ 3].data);
|
||||
s.integer(regs.r[ 4].data);
|
||||
s.integer(regs.r[ 5].data);
|
||||
s.integer(regs.r[ 6].data);
|
||||
s.integer(regs.r[ 7].data);
|
||||
s.integer(regs.r[ 8].data);
|
||||
s.integer(regs.r[ 9].data);
|
||||
s.integer(regs.r[10].data);
|
||||
s.integer(regs.r[11].data);
|
||||
s.integer(regs.r[12].data);
|
||||
s.integer(regs.r[13].data);
|
||||
s.integer(regs.r[14].data);
|
||||
s.integer(regs.r[15].data);
|
||||
|
||||
s.integer(regs.sfr.irq);
|
||||
s.integer(regs.sfr.b);
|
||||
s.integer(regs.sfr.ih);
|
||||
s.integer(regs.sfr.il);
|
||||
s.integer(regs.sfr.alt2);
|
||||
s.integer(regs.sfr.alt1);
|
||||
s.integer(regs.sfr.r);
|
||||
s.integer(regs.sfr.g);
|
||||
s.integer(regs.sfr.ov);
|
||||
s.integer(regs.sfr.s);
|
||||
s.integer(regs.sfr.cy);
|
||||
s.integer(regs.sfr.z);
|
||||
|
||||
s.integer(regs.pbr);
|
||||
s.integer(regs.rombr);
|
||||
s.integer(regs.rambr);
|
||||
s.integer(regs.cbr);
|
||||
s.integer(regs.scbr);
|
||||
|
||||
s.integer(regs.scmr.ht);
|
||||
s.integer(regs.scmr.ron);
|
||||
s.integer(regs.scmr.ran);
|
||||
s.integer(regs.scmr.md);
|
||||
|
||||
s.integer(regs.colr);
|
||||
|
||||
s.integer(regs.por.obj);
|
||||
s.integer(regs.por.freezehigh);
|
||||
s.integer(regs.por.highnibble);
|
||||
s.integer(regs.por.dither);
|
||||
s.integer(regs.por.transparent);
|
||||
|
||||
s.integer(regs.bramr);
|
||||
s.integer(regs.vcr);
|
||||
|
||||
s.integer(regs.cfgr.irq);
|
||||
s.integer(regs.cfgr.ms0);
|
||||
|
||||
s.integer(regs.clsr);
|
||||
|
||||
s.integer(regs.romcl);
|
||||
s.integer(regs.romdr);
|
||||
|
||||
s.integer(regs.ramcl);
|
||||
s.integer(regs.ramar);
|
||||
s.integer(regs.ramdr);
|
||||
|
||||
s.integer(regs.sreg);
|
||||
s.integer(regs.dreg);
|
||||
|
||||
s.array(cache.buffer);
|
||||
s.array(cache.valid);
|
||||
|
||||
for(unsigned i = 0; i < 2; i++) {
|
||||
s.integer(pixelcache[i].offset);
|
||||
s.integer(pixelcache[i].bitpend);
|
||||
s.array(pixelcache[i].data);
|
||||
}
|
||||
|
||||
//timing/timing.hpp
|
||||
s.integer(cache_access_speed);
|
||||
s.integer(memory_access_speed);
|
||||
s.integer(r15_modified);
|
||||
}
|
||||
|
||||
#endif
|
@@ -3,6 +3,7 @@
|
||||
#define SUPERFX_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "bus/bus.cpp"
|
||||
#include "core/core.cpp"
|
||||
#include "memory/memory.cpp"
|
||||
@@ -14,9 +15,14 @@ SuperFX superfx;
|
||||
|
||||
void SuperFX::enter() {
|
||||
while(true) {
|
||||
while(regs.sfr.g == 0) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) {
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(regs.sfr.g == 0) {
|
||||
add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
continue;
|
||||
}
|
||||
|
||||
(this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])();
|
||||
@@ -70,4 +76,4 @@ void SuperFX::reset() {
|
||||
timing_reset();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
@@ -15,6 +15,8 @@ public:
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
unsigned clockmode;
|
||||
unsigned instruction_counter;
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef SUPERFX_CPP
|
||||
|
||||
void SuperFX::add_clocks(unsigned clocks) {
|
||||
if(regs.romcl) {
|
||||
regs.romcl -= min(clocks, regs.romcl);
|
||||
@@ -91,3 +93,5 @@ void SuperFX::timing_reset() {
|
||||
regs.ramar = 0;
|
||||
regs.ramdr = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
140
src/chip/supergameboy/supergameboy.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <../base.hpp>
|
||||
|
||||
#define SUPERGAMEBOY_CPP
|
||||
namespace SNES {
|
||||
|
||||
SuperGameBoy supergameboy;
|
||||
|
||||
void SuperGameBoy::enter() {
|
||||
scheduler.clock.cop_freq = (version == SuperGameBoy1 ? 2147727 : 2097152);
|
||||
|
||||
if(!sgb_run) while(true) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) {
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
audio.coprocessor_sample(0, 0);
|
||||
scheduler.addclocks_cop(1);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SyncAll) {
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
unsigned samples = sgb_run(samplebuffer, 16);
|
||||
for(unsigned i = 0; i < samples; i++) {
|
||||
int16 left = samplebuffer[i] >> 0;
|
||||
int16 right = samplebuffer[i] >> 16;
|
||||
|
||||
//SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects
|
||||
audio.coprocessor_sample(left / 3, right / 3);
|
||||
}
|
||||
|
||||
scheduler.addclocks_cop(samples);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SuperGameBoy::mmio_read(unsigned addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2181) return mmio[0]->mmio_read(addr);
|
||||
if(addr == 0x2182) return mmio[1]->mmio_read(addr);
|
||||
if(addr == 0x420b) return mmio[2]->mmio_read(addr);
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void SuperGameBoy::mmio_write(unsigned addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2181) {
|
||||
row = (row & 0xff00) | (data << 0);
|
||||
mmio[0]->mmio_write(addr, data);
|
||||
}
|
||||
|
||||
if(addr == 0x2182) {
|
||||
row = (row & 0x00ff) | (data << 8);
|
||||
mmio[1]->mmio_write(addr, data);
|
||||
}
|
||||
|
||||
if(addr == 0x420b) {
|
||||
if(data == 0x10 && sgb_row) {
|
||||
if(row >= 0x5000 && row <= 0x6540) sgb_row((row - 0x5000) / 320);
|
||||
if(row >= 0x6800 && row <= 0x7d40) sgb_row((row - 0x6800) / 320);
|
||||
}
|
||||
mmio[2]->mmio_write(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SuperGameBoy::read(unsigned addr) {
|
||||
if(sgb_read) return sgb_read(addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void SuperGameBoy::write(unsigned addr, uint8 data) {
|
||||
if(sgb_write) sgb_write(addr, data);
|
||||
}
|
||||
|
||||
void SuperGameBoy::init() {
|
||||
if(open("supergameboy")) {
|
||||
sgb_rom = sym("sgb_rom");
|
||||
sgb_ram = sym("sgb_ram");
|
||||
sgb_rtc = sym("sgb_rtc");
|
||||
sgb_init = sym("sgb_init");
|
||||
sgb_term = sym("sgb_term");
|
||||
sgb_power = sym("sgb_power");
|
||||
sgb_reset = sym("sgb_reset");
|
||||
sgb_row = sym("sgb_row");
|
||||
sgb_read = sym("sgb_read");
|
||||
sgb_write = sym("sgb_write");
|
||||
sgb_run = sym("sgb_run");
|
||||
sgb_save = sym("sgb_save");
|
||||
sgb_serialize = sym("sgb_serialize");
|
||||
}
|
||||
}
|
||||
|
||||
void SuperGameBoy::enable() {
|
||||
mmio[0] = memory::mmio.mmio[0x2181 - 0x2000];
|
||||
mmio[1] = memory::mmio.mmio[0x2182 - 0x2000];
|
||||
mmio[2] = memory::mmio.mmio[0x420b - 0x2000];
|
||||
|
||||
memory::mmio.map(0x2181, *this);
|
||||
memory::mmio.map(0x2182, *this);
|
||||
memory::mmio.map(0x420b, *this);
|
||||
}
|
||||
|
||||
void SuperGameBoy::power() {
|
||||
version = (cartridge.type() == Cartridge::TypeSuperGameBoy1Bios ? SuperGameBoy1 : SuperGameBoy2);
|
||||
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(version == SuperGameBoy1 ? 2147727.0 : 2097152.0);
|
||||
|
||||
bus.map(Bus::MapDirect, 0x00, 0x3f, 0x6000, 0x7fff, *this);
|
||||
bus.map(Bus::MapDirect, 0x80, 0xbf, 0x6000, 0x7fff, *this);
|
||||
|
||||
sgb_rom(memory::gbrom.data(), memory::gbrom.size() == -1U ? 0 : memory::gbrom.size());
|
||||
sgb_ram(memory::gbram.data(), memory::gbram.size() == -1U ? 0 : memory::gbram.size());
|
||||
sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size());
|
||||
|
||||
if(sgb_init) sgb_init(version);
|
||||
if(sgb_power) sgb_power();
|
||||
}
|
||||
|
||||
void SuperGameBoy::reset() {
|
||||
if(sgb_reset) sgb_reset();
|
||||
}
|
||||
|
||||
void SuperGameBoy::unload() {
|
||||
if(sgb_term) sgb_term();
|
||||
}
|
||||
|
||||
void SuperGameBoy::serialize(serializer &s) {
|
||||
s.integer(row);
|
||||
s.integer(version);
|
||||
if(sgb_serialize) sgb_serialize(s);
|
||||
}
|
||||
|
||||
}
|
41
src/chip/supergameboy/supergameboy.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
class SuperGameBoy : public MMIO, public Memory, public library {
|
||||
public:
|
||||
void enter();
|
||||
|
||||
MMIO *mmio[3];
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
void unload();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint32_t samplebuffer[4096];
|
||||
unsigned row;
|
||||
bool version;
|
||||
|
||||
enum { SuperGameBoy1 = 0, SuperGameBoy2 = 1 };
|
||||
function<void (uint8_t*, unsigned)> sgb_rom;
|
||||
function<void (uint8_t*, unsigned)> sgb_ram;
|
||||
function<void (uint8_t*, unsigned)> sgb_rtc;
|
||||
function<bool (bool)> sgb_init;
|
||||
function<void ()> sgb_term;
|
||||
function<void ()> sgb_power;
|
||||
function<void ()> sgb_reset;
|
||||
function<void (unsigned)> sgb_row;
|
||||
function<uint8 (uint16)> sgb_read;
|
||||
function<void (uint16, uint8)> sgb_write;
|
||||
function<unsigned (uint32_t*, unsigned)> sgb_run;
|
||||
function<void ()> sgb_save;
|
||||
function<void (serializer&)> sgb_serialize;
|
||||
};
|
||||
|
||||
extern SuperGameBoy supergameboy;
|
@@ -5,7 +5,7 @@ namespace SNES {
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "disasm/disasm.cpp"
|
||||
#include "disassembler/disassembler.cpp"
|
||||
|
||||
#define L last_cycle();
|
||||
#define A 0
|
||||
|
@@ -2,7 +2,7 @@ class CPUcore {
|
||||
public:
|
||||
#include "registers.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "disasm/disasm.hpp"
|
||||
#include "disassembler/disassembler.hpp"
|
||||
|
||||
regs_t regs;
|
||||
reg24_t aa, rd;
|
||||
|
@@ -1,30 +0,0 @@
|
||||
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);
|
||||
uint16 dreadw(uint32 addr);
|
||||
uint32 dreadl(uint32 addr);
|
||||
uint32 decode(uint8 offset_type, uint32 addr);
|
||||
uint8 opcode_length();
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
uint8 CPUcore::dreadb(uint32 addr) {
|
||||
if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) {
|
||||
//$[00-3f|80-bf]:[2000-5fff]
|
||||
@@ -102,7 +104,7 @@ uint32 CPUcore::decode(uint8 offset_type, uint32 addr) {
|
||||
return(r & 0xffffff);
|
||||
}
|
||||
|
||||
void CPUcore::disassemble_opcode(char *output) {
|
||||
void CPUcore::disassemble_opcode(char *output, uint32 addr) {
|
||||
static reg24_t pc;
|
||||
char t[256];
|
||||
char *s = output;
|
||||
@@ -112,7 +114,7 @@ void CPUcore::disassemble_opcode(char *output) {
|
||||
return;
|
||||
}
|
||||
|
||||
pc.d = regs.pc.d;
|
||||
pc.d = addr;
|
||||
sprintf(s, "%.6x ", (uint32)pc.d);
|
||||
|
||||
uint8 op = dreadb(pc.d); pc.w++;
|
||||
@@ -477,3 +479,5 @@ uint8 CPUcore::opcode_length() {
|
||||
if(len == 6) return (regs.e || regs.p.x) ? 2 : 3;
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif
|
30
src/cpu/core/disassembler/disassembler.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
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, uint32 addr);
|
||||
uint8 dreadb(uint32 addr);
|
||||
uint16 dreadw(uint32 addr);
|
||||
uint32 dreadl(uint32 addr);
|
||||
uint32 decode(uint8 offset_type, uint32 addr);
|
||||
uint8 opcode_length();
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
void CPUcore::op_nop() {
|
||||
L op_io_irq();
|
||||
}
|
||||
@@ -346,3 +348,5 @@ void CPUcore::op_per_n() {
|
||||
op_writestackn(rd.h);
|
||||
L op_writestackn(rd.l);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
template<int bit, int val> void CPUcore::op_branch() {
|
||||
if((bool)(regs.p & bit) != val) {
|
||||
L rd.l = op_readpc();
|
||||
@@ -175,3 +177,5 @@ L rd.b = op_readstackn();
|
||||
regs.pc.b = rd.b;
|
||||
regs.pc.w = ++rd.w;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
template<void (CPUcore::*op)()> void CPUcore::op_read_const_b() {
|
||||
L rd.l = op_readpc();
|
||||
call(op);
|
||||
@@ -273,3 +275,5 @@ template<void (CPUcore::*op)()> void CPUcore::op_read_isry_w() {
|
||||
L rd.h = op_readdbr(aa.w + regs.y.w + 1);
|
||||
call(op);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
template<int n, int adjust> void CPUcore::op_adjust_imm_b() {
|
||||
L op_io_irq();
|
||||
regs.r[n].l += adjust;
|
||||
@@ -163,3 +165,5 @@ template<void (CPUcore::*op)()> void CPUcore::op_adjust_dpx_w() {
|
||||
op_writedp(dp + regs.x.w + 1, rd.h);
|
||||
L op_writedp(dp + regs.x.w + 0, rd.l);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#ifdef CPUCORE_CPP
|
||||
|
||||
template<int n> void CPUcore::op_write_addr_b() {
|
||||
aa.l = op_readpc();
|
||||
aa.h = op_readpc();
|
||||
@@ -193,3 +195,5 @@ void CPUcore::op_sta_isry_w() {
|
||||
op_writedbr(aa.w + regs.y.w + 0, regs.a.l);
|
||||
L op_writedbr(aa.w + regs.y.w + 1, regs.a.h);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
59
src/cpu/cpu-debugger.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
bool CPUDebugger::property(unsigned id, string &name, string &value) {
|
||||
unsigned n = 0;
|
||||
|
||||
//internal
|
||||
if(id == n++) { name = "S-CPU MDR"; value = string::printf("0x%.2x", mdr()); return true; }
|
||||
|
||||
//$2181-2183
|
||||
if(id == n++) { name = "$2181-$2183"; value = ""; return true; }
|
||||
if(id == n++) { name = "WRAM Address"; value = string::printf("0x%.6x", wram_address()); return true; }
|
||||
|
||||
//$4016
|
||||
if(id == n++) { name = "$4016"; value = ""; return true; }
|
||||
if(id == n++) { name = "Joypad Strobe Latch"; value = joypad_strobe_latch(); return true; }
|
||||
|
||||
//$4200
|
||||
if(id == n++) { name = "$4200"; value = ""; return true; }
|
||||
if(id == n++) { name = "NMI Enable"; value = nmi_enable(); return true; }
|
||||
if(id == n++) { name = "H-IRQ Enable"; value = hirq_enable(); return true; }
|
||||
if(id == n++) { name = "V-IRQ Enable"; value = virq_enable(); return true; }
|
||||
if(id == n++) { name = "Auto Joypad Poll"; value = auto_joypad_poll(); return true; }
|
||||
|
||||
//$4201
|
||||
if(id == n++) { name = "$4201"; value = ""; return true; }
|
||||
if(id == n++) { name = "PIO"; value = string::printf("0x%.2x", pio_bits()); return true; }
|
||||
|
||||
//$4202
|
||||
if(id == n++) { name = "$4202"; value = ""; return true; }
|
||||
if(id == n++) { name = "Multiplicand"; value = string::printf("0x%.2x", multiplicand()); return true; }
|
||||
|
||||
//$4203
|
||||
if(id == n++) { name = "$4203"; value = ""; return true; }
|
||||
if(id == n++) { name = "Multiplier"; value = string::printf("0x%.2x", multiplier()); return true; }
|
||||
|
||||
//$4204-$4205
|
||||
if(id == n++) { name = "$4204-$4205"; value = ""; return true; }
|
||||
if(id == n++) { name = "Dividend"; value = string::printf("0x%.4x", dividend()); return true; }
|
||||
|
||||
//$4206
|
||||
if(id == n++) { name = "$4206"; value = ""; return true; }
|
||||
if(id == n++) { name = "Divisor"; value = string::printf("0x%.2x", divisor()); return true; }
|
||||
|
||||
//$4207-$4208
|
||||
if(id == n++) { name = "$4207-$4208"; value = ""; return true; }
|
||||
if(id == n++) { name = "H-Time"; value = string::printf("0x%.4x", htime()); return true; }
|
||||
|
||||
//$4209-$420a
|
||||
if(id == n++) { name = "$4209-$420a"; value = ""; return true; }
|
||||
if(id == n++) { name = "V-Time"; value = string::printf("0x%.4x", vtime()); return true; }
|
||||
|
||||
//$420d
|
||||
if(id == n++) { name = "$420d"; value = ""; return true; }
|
||||
if(id == n++) { name = "FastROM Enable"; value = fastrom_enable(); return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
42
src/cpu/cpu-debugger.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
struct CPUDebugger : ChipDebugger {
|
||||
bool property(unsigned id, string &name, string &value);
|
||||
|
||||
//internal
|
||||
virtual unsigned mdr() { return 0; }
|
||||
|
||||
//$2181-2183
|
||||
virtual unsigned wram_address() { return 0; }
|
||||
|
||||
//$4016
|
||||
virtual bool joypad_strobe_latch() { return 0; }
|
||||
|
||||
//$4200
|
||||
virtual bool nmi_enable() { return 0; }
|
||||
virtual bool hirq_enable() { return 0; }
|
||||
virtual bool virq_enable() { return 0; }
|
||||
virtual bool auto_joypad_poll() { return 0; }
|
||||
|
||||
//$4201
|
||||
virtual unsigned pio_bits() { return 0; }
|
||||
|
||||
//$4202
|
||||
virtual unsigned multiplicand() { return 0; }
|
||||
|
||||
//$4203
|
||||
virtual unsigned multiplier() { return 0; }
|
||||
|
||||
//$4204-$4205
|
||||
virtual unsigned dividend() { return 0; }
|
||||
|
||||
//$4206
|
||||
virtual unsigned divisor() { return 0; }
|
||||
|
||||
//$4207-$4208
|
||||
virtual unsigned htime() { return 0; }
|
||||
|
||||
//$4209-$420a
|
||||
virtual unsigned vtime() { return 0; }
|
||||
|
||||
//$420d
|
||||
virtual bool fastrom_enable() { return 0; }
|
||||
};
|
@@ -3,6 +3,10 @@
|
||||
#define CPU_CPP
|
||||
namespace SNES {
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "cpu-debugger.cpp"
|
||||
#endif
|
||||
|
||||
void CPU::power() {
|
||||
cpu_version = config.cpu.version;
|
||||
}
|
||||
|
@@ -1,3 +1,7 @@
|
||||
#if defined(DEBUGGER)
|
||||
#include "cpu-debugger.hpp"
|
||||
#endif
|
||||
|
||||
class CPU : public PPUcounter, public MMIO {
|
||||
public:
|
||||
virtual void enter() = 0;
|
||||
|
@@ -1,34 +1,88 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPUDebug::op_step() {
|
||||
void sCPUDebugger::op_step() {
|
||||
bool break_event = false;
|
||||
|
||||
usage[regs.pc] &= ~(UsageFlagM | UsageFlagX);
|
||||
usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0);
|
||||
opcode_pc = regs.pc;
|
||||
|
||||
if(debugger.step_cpu) {
|
||||
debugger.break_event = Debugger::CPUStep;
|
||||
scheduler.exit();
|
||||
scheduler.exit(Scheduler::DebuggerEvent);
|
||||
} else {
|
||||
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Exec, regs.pc, 0x00);
|
||||
}
|
||||
|
||||
if(debugger.trace_cpu) {
|
||||
char t[256];
|
||||
disassemble_opcode(t);
|
||||
debugger.tracefile.print(string() << t << "\n");
|
||||
}
|
||||
|
||||
if(step_event) step_event();
|
||||
sCPU::op_step();
|
||||
scheduler.sync_cpusmp();
|
||||
}
|
||||
|
||||
uint8 sCPUDebug::op_read(uint32 addr) {
|
||||
uint8 sCPUDebugger::op_read(uint32 addr) {
|
||||
uint8 data = sCPU::op_read(addr);
|
||||
usage[addr] |= UsageRead;
|
||||
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Read, addr, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void sCPUDebug::op_write(uint32 addr, uint8 data) {
|
||||
void sCPUDebugger::op_write(uint32 addr, uint8 data) {
|
||||
sCPU::op_write(addr, data);
|
||||
usage[addr] |= UsageWrite;
|
||||
usage[addr] &= ~UsageExec;
|
||||
debugger.breakpoint_test(Debugger::Breakpoint::CPUBus, Debugger::Breakpoint::Write, addr, data);
|
||||
}
|
||||
|
||||
sCPUDebugger::sCPUDebugger() {
|
||||
usage = new uint8[1 << 24]();
|
||||
opcode_pc = 0x8000;
|
||||
}
|
||||
|
||||
sCPUDebugger::~sCPUDebugger() {
|
||||
delete[] usage;
|
||||
}
|
||||
|
||||
//===========
|
||||
//CPUDebugger
|
||||
//===========
|
||||
|
||||
//internal
|
||||
unsigned sCPUDebugger::mdr() { return regs.mdr; }
|
||||
|
||||
//$2181-$2183
|
||||
unsigned sCPUDebugger::wram_address() { return status.wram_addr; }
|
||||
|
||||
//$4016
|
||||
bool sCPUDebugger::joypad_strobe_latch() { return status.joypad_strobe_latch; }
|
||||
|
||||
//$4200
|
||||
bool sCPUDebugger::nmi_enable() { return status.nmi_enabled; }
|
||||
bool sCPUDebugger::hirq_enable() { return status.hirq_enabled; }
|
||||
bool sCPUDebugger::virq_enable() { return status.virq_enabled; }
|
||||
bool sCPUDebugger::auto_joypad_poll() { return status.auto_joypad_poll; }
|
||||
|
||||
//$4201
|
||||
unsigned sCPUDebugger::pio_bits() { return status.pio; }
|
||||
|
||||
//$4202
|
||||
unsigned sCPUDebugger::multiplicand() { return status.mul_a; }
|
||||
|
||||
//$4203
|
||||
unsigned sCPUDebugger::multiplier() { return status.mul_b; }
|
||||
|
||||
//$4204-$4205
|
||||
unsigned sCPUDebugger::dividend() { return status.div_a; }
|
||||
|
||||
//$4206
|
||||
unsigned sCPUDebugger::divisor() { return status.div_b; }
|
||||
|
||||
//$4207-$4208
|
||||
unsigned sCPUDebugger::htime() { return status.hirq_pos; }
|
||||
|
||||
//$4209-$420a
|
||||
unsigned sCPUDebugger::vtime() { return status.virq_pos; }
|
||||
|
||||
//$420d
|
||||
bool sCPUDebugger::fastrom_enable() { return status.rom_speed; }
|
||||
|
||||
#endif
|
||||
|
@@ -1,6 +1,64 @@
|
||||
class sCPUDebug : public sCPU {
|
||||
class sCPUDebugger : public sCPU, public CPUDebugger {
|
||||
public:
|
||||
function<void ()> step_event;
|
||||
|
||||
enum Usage {
|
||||
UsageRead = 0x80,
|
||||
UsageWrite = 0x40,
|
||||
UsageExec = 0x20,
|
||||
UsageFlagM = 0x02,
|
||||
UsageFlagX = 0x01,
|
||||
};
|
||||
uint8 *usage;
|
||||
uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints
|
||||
|
||||
void op_step();
|
||||
uint8 op_read(uint32 addr);
|
||||
void op_write(uint32 addr, uint8 data);
|
||||
|
||||
sCPUDebugger();
|
||||
~sCPUDebugger();
|
||||
|
||||
//===========
|
||||
//CPUDebugger
|
||||
//===========
|
||||
|
||||
//internal
|
||||
unsigned mdr();
|
||||
|
||||
//$2181-$2183
|
||||
unsigned wram_address();
|
||||
|
||||
//$4016
|
||||
bool joypad_strobe_latch();
|
||||
|
||||
//$4200
|
||||
bool nmi_enable();
|
||||
bool hirq_enable();
|
||||
bool virq_enable();
|
||||
bool auto_joypad_poll();
|
||||
|
||||
//$4201
|
||||
unsigned pio_bits();
|
||||
|
||||
//$4202
|
||||
unsigned multiplicand();
|
||||
|
||||
//$4203
|
||||
unsigned multiplier();
|
||||
|
||||
//$4204-$4205
|
||||
unsigned dividend();
|
||||
|
||||
//$4206
|
||||
unsigned divisor();
|
||||
|
||||
//$4207-$4208
|
||||
unsigned htime();
|
||||
|
||||
//$4209-$420a
|
||||
unsigned vtime();
|
||||
|
||||
//$420d
|
||||
bool fastrom_enable();
|
||||
};
|
||||
|
@@ -5,7 +5,7 @@ namespace SNES {
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.cpp"
|
||||
sCPUDebug cpu;
|
||||
sCPUDebugger cpu;
|
||||
#else
|
||||
sCPU cpu;
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@ void sCPU::enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SyncCpu) {
|
||||
scheduler.sync = Scheduler::SyncAll;
|
||||
scheduler.exit();
|
||||
scheduler.exit(Scheduler::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(status.interrupt_pending) {
|
||||
|
@@ -100,7 +100,7 @@ public:
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.hpp"
|
||||
extern sCPUDebug cpu;
|
||||
extern sCPUDebugger cpu;
|
||||
#else
|
||||
extern sCPU cpu;
|
||||
#endif
|
||||
|
@@ -32,7 +32,7 @@ configuration data.
|
||||
|
||||
<h3>Supported Filetypes</h3><br>
|
||||
|
||||
<b>SFC, SMC:</b> SNES cartridge — ROM image.<br>
|
||||
<b>SFC:</b> SNES cartridge — ROM image.<br>
|
||||
<b>BS:</b> Satellaview BS-X flash cartridge — EEPROM image.<br>
|
||||
<b>ST:</b> Sufami Turbo cartridge — ROM image.<br>
|
||||
<b>SRM, PSR:</b> non-volatile memory, often used to save game data — (P)SRAM image.<br>
|
||||
|
BIN
src/data/icons-16x16/applications-multimedia.png
Normal file
After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 477 B |
BIN
src/data/icons-16x16/folder-new.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
src/data/icons-16x16/folder.png
Normal file
After Width: | Height: | Size: 581 B |
Before Width: | Height: | Size: 720 B |
BIN
src/data/icons-16x16/item-check-off.png
Normal file
After Width: | Height: | Size: 209 B |
BIN
src/data/icons-16x16/item-check-on.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
src/data/icons-16x16/item-radio-off.png
Normal file
After Width: | Height: | Size: 565 B |
BIN
src/data/icons-16x16/item-radio-on.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
src/data/icons-16x16/media-playback-start.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
src/data/icons-16x16/media-playback-stop.png
Normal file
After Width: | Height: | Size: 429 B |
BIN
src/data/icons-16x16/media-record.png
Normal file
After Width: | Height: | Size: 653 B |
BIN
src/data/icons-16x16/preferences-system.png
Normal file
After Width: | Height: | Size: 611 B |
BIN
src/data/icons-16x16/system-search.png
Normal file
After Width: | Height: | Size: 935 B |
Before Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 650 B |
Before Width: | Height: | Size: 995 B |
Before Width: | Height: | Size: 978 B |
Before Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 927 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 100 KiB |
@@ -1,7 +1,7 @@
|
||||
#include <../base.hpp>
|
||||
|
||||
#define ADSP_CPP
|
||||
namespaec SNES {
|
||||
namespace SNES {
|
||||
|
||||
aDSP dsp;
|
||||
|
||||
|
@@ -1,3 +1,3 @@
|
||||
class sDSPDebug : public sDSP {
|
||||
class sDSPDebugger : public sDSP {
|
||||
public:
|
||||
};
|
||||
|
@@ -9,7 +9,7 @@ namespace SNES {
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.cpp"
|
||||
sDSPDebug dsp;
|
||||
sDSPDebugger dsp;
|
||||
#else
|
||||
sDSP dsp;
|
||||
#endif
|
||||
@@ -20,7 +20,10 @@ namespace SNES {
|
||||
#define VREG(n) state.regs[v.vidx + v_##n]
|
||||
|
||||
#if !defined(DSP_STATE_MACHINE)
|
||||
#define phase_start() while(true) { if(scheduler.sync == Scheduler::SyncAll) scheduler.exit();
|
||||
#define phase_start() while(true) { \
|
||||
if(scheduler.sync == Scheduler::SyncAll) { \
|
||||
scheduler.exit(Scheduler::SynchronizeEvent); \
|
||||
}
|
||||
#define phase(n)
|
||||
#define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp()
|
||||
#define phase_end() }
|
||||
|
@@ -170,7 +170,7 @@ private:
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#include "debugger/debugger.hpp"
|
||||
extern sDSPDebug dsp;
|
||||
extern sDSPDebugger dsp;
|
||||
#else
|
||||
extern sDSP dsp;
|
||||
#endif
|
||||
|
@@ -5,6 +5,10 @@
|
||||
#endif
|
||||
|
||||
namespace SNES {
|
||||
struct ChipDebugger {
|
||||
virtual bool property(unsigned id, string &name, string &value) = 0;
|
||||
};
|
||||
|
||||
#include "memory/memory.hpp"
|
||||
#include "memory/smemory/smemory.hpp"
|
||||
|
||||
|
104
src/lib/libco/amd64.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
libco.amd64 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (*co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
//ABI: Win64
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48,
|
||||
0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C,
|
||||
0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
|
||||
0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F,
|
||||
0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29,
|
||||
0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44,
|
||||
0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48,
|
||||
0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C,
|
||||
0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
|
||||
0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F,
|
||||
0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29,
|
||||
0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44,
|
||||
0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void co_init() {
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
//ABI: SystemV
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C,
|
||||
0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48,
|
||||
0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C,
|
||||
0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init() {
|
||||
unsigned long long addr = (unsigned long long)co_swap_function;
|
||||
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
if(!co_swap) {
|
||||
co_init();
|
||||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 512; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle) {
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -6,15 +6,17 @@
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
#include "x86.c"
|
||||
#elif defined(__GNUC__) && defined(__amd64__) && !defined(__MINGW64__)
|
||||
#include "x86-64.c"
|
||||
#elif defined(__MINGW64__)
|
||||
#include "fiber.c"
|
||||
#elif defined(__GNUC__) && defined(__amd64__)
|
||||
#include "amd64.c"
|
||||
#elif defined(__GNUC__) && defined(__powerpc__) && defined(__ELF__)
|
||||
#include "ppc-elf.c"
|
||||
#elif defined(__GNUC__)
|
||||
#include "sjlj.c"
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
#include "x86.c"
|
||||
#elif defined(_MSC_VER) && defined(_M_AMD64)
|
||||
#include "amd64.c"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "fiber.c"
|
||||
#else
|
||||
#error "libco: unsupported processor, compiler or operating system"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
libco
|
||||
version: 0.13 rc2 (2008-01-28)
|
||||
version: 0.15 (2009-10-12)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
|
325
src/lib/libco/ppc-elf.c
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* libco.ppc-elf
|
||||
* author: Kernigh
|
||||
* license: public domain
|
||||
*
|
||||
* PowerPC 32-bit ELF implementation of libco (for compile with GCC),
|
||||
* ported from PowerPC Mac OS X implementation (ppc.s) by Vas Crabb.
|
||||
* This ELF version works for OpenBSD, and might also work for FreeBSD,
|
||||
* NetBSD and Linux.
|
||||
*
|
||||
* Note 1: This implementation does not handle the AltiVec/VMX
|
||||
* registers, because the ELF ABI does not mention them,
|
||||
* and my OpenBSD system is not using them.
|
||||
*
|
||||
* Note 2: If you want position-independent code, then you must
|
||||
* define __PIC__. gcc -fpic or -fPIC defines __PIC__, but
|
||||
* gcc -fpie or -fPIE might not. If you want to use -fpie
|
||||
* or -fPIE, then you might need a manual definition:
|
||||
* gcc -fpie -D__PIC__=1
|
||||
* gcc -fPIE -D__PIC__=2
|
||||
*
|
||||
* The ELF ABI is "System V Application Binary Interface, PowerPC
|
||||
* Processor Supplement", which you can get from
|
||||
* <http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf>
|
||||
* (PDF file, hosted by Linux Foundation).
|
||||
*
|
||||
* ELF and Mac OS X use similar conventions to allocate the registers,
|
||||
* and to pass arguments and return values through registers. The main
|
||||
* differences are that ELF has a slightly different stack format, that
|
||||
* symbols are different (and without an extra underscore at the start),
|
||||
* and that the assembly syntax is different.
|
||||
*
|
||||
* A function may destroy the values of volatile registers, but must
|
||||
* preserve the values of nonvolatile registers. So the co_switch()
|
||||
* function only saves the nonvolatile registers.
|
||||
*
|
||||
* [nonvolatile registers in ELF]
|
||||
* %r1, %r14..%r31
|
||||
* %f14..%f31
|
||||
* %cr2..%cr4 in cr
|
||||
*
|
||||
* [volatile registers in ELF]
|
||||
* %r0, %r3..%r10
|
||||
* %f0..%f13
|
||||
* %cr0, %cr1, %cr5..%cr7 in cr
|
||||
* ctr, lr, xer
|
||||
*
|
||||
* lr (link register) is volatile, but it contains the return address,
|
||||
* so co_switch must save lr.
|
||||
*
|
||||
* %r13 is the small data pointer. This is constant across threads, so
|
||||
* co_switch() does not touch %r13.
|
||||
*
|
||||
* %r2 is a reserved register, so co_switch() does not touch %r2. Some
|
||||
* systems might borrow an idea from the PowerPC Embedded ABI, and might
|
||||
* use %r2 as a small read-only data pointer, which is constant across
|
||||
* threads.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void * cothread_t;
|
||||
|
||||
/*
|
||||
* co_active_context is either in a global offset table (if we are
|
||||
* compiling -fPIC or -fPIE) or has an absolute position.
|
||||
*/
|
||||
static void *co_main_stack_pointer;
|
||||
static cothread_t co_active_context = &co_main_stack_pointer;
|
||||
|
||||
extern cothread_t co_active() {
|
||||
return co_active_context;
|
||||
}
|
||||
|
||||
/*
|
||||
* Embedded assembly.
|
||||
*
|
||||
* We are not using the percent-sign substitution feature,
|
||||
* so we must write "%r1", not "%%r1".
|
||||
*
|
||||
* We always write 'bl malloc@plt', not 'bl malloc'. The '@plt'
|
||||
* is necessary in position-indepent code and seems to have no
|
||||
* significant effect in fixed-position code.
|
||||
*
|
||||
* We never use the 'lmw' or 'stmw' instructions. The ELF ABI
|
||||
* mentions that these instructions "are usually slower than
|
||||
* a sequence of other instructions that have the same effect."
|
||||
* We instead use sequences of 'lwz' or 'stz' instructions.
|
||||
*/
|
||||
__asm__("\n"
|
||||
"### embedded assembly \n"
|
||||
".section \".text\" \n"
|
||||
" .balign 4 \n"
|
||||
" \n"
|
||||
/*
|
||||
* void co_switch(co_thread to %r3)
|
||||
*
|
||||
* Allocate our stack frame of 240 bytes:
|
||||
* Old New Value
|
||||
* 4(%r1) 244(%r1) return address, used by us
|
||||
* 0(%r1) 240(%r1) frame pointer
|
||||
* 232(%r1) %f31
|
||||
* 224(%r1) %f30
|
||||
* ...
|
||||
* 96(%r1) %f14
|
||||
* 92(%r1) %r31
|
||||
* 88(%r1) %r30
|
||||
* ...
|
||||
* 24(%r1) %r14
|
||||
* 20(%r1) condition register
|
||||
* 8(%r1) padding of 12 bytes
|
||||
* 4(%r1) return address, never used
|
||||
* 0(%r1) frame pointer
|
||||
*
|
||||
* Save our registers in our stack frame.
|
||||
* Save our stack pointer in 0(%r4).
|
||||
* Switch to the stack of the other thread.
|
||||
* Restore registers and return.
|
||||
*/
|
||||
" .globl co_switch \n"
|
||||
" .type co_switch, @function \n"
|
||||
"co_switch: \n"
|
||||
" mflr %r0 # %r0 = return address \n"
|
||||
" mfcr %r9 # %r9 = condition register \n"
|
||||
" stwu %r1, -240(%r1) # allocate stack frame \n"
|
||||
" \n"
|
||||
" stw %r0, 244(%r1) # save return address \n"
|
||||
" stfd %f31, 232(%r1) # save floating-point regs \n"
|
||||
" stfd %f30, 224(%r1) \n"
|
||||
" stfd %f29, 216(%r1) \n"
|
||||
" stfd %f28, 208(%r1) \n"
|
||||
" stfd %f27, 200(%r1) \n"
|
||||
" stfd %f26, 192(%r1) \n"
|
||||
" stfd %f25, 184(%r1) \n"
|
||||
" stfd %f24, 176(%r1) \n"
|
||||
" stfd %f23, 168(%r1) \n"
|
||||
" stfd %f22, 160(%r1) \n"
|
||||
" stfd %f21, 152(%r1) \n"
|
||||
" stfd %f20, 144(%r1) \n"
|
||||
" stfd %f19, 136(%r1) \n"
|
||||
" stfd %f18, 128(%r1) \n"
|
||||
" stfd %f17, 120(%r1) \n"
|
||||
" stfd %f16, 112(%r1) \n"
|
||||
" stfd %f16, 104(%r1) \n"
|
||||
" stfd %f14, 96(%r1) \n"
|
||||
" stw %r31, 92(%r1) # save general-purpose regs \n"
|
||||
" stw %r30, 88(%r1) \n"
|
||||
" stw %r29, 84(%r1) \n"
|
||||
" stw %r28, 80(%r1) \n"
|
||||
" stw %r27, 76(%r1) \n"
|
||||
" stw %r26, 72(%r1) \n"
|
||||
" stw %r25, 68(%r1) \n"
|
||||
" stw %r24, 64(%r1) \n"
|
||||
" stw %r23, 60(%r1) \n"
|
||||
" stw %r22, 56(%r1) \n"
|
||||
" stw %r21, 52(%r1) \n"
|
||||
" stw %r20, 48(%r1) \n"
|
||||
" stw %r19, 44(%r1) \n"
|
||||
" stw %r18, 40(%r1) \n"
|
||||
" stw %r17, 36(%r1) \n"
|
||||
" stw %r16, 32(%r1) \n"
|
||||
" stw %r15, 28(%r1) \n"
|
||||
" stw %r14, 24(%r1) \n"
|
||||
" stw %r9, 20(%r1) # save condition reg \n"
|
||||
" \n"
|
||||
" # save current context, set new context \n"
|
||||
" # %r4 = co_active_context \n"
|
||||
" # co_active_context = %r3 \n"
|
||||
#if __PIC__ == 2
|
||||
" # position-independent code, large model (-fPIC) \n"
|
||||
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
|
||||
" mflr %r8 # %r8 = address of got \n"
|
||||
" addis %r7, %r8, co_active_context@got@ha \n"
|
||||
" lwz %r6, co_active_context@got@l(%r7) \n"
|
||||
" lwz %r4, 0(%r6) \n"
|
||||
" stw %r3, 0(%r6) \n"
|
||||
#elif __PIC__ == 1
|
||||
" # position-independent code, small model (-fpic) \n"
|
||||
" bl _GLOBAL_OFFSET_TABLE_@local-4 \n"
|
||||
" mflr %r8 # %r8 = address of got \n"
|
||||
" lwz %r7, co_active_context@got(%r8) \n"
|
||||
" lwz %r4, 0(%r7) \n"
|
||||
" stw %r3, 0(%r7) \n"
|
||||
#else
|
||||
" # fixed-position code \n"
|
||||
" lis %r8, co_active_context@ha \n"
|
||||
" lwz %r4, co_active_context@l(%r8) \n"
|
||||
" stw %r3, co_active_context@l(%r8) \n"
|
||||
#endif
|
||||
" \n"
|
||||
" # save current stack pointer \n"
|
||||
" stw %r1, 0(%r4) \n"
|
||||
" # get new stack pointer \n"
|
||||
" lwz %r1, 0(%r3) \n"
|
||||
" \n"
|
||||
" lwz %r0, 244(%r1) # get return address \n"
|
||||
" lfd %f31, 232(%r1) # restore floating-point regs \n"
|
||||
" lfd %f30, 224(%r1) \n"
|
||||
" lfd %f29, 216(%r1) \n"
|
||||
" lfd %f28, 208(%r1) \n"
|
||||
" lfd %f27, 200(%r1) \n"
|
||||
" lfd %f26, 192(%r1) \n"
|
||||
" lfd %f25, 184(%r1) \n"
|
||||
" lfd %f24, 176(%r1) \n"
|
||||
" lfd %f23, 168(%r1) \n"
|
||||
" lfd %f22, 160(%r1) \n"
|
||||
" lfd %f21, 152(%r1) \n"
|
||||
" lfd %f20, 144(%r1) \n"
|
||||
" lfd %f19, 136(%r1) \n"
|
||||
" lfd %f18, 128(%r1) \n"
|
||||
" lfd %f17, 120(%r1) \n"
|
||||
" lfd %f16, 112(%r1) \n"
|
||||
" lfd %f16, 104(%r1) \n"
|
||||
" lfd %f14, 96(%r1) \n"
|
||||
" lwz %r31, 92(%r1) # restore general-purpose regs \n"
|
||||
" lwz %r30, 88(%r1) \n"
|
||||
" lwz %r29, 84(%r1) \n"
|
||||
" lwz %r28, 80(%r1) \n"
|
||||
" lwz %r27, 76(%r1) \n"
|
||||
" lwz %r26, 72(%r1) \n"
|
||||
" lwz %r25, 68(%r1) \n"
|
||||
" lwz %r24, 64(%r1) \n"
|
||||
" lwz %r23, 60(%r1) \n"
|
||||
" lwz %r22, 56(%r1) \n"
|
||||
" lwz %r21, 52(%r1) \n"
|
||||
" lwz %r20, 48(%r1) \n"
|
||||
" lwz %r19, 44(%r1) \n"
|
||||
" lwz %r18, 40(%r1) \n"
|
||||
" lwz %r17, 36(%r1) \n"
|
||||
" lwz %r16, 32(%r1) \n"
|
||||
" lwz %r15, 28(%r1) \n"
|
||||
" lwz %r14, 24(%r1) \n"
|
||||
" lwz %r9, 20(%r1) # get condition reg \n"
|
||||
" \n"
|
||||
" addi %r1, %r1, 240 # free stack frame \n"
|
||||
" mtlr %r0 # restore return address \n"
|
||||
" mtcr %r9 # restore condition register \n"
|
||||
" blr # return \n"
|
||||
" .size co_switch, . - co_switch \n"
|
||||
" \n"
|
||||
/*
|
||||
* cothread_t %r3 co_create(unsigned int stack_size %r3,
|
||||
* void (*coentry %r4)())
|
||||
*
|
||||
* Allocate a new stack, such that when you co_switch to that
|
||||
* stack, then co_switch returns to coentry.
|
||||
*/
|
||||
" .globl co_create \n"
|
||||
" .type co_create, @function \n"
|
||||
"co_create: \n"
|
||||
" mflr %r0 # %r0 = return address \n"
|
||||
" stwu %r1, -16(%r1) # allocate my stack frame \n"
|
||||
" stw %r0, 20(%r1) # save return address \n"
|
||||
" stw %r31, 12(%r1) # save %r31 \n"
|
||||
" stw %r30, 8(%r1) # save %r30 \n"
|
||||
" \n"
|
||||
" mr %r30, %r3 # %r30 = stack_size \n"
|
||||
" mr %r31, %r4 # %r31 = coentry \n"
|
||||
" \n"
|
||||
" # Call malloc(stack_size %r3) to allocate stack; \n"
|
||||
" # malloc() probably uses good alignment. \n"
|
||||
" # \n"
|
||||
" bl malloc@plt # returns %r3 = low end \n"
|
||||
" cmpwi %r3, 0 # if returned NULL, \n"
|
||||
" beq- 1f # then abort \n"
|
||||
" \n"
|
||||
" # we return %r3 = low end of stack \n"
|
||||
" add %r4, %r3, %r30 # %r4 = high end of stack \n"
|
||||
" \n"
|
||||
" # uncomment if malloc() uses wrong alignment \n"
|
||||
" #rlwinm %r4,%r4,0,0,27 # force 16-byte alignment \n"
|
||||
" \n"
|
||||
/*
|
||||
* Allocate two stack frames:
|
||||
* 16 bytes for stack frame with return address
|
||||
* 240 bytes for co_switch stack frame
|
||||
*
|
||||
* Old New Value
|
||||
* -8(%r4) 248(%r5) padding of 8 bytes
|
||||
* -12(%r4) 244(%r5) return address = coentry
|
||||
* -16(%r4) 240(%r5) frame pointer = NULL
|
||||
* 232(%r5) %f31 = 0
|
||||
* ...
|
||||
* 20(%r5) condition register = 0
|
||||
* 0(%r5) frame pointer
|
||||
*/
|
||||
" li %r9, (240-20)/4+1 \n"
|
||||
" addi %r5, %r4, -16 # allocate first stack frame \n"
|
||||
" li %r0, 0 \n"
|
||||
" stwu %r5, -240(%r5) # allocate second stack frame \n"
|
||||
" li %r8, 20 \n"
|
||||
" mtctr %r9 # loop %r9 times \n"
|
||||
"2: # loop to store zero to 20(%r5) through 240(%r5) \n"
|
||||
" stwx %r0, %r5, %r8 \n"
|
||||
" addi %r8, %r8, 4 # index += 4 \n"
|
||||
" bdnz+ 2b # ctr -= 1, branch if nonzero \n"
|
||||
" \n"
|
||||
" stw %r31, 244(%r5) # return address = coentry \n"
|
||||
" stw %r5, 0(%r3) # save stack pointer \n"
|
||||
" \n"
|
||||
" lwz %r0, 20(%r1) # get return address \n"
|
||||
" lwz %r31, 12(%r1) # restore %r31 \n"
|
||||
" lwz %r30, 8(%r1) # restore %r30 \n"
|
||||
" mtlr %r0 # restore return address \n"
|
||||
" addi %r1, %r1, 16 # free stack frame \n"
|
||||
" blr # return \n"
|
||||
" \n"
|
||||
"1: b abort@plt # branch 1f to abort \n"
|
||||
" .size co_create, . - co_create \n"
|
||||
" \n"
|
||||
/*
|
||||
* void co_delete(cothread_t) => void free(void *)
|
||||
*/
|
||||
" .globl co_delete \n"
|
||||
" .type co_delete, @function \n"
|
||||
"co_delete: \n"
|
||||
" b free@plt \n"
|
||||
" \n"
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
libco.x86-64 (2008-01-28)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[32];
|
||||
static thread_local cothread_t co_active_ = 0;
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
return co_active_;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
assert(sizeof(long) == 8);
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
size += 128; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)calloc(size, 1)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void co_switch(cothread_t to) {
|
||||
register long stack = *(long*)to; /* stack[0] = "to" thread entry point */
|
||||
register cothread_t from = co_active_;
|
||||
co_active_ = to;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movq %%rsp,(%1) \n\t" /* save old stack pointer */
|
||||
"movq (%0),%%rsp \n\t" /* load new stack pointer */
|
||||
"addq $8,%%rsp \n\t" /* "pop" return address off stack */
|
||||
|
||||
"movq %%rbp, 8(%1) \n\t" /* backup non-volatile registers */
|
||||
"movq %%rbx,16(%1) \n\t"
|
||||
"movq %%r12,24(%1) \n\t"
|
||||
"movq %%r13,32(%1) \n\t"
|
||||
"movq %%r14,40(%1) \n\t"
|
||||
"movq %%r15,48(%1) \n\t"
|
||||
|
||||
"movq 8(%0),%%rbp \n\t" /* restore non-volatile registers */
|
||||
"movq 16(%0),%%rbx \n\t"
|
||||
"movq 24(%0),%%r12 \n\t"
|
||||
"movq 32(%0),%%r13 \n\t"
|
||||
"movq 40(%0),%%r14 \n\t"
|
||||
"movq 48(%0),%%r15 \n\t"
|
||||
|
||||
"jmp *(%2) \n\t" /* jump into "to" thread */
|
||||
: /* no outputs */
|
||||
: "r" (to), "r" (from), "r" (stack)
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
libco.x86 (2008-01-28)
|
||||
libco.x86 (2009-10-12)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
@@ -13,26 +13,63 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[32];
|
||||
static thread_local cothread_t co_active_ = 0;
|
||||
#if defined(_MSC_VER)
|
||||
#define fastcall __fastcall
|
||||
#elif defined(__GNUC__)
|
||||
#define fastcall __attribute__((fastcall))
|
||||
#else
|
||||
#error "libco: please define fastcall macro"
|
||||
#endif
|
||||
|
||||
static thread_local long co_active_buffer[64];
|
||||
static thread_local cothread_t co_active_handle = 0;
|
||||
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
|
||||
|
||||
//ABI: fastcall
|
||||
static unsigned char co_swap_function[] = {
|
||||
0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A,
|
||||
0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0,
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
void co_init() {
|
||||
DWORD old_privileges;
|
||||
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
void co_init() {
|
||||
unsigned long addr = (unsigned long)co_swap_function;
|
||||
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
|
||||
unsigned long size = (addr - base) + sizeof co_swap_function;
|
||||
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
cothread_t co_active() {
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
return co_active_;
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
return co_active_handle;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
cothread_t handle;
|
||||
assert(sizeof(long) == 4);
|
||||
if(!co_active_) co_active_ = &co_active_buffer;
|
||||
size += 128; /* allocate additional space for storage */
|
||||
if(!co_swap) {
|
||||
co_init();
|
||||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 256; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)calloc(size, 1)) {
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
@@ -46,65 +83,11 @@ void co_delete(cothread_t handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
void co_switch(cothread_t to) {
|
||||
register long stack = *(long*)to; /* stack[0] = "to" thread entry point */
|
||||
register cothread_t from = co_active_;
|
||||
co_active_ = to;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movl %%esp,(%1) \n\t" /* save old stack pointer */
|
||||
"movl (%0),%%esp \n\t" /* load new stack pointer */
|
||||
"addl $4,%%esp \n\t" /* "pop" return address off stack */
|
||||
|
||||
"movl %%ebp, 4(%1) \n\t" /* backup non-volatile registers */
|
||||
"movl %%esi, 8(%1) \n\t"
|
||||
"movl %%edi,12(%1) \n\t"
|
||||
"movl %%ebx,16(%1) \n\t"
|
||||
|
||||
"movl 4(%0),%%ebp \n\t" /* restore non-volatile registers */
|
||||
"movl 8(%0),%%esi \n\t"
|
||||
"movl 12(%0),%%edi \n\t"
|
||||
"movl 16(%0),%%ebx \n\t"
|
||||
|
||||
"jmp *(%2) \n\t" /* jump into "to" thread */
|
||||
: /* no outputs */
|
||||
: "r" (to), "r" (from), "r" (stack)
|
||||
);
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
__declspec(naked) __declspec(noinline)
|
||||
static void __fastcall co_swap(register cothread_t to, register cothread_t from) {
|
||||
/* ecx = to, edx = from */
|
||||
__asm {
|
||||
mov [edx],esp
|
||||
mov esp,[ecx]
|
||||
pop eax
|
||||
|
||||
mov [edx+ 4],ebp
|
||||
mov [edx+ 8],esi
|
||||
mov [edx+12],edi
|
||||
mov [edx+16],ebx
|
||||
|
||||
mov ebp,[ecx+ 4]
|
||||
mov esi,[ecx+ 8]
|
||||
mov edi,[ecx+12]
|
||||
mov ebx,[ecx+16]
|
||||
|
||||
jmp eax
|
||||
}
|
||||
}
|
||||
|
||||
void co_switch(cothread_t handle) {
|
||||
register cothread_t co_prev_ = co_active_;
|
||||
co_swap(co_active_ = handle, co_prev_);
|
||||
register cothread_t co_previous_handle = co_active_handle;
|
||||
co_swap(co_active_handle = handle, co_previous_handle);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,138 +0,0 @@
|
||||
Colortable colortable;
|
||||
|
||||
void Colortable::set_format(Format format_) { format = format_; }
|
||||
void Colortable::set_contrast(signed contrast_) { contrast = contrast_; }
|
||||
void Colortable::set_brightness(signed brightness_) { brightness = brightness_; }
|
||||
void Colortable::set_gamma(signed gamma_) { gamma = gamma_; }
|
||||
|
||||
void Colortable::enable_gamma_ramp(bool value) { gamma_ramp = value; }
|
||||
void Colortable::enable_sepia(bool value) { sepia = value; }
|
||||
void Colortable::enable_grayscale(bool value) { grayscale = value; }
|
||||
void Colortable::enable_invert(bool value) { invert = value; }
|
||||
|
||||
void Colortable::update() {
|
||||
double kr = 0.2126, kb = 0.0722, kg = (1.0 - kr - kb); //luminance weights
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
unsigned color //bgr555->rgb888 conversion
|
||||
= ((i & 0x001f) << 19) | ((i & 0x001c) << 14)
|
||||
| ((i & 0x03e0) << 6) | ((i & 0x0380) << 1)
|
||||
| ((i & 0x7c00) >> 7) | ((i & 0x7000) >> 12);
|
||||
|
||||
signed l;
|
||||
signed r = (color >> 16) & 0xff;
|
||||
signed g = (color >> 8) & 0xff;
|
||||
signed b = (color ) & 0xff;
|
||||
|
||||
if(gamma_ramp == true) {
|
||||
r = gamma_ramp_table[r >> 3];
|
||||
g = gamma_ramp_table[g >> 3];
|
||||
b = gamma_ramp_table[b >> 3];
|
||||
}
|
||||
|
||||
if(contrast != 0) {
|
||||
r = contrast_adjust(r);
|
||||
g = contrast_adjust(g);
|
||||
b = contrast_adjust(b);
|
||||
}
|
||||
|
||||
if(brightness != 0) {
|
||||
r = brightness_adjust(r);
|
||||
g = brightness_adjust(g);
|
||||
b = brightness_adjust(b);
|
||||
}
|
||||
|
||||
if(gamma != 100) {
|
||||
r = gamma_adjust(r);
|
||||
g = gamma_adjust(g);
|
||||
b = gamma_adjust(b);
|
||||
}
|
||||
|
||||
if(sepia == true) {
|
||||
l = (signed)((double)r * kr + (double)g * kg + (double)b * kb);
|
||||
l = max(0, min(255, l));
|
||||
|
||||
r = (signed)((double)l * (1.0 + 0.300));
|
||||
g = (signed)((double)l * (1.0 - 0.055));
|
||||
b = (signed)((double)l * (1.0 - 0.225));
|
||||
|
||||
r = max(0, min(255, r));
|
||||
g = max(0, min(255, g));
|
||||
b = max(0, min(255, b));
|
||||
}
|
||||
|
||||
if(grayscale == true) {
|
||||
l = (signed)((double)r * kr + (double)g * kg + (double)b * kb);
|
||||
l = max(0, min(255, l));
|
||||
r = g = b = l;
|
||||
}
|
||||
|
||||
if(invert == true) {
|
||||
r ^= 0xff;
|
||||
g ^= 0xff;
|
||||
b ^= 0xff;
|
||||
}
|
||||
|
||||
switch(format) {
|
||||
case RGB555: {
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
table[i] = (r << 10) | (g << 5) | (b);
|
||||
} break;
|
||||
|
||||
case RGB565: {
|
||||
r >>= 3;
|
||||
g >>= 2;
|
||||
b >>= 3;
|
||||
table[i] = (r << 11) | (g << 5) | (b);
|
||||
} break;
|
||||
|
||||
case RGB888: {
|
||||
table[i] = (r << 16) | (g << 8) | (b);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
table[i] = ~0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Colortable::Colortable() {
|
||||
table = new uint32_t[32768];
|
||||
contrast = 0;
|
||||
brightness = 0;
|
||||
gamma = 100;
|
||||
|
||||
gamma_ramp = false;
|
||||
sepia = false;
|
||||
grayscale = false;
|
||||
invert = false;
|
||||
}
|
||||
|
||||
Colortable::~Colortable() {
|
||||
delete[] table;
|
||||
}
|
||||
|
||||
const uint8_t Colortable::gamma_ramp_table[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
|
||||
uint8_t Colortable::contrast_adjust(uint8_t input) {
|
||||
signed result = input - contrast + (2 * contrast * input + 127) / 255;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Colortable::brightness_adjust(uint8_t input) {
|
||||
signed result = input + brightness;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Colortable::gamma_adjust(uint8_t input) {
|
||||
signed result = (signed)(pow(((double)input / 255.0), (double)gamma / 100.0) * 255.0 + 0.5);
|
||||
return max(0, min(255, result));
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
class Colortable {
|
||||
public:
|
||||
enum Format {
|
||||
RGB555,
|
||||
RGB565,
|
||||
RGB888,
|
||||
};
|
||||
|
||||
const inline uint32_t operator[](uint16_t index) const { return table[index]; }
|
||||
|
||||
void set_format(Format);
|
||||
void set_contrast(signed);
|
||||
void set_brightness(signed);
|
||||
void set_gamma(signed);
|
||||
void enable_gamma_ramp(bool);
|
||||
void enable_sepia(bool);
|
||||
void enable_grayscale(bool);
|
||||
void enable_invert(bool);
|
||||
|
||||
void update();
|
||||
|
||||
Colortable();
|
||||
~Colortable();
|
||||
|
||||
private:
|
||||
uint32_t *table;
|
||||
Format format;
|
||||
|
||||
signed contrast;
|
||||
signed brightness;
|
||||
signed gamma;
|
||||
|
||||
bool gamma_ramp;
|
||||
bool sepia;
|
||||
bool grayscale;
|
||||
bool invert;
|
||||
|
||||
static const uint8_t gamma_ramp_table[32];
|
||||
uint8_t contrast_adjust(uint8_t input);
|
||||
uint8_t brightness_adjust(uint8_t input);
|
||||
uint8_t gamma_adjust(uint8_t input);
|
||||
};
|
||||
|
||||
extern Colortable colortable;
|
@@ -1,32 +0,0 @@
|
||||
DirectFilter filter_direct;
|
||||
|
||||
void DirectFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
}
|
||||
|
||||
void DirectFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
if(width == 512 && line[y] == 256) {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
input += 256;
|
||||
} else {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t p = *input++;
|
||||
*output++ = colortable[p];
|
||||
}
|
||||
}
|
||||
input += pitch - width;
|
||||
output += outpitch - width;
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
class DirectFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
};
|
||||
|
||||
extern DirectFilter filter_direct;
|
@@ -1,32 +0,0 @@
|
||||
FilterInterface filter;
|
||||
|
||||
void FilterInterface::set(FilterInterface::FilterType type) {
|
||||
active_filter = type;
|
||||
}
|
||||
|
||||
void FilterInterface::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
switch(active_filter) { default:
|
||||
case Direct: return filter_direct.size(outwidth, outheight, width, height);
|
||||
case Scanline: return filter_scanline.size(outwidth, outheight, width, height);
|
||||
case Scale2x: return filter_scale2x.size(outwidth, outheight, width, height);
|
||||
case LQ2x: return filter_lq2x.size(outwidth, outheight, width, height);
|
||||
case HQ2x: return filter_hq2x.size(outwidth, outheight, width, height);
|
||||
case NTSC: return filter_ntsc.size(outwidth, outheight, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void FilterInterface::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
switch(active_filter) { default:
|
||||
case Direct: return filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
case Scanline: return filter_scanline.render(output, outpitch, input, pitch, line, width, height);
|
||||
case Scale2x: return filter_scale2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case LQ2x: return filter_lq2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case HQ2x: return filter_hq2x.render(output, outpitch, input, pitch, line, width, height);
|
||||
case NTSC: return filter_ntsc.render(output, outpitch, input, pitch, line, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
FilterInterface::FilterInterface() : active_filter(FilterInterface::Direct) {}
|
@@ -1,37 +0,0 @@
|
||||
class Filter {
|
||||
public:
|
||||
virtual void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) = 0;
|
||||
|
||||
virtual void render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) = 0;
|
||||
};
|
||||
|
||||
class FilterInterface : public Filter {
|
||||
public:
|
||||
enum FilterType {
|
||||
Direct,
|
||||
Scanline,
|
||||
Scale2x,
|
||||
LQ2x,
|
||||
HQ2x,
|
||||
NTSC,
|
||||
};
|
||||
|
||||
void set(FilterType type);
|
||||
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
|
||||
void render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
);
|
||||
|
||||
FilterInterface();
|
||||
|
||||
private:
|
||||
FilterType active_filter;
|
||||
};
|
||||
|
||||
extern FilterInterface filter;
|
@@ -1,204 +0,0 @@
|
||||
//HQ2x filter
|
||||
//authors: byuu and blargg
|
||||
//license: public domain
|
||||
//
|
||||
//note: this is a clean reimplementation of the original HQ2x filter, which was
|
||||
//written by Maxim Stepin (MaxSt). it is not 100% identical, but very similar.
|
||||
|
||||
HQ2xFilter filter_hq2x;
|
||||
|
||||
const uint8_t HQ2xFilter::hqTable[256] = {
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
|
||||
};
|
||||
|
||||
bool HQ2xFilter::same(uint16_t x, uint16_t y) {
|
||||
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
|
||||
}
|
||||
|
||||
bool HQ2xFilter::diff(uint32_t x, uint16_t y) {
|
||||
return ((x - yuvTable[y]) & diff_mask);
|
||||
}
|
||||
|
||||
void HQ2xFilter::grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
|
||||
uint16_t HQ2xFilter::pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
|
||||
|
||||
uint16_t HQ2xFilter::blend1(uint32_t A, uint32_t B) {
|
||||
grow(A); grow(B);
|
||||
A = (A * 3 + B) >> 2;
|
||||
return pack(A);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend2(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + B + C) >> 2);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend3(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 5 + B * 2 + C) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend4(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 6 + B + C) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend5(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + (B + C) * 3) >> 3);
|
||||
}
|
||||
|
||||
uint16_t HQ2xFilter::blend6(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 14 + B + C) >> 4);
|
||||
}
|
||||
|
||||
alwaysinline uint16_t HQ2xFilter::blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
|
||||
switch(rule) { default:
|
||||
case 0: return E;
|
||||
case 1: return blend1(E, A);
|
||||
case 2: return blend1(E, D);
|
||||
case 3: return blend1(E, B);
|
||||
case 4: return blend2(E, D, B);
|
||||
case 5: return blend2(E, A, B);
|
||||
case 6: return blend2(E, A, D);
|
||||
case 7: return blend3(E, B, D);
|
||||
case 8: return blend3(E, D, B);
|
||||
case 9: return blend4(E, D, B);
|
||||
case 10: return blend5(E, D, B);
|
||||
case 11: return blend6(E, D, B);
|
||||
case 12: return same(B, D) ? blend2(E, D, B) : E;
|
||||
case 13: return same(B, D) ? blend5(E, D, B) : E;
|
||||
case 14: return same(B, D) ? blend6(E, D, B) : E;
|
||||
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
|
||||
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
|
||||
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
|
||||
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
|
||||
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
|
||||
}
|
||||
}
|
||||
|
||||
void HQ2xFilter::size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height) {
|
||||
outwidth = width;
|
||||
outheight = height;
|
||||
|
||||
if(width <= 256 && height <= 240) {
|
||||
outwidth *= 2;
|
||||
outheight *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void HQ2xFilter::render(
|
||||
uint32_t *output, unsigned outpitch, uint16_t *input, unsigned pitch,
|
||||
unsigned *line, unsigned width, unsigned height
|
||||
) {
|
||||
if(width > 256 || height > 240) {
|
||||
filter_direct.render(output, outpitch, input, pitch, line, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
int prevline = (y == 0) ? 0 : pitch;
|
||||
int nextline = (y == height - 1) ? 0 : pitch;
|
||||
|
||||
input++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
for(unsigned x = 1; x < 256 - 1; x++) {
|
||||
uint16_t A = *(input - prevline - 1);
|
||||
uint16_t B = *(input - prevline + 0);
|
||||
uint16_t C = *(input - prevline + 1);
|
||||
uint16_t D = *(input - 1);
|
||||
uint16_t E = *(input + 0);
|
||||
uint16_t F = *(input + 1);
|
||||
uint16_t G = *(input + nextline - 1);
|
||||
uint16_t H = *(input + nextline + 0);
|
||||
uint16_t I = *(input + nextline + 1);
|
||||
uint32_t e = yuvTable[E] + diff_offset;
|
||||
|
||||
uint8_t pattern;
|
||||
pattern = diff(e, A) << 0;
|
||||
pattern |= diff(e, B) << 1;
|
||||
pattern |= diff(e, C) << 2;
|
||||
pattern |= diff(e, D) << 3;
|
||||
pattern |= diff(e, F) << 4;
|
||||
pattern |= diff(e, G) << 5;
|
||||
pattern |= diff(e, H) << 6;
|
||||
pattern |= diff(e, I) << 7;
|
||||
|
||||
*(out0 + 0) = colortable[blend(hqTable[pattern], E, A, B, D, F, H)]; pattern = rotate[pattern];
|
||||
*(out0 + 1) = colortable[blend(hqTable[pattern], E, C, F, B, H, D)]; pattern = rotate[pattern];
|
||||
*(out1 + 1) = colortable[blend(hqTable[pattern], E, I, H, F, D, B)]; pattern = rotate[pattern];
|
||||
*(out1 + 0) = colortable[blend(hqTable[pattern], E, G, D, H, B, F)];
|
||||
|
||||
input++;
|
||||
out0 += 2;
|
||||
out1 += 2;
|
||||
}
|
||||
|
||||
input++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
input += pitch - 256;
|
||||
out0 += outpitch + outpitch - 512;
|
||||
out1 += outpitch + outpitch - 512;
|
||||
}
|
||||
}
|
||||
|
||||
HQ2xFilter::HQ2xFilter() {
|
||||
yuvTable = new uint32_t[32768];
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint8_t R = (i >> 0) & 31;
|
||||
uint8_t G = (i >> 5) & 31;
|
||||
uint8_t B = (i >> 10) & 31;
|
||||
|
||||
//bgr555->bgr888
|
||||
double r = (R << 3) | (R >> 2);
|
||||
double g = (G << 3) | (G >> 2);
|
||||
double b = (B << 3) | (B >> 2);
|
||||
|
||||
//bgr888->yuv888
|
||||
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
|
||||
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
|
||||
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
|
||||
|
||||
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
|
||||
}
|
||||
|
||||
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407;
|
||||
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0;
|
||||
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
|
||||
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
|
||||
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
HQ2xFilter::~HQ2xFilter() {
|
||||
delete[] yuvTable;
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
class HQ2xFilter : public Filter {
|
||||
public:
|
||||
void size(unsigned &outwidth, unsigned &outheight, unsigned width, unsigned height);
|
||||
void render(uint32_t*, unsigned, uint16_t*, unsigned, unsigned*, unsigned, unsigned);
|
||||
|
||||
HQ2xFilter();
|
||||
~HQ2xFilter();
|
||||
|
||||
private:
|
||||
static const uint8_t hqTable[256];
|
||||
uint32_t *yuvTable;
|
||||
uint32_t diff_offset;
|
||||
uint32_t diff_mask;
|
||||
uint8_t rotate[256];
|
||||
|
||||
bool same(uint16_t, uint16_t);
|
||||
bool diff(uint32_t, uint16_t);
|
||||
|
||||
void grow(uint32_t&);
|
||||
uint16_t pack(uint32_t);
|
||||
|
||||
uint16_t blend1(uint32_t, uint32_t);
|
||||
uint16_t blend2(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend3(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend4(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend5(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend6(uint32_t, uint32_t, uint32_t);
|
||||
uint16_t blend (unsigned, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, uint16_t);
|
||||
};
|
||||
|
||||
extern HQ2xFilter filter_hq2x;
|
@@ -1,15 +0,0 @@
|
||||
#include "libfilter.hpp"
|
||||
using nall::min;
|
||||
using nall::max;
|
||||
|
||||
namespace libfilter {
|
||||
#include "colortable.cpp"
|
||||
#include "filter.cpp"
|
||||
|
||||
#include "direct.cpp"
|
||||
#include "scanline.cpp"
|
||||
#include "scale2x.cpp"
|
||||
#include "lq2x.cpp"
|
||||
#include "hq2x.cpp"
|
||||
#include "ntsc.cpp"
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
#ifndef LIBFILTER_H
|
||||
#define LIBFILTER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace libfilter {
|
||||
#include "colortable.hpp"
|
||||
#include "filter.hpp"
|
||||
|
||||
#include "direct.hpp"
|
||||
#include "scanline.hpp"
|
||||
#include "scale2x.hpp"
|
||||
#include "lq2x.hpp"
|
||||
#include "hq2x.hpp"
|
||||
#include "ntsc.hpp"
|
||||
};
|
||||
|
||||
#endif
|