mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v033 release.
This release adds SPC7110 emulation, without the need for graphics packs!!, and a rewritten S-RTC (real-time clock) emulator. SPC7110 support means that Far East of Eden Zero, FEoEZ: Shounen Jump Edition, Momotarou Dentetsu Happy and Super Power League 4 are now all fully playable. I will warn you, the emulation is very slow in this version -- while most areas of each game will run at the same speed as other games, there are a few peak moments where speed will drop by up to ~50%. The reason for the slow-down is that I am currently uncertain how to determine the amount of data to decompress in advance, so I default to the maximum amount possible. The reason I am releasing now anyway, is because I beleive in the "release early, release often" paradigm. It will likely take me a few weeks to finish researching this chip, and I didn't want to keep the work I had private during that time. But rest assured, bsnes v034 should feature much faster SPC7110 emulation. neviksti, Andreas Naive and jolly_codger worked non-stop on the SPC7110 decompression algorithm for the past two weeks. caitsith2 provided valuable data to the effort. I only wish that I could've been of some use, but alas, I had no role in this. In the end, it was neviksti who managed to crack all three(!!) compression modes of this chip, which turned out to be a customized 8-bit QM-coder with a prediction model. You can read more about this here. I would also like to thank Dark Force and John Weidman (aka The Dumper) for their research notes on the SPC7110 register interface. For those who don't understand the hoopla about figuring out this compression algorithm when we already had graphics pack simulation, I should note that we have since found a few errors in these packs. Not to mention, you no longer need ~4-16MB packs for each game you wish to run. They work like any other game now. Better still, the chip can now be used to compress new graphics, eg for any future translation efforts on these titles. The real-time clocks in both Far East of Eden Zero and Dai Kaijuu Monogatari 2 will now save a ".rtc" file in your save folder, which contains the clock as set by the video game, as well as a timestamp from your computer when the time was last updated. It uses the difference between the saved timestamp and current time to update the time. This allows you to specify any time you like, whereas previously bsnes would just use your computer's current time, ignoring the time you set in-game. It also allows the "round clock by 30 seconds" option in both games to work. I avoided this before because this method makes supporting daylight savings time and such impractical, although I should note that the original hardware did not support DST, either. This method was required to pass the SPC7110 tests, and is overall much more faithful to how the original chips worked. Once again, I'd really like to personally thank neviksti for his tireless efforts. Eliminating graphics packs from SNES emulation was one of my primary reasons for getting involved in the SNES emulation scene. That neviksti managed to crack this algorithm means a lot to me. Thank you so much, neviksti. This release is dedicated to you, now go get some sleep Wink
This commit is contained in:
parent
7d83cde40a
commit
9133129209
|
@ -66,7 +66,8 @@ such software.
|
|||
libco, author: byuu
|
||||
libui, author: byuu
|
||||
OBC-1 emu, author: byuu
|
||||
S-DD1 emu, author: Andreas Naive
|
||||
S-DD1 decompressor, author: Andreas Naive
|
||||
SPC7110 decompressor, author: neviksti
|
||||
S-RTC emu, author: byuu
|
||||
|
||||
Any software listed above as exemptions may be relicensed individually from
|
||||
|
|
29
readme.txt
29
readme.txt
|
@ -1,10 +1,10 @@
|
|||
bsnes
|
||||
Version: 0.032a
|
||||
Version: 0.033
|
||||
Author: byuu
|
||||
|
||||
--------
|
||||
========
|
||||
General:
|
||||
--------
|
||||
========
|
||||
bsnes is a Super Nintendo / Super Famicom emulator that began on
|
||||
October 14th, 2004.
|
||||
|
||||
|
@ -13,9 +13,9 @@ http://byuu.org/
|
|||
|
||||
Please see license.txt for important licensing information.
|
||||
|
||||
--------------
|
||||
==============
|
||||
Configuration:
|
||||
--------------
|
||||
==============
|
||||
bsnes has two configuration files: bsnes.cfg, for program settings; and
|
||||
locale.cfg, for localization.
|
||||
|
||||
|
@ -36,9 +36,9 @@ If you wish to have multiple configuration profiles for the same user, you will
|
|||
need to make copies of the bsnes executable, and use each one in single-user
|
||||
mode.
|
||||
|
||||
------------------
|
||||
==================
|
||||
Known Limitations:
|
||||
------------------
|
||||
==================
|
||||
S-CPU
|
||||
- Multiply / divide register delays not implemented
|
||||
|
||||
|
@ -56,9 +56,9 @@ Hardware Bugs
|
|||
- S-CPU.r1 HDMA crashing bug not emulated
|
||||
- S-CPU<>S-SMP communication bus conflicts not emulated
|
||||
|
||||
---------------------
|
||||
=====================
|
||||
Unsupported Hardware:
|
||||
---------------------
|
||||
=====================
|
||||
SA-1
|
||||
Coprocessor used in many popular games, including:
|
||||
- Dragon Ball Z Hyper Dimension
|
||||
|
@ -75,13 +75,6 @@ Coprocessor used in many popular games, including:
|
|||
- Star Fox 2 (unreleased beta)
|
||||
- Super Mario World 2: Yoshi's Island
|
||||
|
||||
SPC7110
|
||||
Coprocessor used only by the following games:
|
||||
- Far East of Eden Zero
|
||||
- Far East of Eden Zero: Shounen Jump no Shou
|
||||
- Momotarou Densetsu Happy
|
||||
- Super Power League 4
|
||||
|
||||
ST-011
|
||||
SETA DSP used by Quick-move Shogi Match with Nidan Rank-holder Morita
|
||||
|
||||
|
@ -91,9 +84,9 @@ SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2
|
|||
Super Gameboy
|
||||
Cartridge passthrough used for playing Gameboy games
|
||||
|
||||
------------------------
|
||||
========================
|
||||
Unsupported Controllers:
|
||||
------------------------
|
||||
========================
|
||||
Mouse
|
||||
Super Scope
|
||||
Justifier
|
||||
|
|
23
src/Makefile
23
src/Makefile
|
@ -79,7 +79,7 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`)
|
|||
|
||||
objects = main libco hiro ruby libfilter string reader cart cheat \
|
||||
memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \
|
||||
bsx srtc sdd1 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
|
||||
bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010
|
||||
|
||||
ifeq ($(enable_gzip),true)
|
||||
objects += adler32 compress crc32 deflate gzio inffast inflate inftrees ioapi trees unzip zip zutil
|
||||
|
@ -196,16 +196,17 @@ obj/snes.$(obj): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/*
|
|||
### special chips ###
|
||||
#####################
|
||||
|
||||
obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/*
|
||||
obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/*
|
||||
obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/*
|
||||
obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/*
|
||||
obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/*
|
||||
obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/*
|
||||
obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/*
|
||||
obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/*
|
||||
obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/*
|
||||
obj/st010.$(obj): chip/st010/st010.cpp chip/st010/*
|
||||
obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/*
|
||||
obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/*
|
||||
obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/*
|
||||
obj/spc7110.$(obj): chip/spc7110/spc7110.cpp chip/spc7110/*
|
||||
obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/*
|
||||
obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/*
|
||||
obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/*
|
||||
obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/*
|
||||
obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/*
|
||||
obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/*
|
||||
obj/st010.$(obj) : chip/st010/st010.cpp chip/st010/*
|
||||
|
||||
############
|
||||
### zlib ###
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define BSNES_VERSION "0.032a"
|
||||
#define BSNES_VERSION "0.033"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "cart_header.cpp"
|
||||
|
||||
namespace memory {
|
||||
MappedRAM cartrom, cartram;
|
||||
MappedRAM cartrom, cartram, cartrtc;
|
||||
MappedRAM bscram;
|
||||
MappedRAM stArom, stAram;
|
||||
MappedRAM stBrom, stBram;
|
||||
|
@ -27,12 +27,12 @@ Cartridge::Region Cartridge::region() { return info.region; }
|
|||
bool Cartridge::loaded() { return cart.loaded; }
|
||||
|
||||
void Cartridge::load_begin(CartridgeType cart_type) {
|
||||
cart.rom = cart.ram = 0;
|
||||
cart.rom = cart.ram = cart.rtc = 0;
|
||||
bs.ram = 0;
|
||||
stA.rom = stA.ram = 0;
|
||||
stB.rom = stB.ram = 0;
|
||||
|
||||
cart.rom_size = cart.ram_size = 0;
|
||||
cart.rom_size = cart.ram_size = cart.rtc_size = 0;
|
||||
bs.ram_size = 0;
|
||||
stA.rom_size = stA.ram_size = 0;
|
||||
stB.rom_size = stB.ram_size = 0;
|
||||
|
@ -44,20 +44,21 @@ void Cartridge::load_begin(CartridgeType cart_type) {
|
|||
info.bsxflash = false;
|
||||
info.st = false;
|
||||
|
||||
info.superfx = false;
|
||||
info.sa1 = false;
|
||||
info.spc7110 = false;
|
||||
info.srtc = false;
|
||||
info.sdd1 = false;
|
||||
info.cx4 = false;
|
||||
info.dsp1 = false;
|
||||
info.dsp2 = false;
|
||||
info.dsp3 = false;
|
||||
info.dsp4 = false;
|
||||
info.obc1 = false;
|
||||
info.st010 = false;
|
||||
info.st011 = false;
|
||||
info.st018 = false;
|
||||
info.superfx = false;
|
||||
info.sa1 = false;
|
||||
info.srtc = false;
|
||||
info.sdd1 = false;
|
||||
info.spc7110 = false;
|
||||
info.spc7110rtc = false;
|
||||
info.cx4 = false;
|
||||
info.dsp1 = false;
|
||||
info.dsp2 = false;
|
||||
info.dsp3 = false;
|
||||
info.dsp4 = false;
|
||||
info.obc1 = false;
|
||||
info.st010 = false;
|
||||
info.st011 = false;
|
||||
info.st018 = false;
|
||||
|
||||
info.dsp1_mapper = DSP1Unmapped;
|
||||
|
||||
|
@ -73,6 +74,7 @@ void Cartridge::load_begin(CartridgeType cart_type) {
|
|||
void Cartridge::load_end() {
|
||||
memory::cartrom.map(cart.rom, cart.rom_size);
|
||||
memory::cartram.map(cart.ram, cart.ram_size);
|
||||
memory::cartrtc.map(cart.rtc, cart.rtc_size);
|
||||
memory::bscram.map(bs.ram, bs.ram_size);
|
||||
memory::stArom.map(stA.rom, stA.rom_size);
|
||||
memory::stAram.map(stA.ram, stA.ram_size);
|
||||
|
@ -110,6 +112,7 @@ bool Cartridge::unload() {
|
|||
|
||||
if(cart.rom) { delete[] cart.rom; cart.rom = 0; }
|
||||
if(cart.ram) { delete[] cart.ram; cart.ram = 0; }
|
||||
if(cart.rtc) { delete[] cart.rtc; cart.rtc = 0; }
|
||||
if(bs.ram) { delete[] bs.ram; bs.ram = 0; }
|
||||
if(stA.rom) { delete[] stA.rom; stA.rom = 0; }
|
||||
if(stA.ram) { delete[] stA.ram; stA.ram = 0; }
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SPC7110ROM,
|
||||
BSXROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
|
@ -48,8 +49,8 @@ public:
|
|||
struct {
|
||||
bool loaded;
|
||||
char fn[PATH_MAX];
|
||||
uint8 *rom, *ram;
|
||||
uint rom_size, ram_size;
|
||||
uint8 *rom, *ram, *rtc;
|
||||
uint rom_size, ram_size, rtc_size;
|
||||
} cart;
|
||||
|
||||
struct {
|
||||
|
@ -85,6 +86,7 @@ public:
|
|||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
|
@ -142,12 +144,13 @@ public:
|
|||
|
||||
private:
|
||||
char patchfn[PATH_MAX];
|
||||
char savefn[PATH_MAX];
|
||||
char savefn[PATH_MAX];
|
||||
char rtcfn[PATH_MAX];
|
||||
char cheatfn[PATH_MAX];
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
extern MappedRAM cartrom, cartram;
|
||||
extern MappedRAM cartrom, cartram, cartrtc;
|
||||
extern MappedRAM bscram;
|
||||
extern MappedRAM stArom, stAram;
|
||||
extern MappedRAM stBrom, stBram;
|
||||
|
|
|
@ -52,8 +52,9 @@ void Cartridge::read_header() {
|
|||
}
|
||||
|
||||
if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
//rom_type: 0xf5 = no S-RTC, 0xf9 = S-RTC
|
||||
info.spc7110 = true;
|
||||
info.spc7110rtc = (rom_type == 0xf9);
|
||||
info.mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapper == 0x20 && rom_type == 0xf3) {
|
||||
|
@ -179,7 +180,7 @@ void Cartridge::find_header() {
|
|||
score_ex = 0;
|
||||
} else {
|
||||
if(rom[0x7fc0 + MAPPER] == 0x32) score_lo++;
|
||||
else score_ex += 16;
|
||||
else score_ex += 12;
|
||||
}
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
|
|
|
@ -40,6 +40,14 @@ void Cartridge::load_cart_normal(const char *filename) {
|
|||
}
|
||||
}
|
||||
|
||||
if(info.srtc || info.spc7110rtc) {
|
||||
cart.rtc = new(zeromemory) uint8_t[cart.rtc_size = 20];
|
||||
if(load_file(get_save_filename(cart.fn, "rtc"), data, size, CompressionNone) == true) {
|
||||
memcpy(cart.rtc, data, min(size, cart.rtc_size));
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
|
@ -49,6 +57,7 @@ void Cartridge::load_cart_normal(const char *filename) {
|
|||
|
||||
void Cartridge::unload_cart_normal() {
|
||||
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
|
||||
if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size);
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "bsx/bsx.h"
|
||||
#include "srtc/srtc.h"
|
||||
#include "sdd1/sdd1.h"
|
||||
#include "spc7110/spc7110.h"
|
||||
#include "cx4/cx4.h"
|
||||
#include "dsp1/dsp1.h"
|
||||
#include "dsp2/dsp2.h"
|
||||
|
|
|
@ -0,0 +1,697 @@
|
|||
const uint8_t SPC7110Codec::EvolutionTable[53][4] = {
|
||||
//prob, nextlps, nextmps, toggle invert
|
||||
{0x5a, 1, 1,1}, //0 l,m
|
||||
{0x25, 6, 2,0}, //1 l,m
|
||||
{0x11, 8, 3,0}, //2 l,m
|
||||
{0x08, 10, 4,0}, //3 ,m
|
||||
{0x03, 12, 5,0}, //4 ,m
|
||||
{0x01, 15, 5,0}, //5 ,m
|
||||
|
||||
{0x5a, 7, 7,1}, //6 l,
|
||||
{0x3f, 19, 8,0}, //7 l,m
|
||||
{0x2c, 21, 9,0}, //8 l,m
|
||||
{0x20, 22, 10,0}, //9 ,m
|
||||
{0x17, 23, 11,0}, //10 ,m
|
||||
{0x11, 25, 12,0}, //11 ,m
|
||||
{0x0c, 26, 13,0}, //12 ,m
|
||||
{0x09, 28, 14,0}, //13 ,m
|
||||
{0x07, 29, 15,0}, //14 ,m
|
||||
{0x05, 31, 16,0}, //15 ,m
|
||||
{0x04, 32, 17,0}, //16 ,m
|
||||
{0x03, 34, 18,0}, //17 ,m
|
||||
{0x02, 35, 5,0}, //18 ,m
|
||||
|
||||
{0x5a, 20, 20,1}, //19 l,m
|
||||
{0x48, 39, 21,0}, //20 l,m
|
||||
{0x3a, 40, 22,0}, //21 l,m
|
||||
{0x2e, 42, 23,0}, //22 l,m
|
||||
{0x26, 44, 24,0}, //23 l,m
|
||||
{0x1f, 45, 25,0}, //24 l,m
|
||||
{0x19, 46, 26,0}, //25 l,m
|
||||
{0x15, 25, 27,0}, //26 l,m
|
||||
{0x11, 26, 28,0}, //27 l,m
|
||||
{0x0e, 26, 29,0}, //28 l,m
|
||||
{0x0b, 27, 30,0}, //29 ,m
|
||||
{0x09, 28, 31,0}, //30 ,m
|
||||
{0x08, 29, 32,0}, //31 l,m
|
||||
{0x07, 30, 33,0}, //32 l,m
|
||||
{0x05, 31, 34,0}, //33 l,m <--- changed lps
|
||||
{0x04, 33, 35,0}, //34 ,m ... this is NOT skipped
|
||||
{0x04, 33, 36,0}, //35 ,m
|
||||
{0x03, 34, 37,0}, //36 ,m
|
||||
{0x02, 35, 38,0}, //37 ,m ... this is NOT skipped
|
||||
{0x02, 36, 5,0}, //38 ,m
|
||||
|
||||
{0x58, 39, 40,1}, //39 l,m
|
||||
{0x4d, 47, 41,0}, //40 l,m
|
||||
{0x43, 48, 42,0}, //41 ,m
|
||||
{0x3b, 49, 43,0}, //42 ,m
|
||||
{0x34, 50, 44,0}, //43 l,m
|
||||
{0x2e, 51, 45,0}, //44 l,m
|
||||
{0x29, 44, 46,0}, //45 l,m
|
||||
{0x25, 45, 24,0}, //46 ,m
|
||||
|
||||
{0x56, 47, 48,1}, //47 l,m
|
||||
{0x4f, 47, 49,0}, //48 l,m
|
||||
{0x47, 48, 50,0}, //49 l,m
|
||||
{0x41, 49, 51,0}, //50 l,m
|
||||
{0x3c, 50, 52,0}, //51 l,m
|
||||
{0x37, 51, 43,0} //52 ,m
|
||||
};
|
||||
|
||||
const uint8_t SPC7110Codec::Mode2ContextTable[32][4] = {
|
||||
// "bit" = (lps^invert)
|
||||
//next_0, use ref pixel, next_1, use ref pixel
|
||||
// if use ref pixel, then add on the 0-4 bell number grouping
|
||||
{1, 0, 2, 0}, //0
|
||||
|
||||
{3, 1, 8, 1}, //1 prev bit 0
|
||||
{13,0, 14,0}, //2 prev bit 1
|
||||
|
||||
{15,0, 16,0}, //3 prev bit 00
|
||||
{17,0, 18,0}, //4
|
||||
{19,0, 20,0}, //5
|
||||
{21,0, 22,0}, //6
|
||||
{23,0, 24,0}, //7
|
||||
{25,0, 26,0}, //8 prev bit 01
|
||||
{25,0, 26,0}, //9
|
||||
{25,0, 26,0}, //10
|
||||
{25,0, 26,0}, //11
|
||||
{25,0, 26,0}, //12
|
||||
{27,0, 28,0}, //13 prev bit 10
|
||||
{29,0, 30,0}, //14 prev bit 11
|
||||
|
||||
{31,0, 31,0}, //15 000 ref group 0
|
||||
{31,0, 31,0}, //16 001 ref group 0
|
||||
{31,0, 31,0}, //17 000 ref group 1
|
||||
{31,0, 31,0}, //18 001 ref group 1
|
||||
{31,0, 31,0}, //19 000 ref group 2
|
||||
{31,0, 31,0}, //20 001 ref group 2
|
||||
{31,0, 31,0}, //21 000 ref group 3
|
||||
{31,0, 31,0}, //22 001 ref group 3
|
||||
{31,0, 31,0}, //23 000 ref group 4
|
||||
{31,0, 31,0}, //24 001 ref group 4
|
||||
{31,0, 31,0}, //25 010
|
||||
{31,0, 31,0}, //26 011
|
||||
{31,0, 31,0}, //27 100
|
||||
{31,0, 31,0}, //28 101
|
||||
{31,0, 31,0}, //29 110
|
||||
{31,0, 31,0}, //30 111
|
||||
|
||||
{31,0, 31,0} //31 -- used as a trap for testing purposes --
|
||||
};
|
||||
|
||||
#define PROB(x) EvolutionTable[Contexts[x].index][0]
|
||||
#define NEXT_LPS(x) EvolutionTable[Contexts[x].index][1]
|
||||
#define NEXT_MPS(x) EvolutionTable[Contexts[x].index][2]
|
||||
#define TOGGLE_INVERT(x) EvolutionTable[Contexts[x].index][3]
|
||||
#define BIT(x,y) ((x>>y)&1)
|
||||
|
||||
void SPC7110Codec::decomp_mode0(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 30;
|
||||
|
||||
uint8 top,val;
|
||||
uint8 con,mps,prob;
|
||||
uint8 flag_lps,shift,mask;
|
||||
|
||||
int out=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
|
||||
int i,bit;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=*datain;
|
||||
datain++;
|
||||
|
||||
in=*datain;
|
||||
datain++;
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i++)
|
||||
{
|
||||
if(i==-1800)
|
||||
{
|
||||
int k;
|
||||
printf("\nEvolution table:\n");
|
||||
//for(k=0;k<53;k++)
|
||||
//printf(" %d,%d //%d\n",SeenEvolution[k][0],SeenEvolution[k][1],k);
|
||||
}
|
||||
|
||||
|
||||
for(bit=0;bit<8;bit++)
|
||||
{
|
||||
//get context
|
||||
mask = (1<<(bit&3)) - 1;
|
||||
con = mask + ((inverts&mask)^(lps&mask));
|
||||
if(bit>3)
|
||||
con+=15;
|
||||
|
||||
//get PROB and MPS
|
||||
prob = PROB(con);
|
||||
mps = (BIT(out,15) ^ Contexts[con].invert);
|
||||
|
||||
if(i>=15 && i<=18 && 0)
|
||||
printf("byte %d bit %d: val=%.2X top=%.2X prob=%.2X mps=%d con=%d state=%d\n",
|
||||
i,bit,val,top,prob,mps,con,Contexts[con].index);
|
||||
|
||||
//get bit
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
out = (out << 1) + mps;
|
||||
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
out = (out << 1) + 1-mps;
|
||||
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F) // NOTE: not 0x80, it's a strange border case
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=*datain;
|
||||
datain++;
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
|
||||
if(flag_lps)
|
||||
{
|
||||
//SeenEvolution[Contexts[con].index][0]=1;
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
}
|
||||
else if(shift)
|
||||
{
|
||||
//SeenEvolution[Contexts[con].index][1]=1;
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
}
|
||||
}
|
||||
|
||||
//save byte
|
||||
*dataout = (out & 0xFF);
|
||||
dataout++;
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Codec::decomp_mode1(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 15;
|
||||
|
||||
int pixelorder[4]={0,1,2,3};
|
||||
int realorder[4];
|
||||
int a,b,c;
|
||||
int m,n;
|
||||
|
||||
uint8 top,val;
|
||||
uint8 con,prob;
|
||||
uint8 flag_lps,shift;
|
||||
|
||||
int out=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
int in_len=0;
|
||||
|
||||
int i,j,pixel;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=datain[in_len++];
|
||||
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i+=2)
|
||||
{
|
||||
if(i!=0)
|
||||
{
|
||||
//turn pixel data into bitplanes
|
||||
//and save as output
|
||||
*dataout = (BIT(out,15)<<7) + (BIT(out,13)<<6) + (BIT(out,11)<<5) + (BIT(out,9)<<4)
|
||||
+ (BIT(out,7)<<3) + (BIT(out,5)<<2) + (BIT(out,3)<<1) + BIT(out,1);
|
||||
dataout++;
|
||||
*dataout = (BIT(out,14)<<7) + (BIT(out,12)<<6) + (BIT(out,10)<<5) + (BIT(out,8)<<4)
|
||||
+ (BIT(out,6)<<3) + (BIT(out,4)<<2) + (BIT(out,2)<<1) + BIT(out,0);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
for(pixel=0;pixel<8;pixel++)
|
||||
{
|
||||
//get first symbol context
|
||||
a = ((out >> (1*2)) & 0x3);
|
||||
b = ((out >> (7*2)) & 0x3);
|
||||
c = ((out >> (8*2)) & 0x3);
|
||||
if(a==b && b==c)
|
||||
con=0;
|
||||
else if (a==b && b!=c)
|
||||
con=1;
|
||||
else if (a!=b && b==c)
|
||||
con=2;
|
||||
else if (a==c && b!=c)
|
||||
con=3;
|
||||
else
|
||||
con=4;
|
||||
|
||||
//update pixel order
|
||||
for(m=0;m<4;m++)
|
||||
if(pixelorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=pixelorder[n-1];
|
||||
pixelorder[n-1]=pixelorder[n];
|
||||
pixelorder[n]=j;
|
||||
}
|
||||
|
||||
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get context of second symbol
|
||||
con = 5 + con*2 + ((lps^inverts)&1);
|
||||
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m=0;m<4;m++)
|
||||
realorder[m]=pixelorder[m];
|
||||
//shift refence pixel c value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==c)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel b value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==b)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel a value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get pixel
|
||||
b=realorder[(lps^inverts)&3];
|
||||
out = (out<<2) + b;
|
||||
}
|
||||
}
|
||||
|
||||
//turn pixel data into bitplanes
|
||||
//and save as output.. BUT don't save second byte unless asked to
|
||||
*dataout = (BIT(out,15)<<7) + (BIT(out,13)<<6) + (BIT(out,11)<<5) + (BIT(out,9)<<4)
|
||||
+ (BIT(out,7)<<3) + (BIT(out,5)<<2) + (BIT(out,3)<<1) + BIT(out,1);
|
||||
dataout++;
|
||||
if((len&1)==0)
|
||||
{
|
||||
*dataout = (BIT(out,14)<<7) + (BIT(out,12)<<6) + (BIT(out,10)<<5) + (BIT(out,8)<<4)
|
||||
+ (BIT(out,6)<<3) + (BIT(out,4)<<2) + (BIT(out,2)<<1) + BIT(out,0);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
if(in_count==8)
|
||||
in_len--;
|
||||
//printf("Used %d bytes of input.\n",in_len);
|
||||
//return in_len;
|
||||
}
|
||||
|
||||
void SPC7110Codec::decomp_mode2(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 32;
|
||||
|
||||
int pixelorder[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
|
||||
int realorder[16];
|
||||
int a,b,c;
|
||||
int m,n;
|
||||
|
||||
uint8 bitplanebuffer[16];
|
||||
uint8 buf_idx=0;
|
||||
|
||||
uint8 top,val,prob;
|
||||
uint8 con,refcon;
|
||||
uint8 flag_lps,invertbit,shift;
|
||||
|
||||
int out=0;
|
||||
int out2=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
int in_len=0;
|
||||
|
||||
int i,j,pixel,bit;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=datain[in_len++];
|
||||
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i+=2)
|
||||
{
|
||||
for(pixel=0;pixel<8;pixel++)
|
||||
{
|
||||
//get first symbol context
|
||||
a = ((out >> (0*4)) & 0x0F);
|
||||
b = ((out >> (7*4)) & 0x0F);
|
||||
c = ((out2>> (0*4)) & 0x0F);
|
||||
if(a==b && b==c)
|
||||
refcon=0;
|
||||
else if (a==b && b!=c)
|
||||
refcon=1;
|
||||
else if (a!=b && b==c)
|
||||
refcon=2;
|
||||
else if (a==c && b!=c)
|
||||
refcon=3;
|
||||
else
|
||||
refcon=4;
|
||||
|
||||
con=0;
|
||||
|
||||
//update pixel order
|
||||
for(m=0;m<16;m++)
|
||||
if(pixelorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=pixelorder[n-1];
|
||||
pixelorder[n-1]=pixelorder[n];
|
||||
pixelorder[n]=j;
|
||||
}
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m=0;m<16;m++)
|
||||
realorder[m]=pixelorder[m];
|
||||
//shift refence pixel c value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==c)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel b value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==b)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel a value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
|
||||
//get 4 symbols
|
||||
for(bit=0;bit<4;bit++)
|
||||
{
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
invertbit = Contexts[con].invert;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get next context
|
||||
if(Mode2ContextTable[con][2*(flag_lps^invertbit)+1])
|
||||
con=Mode2ContextTable[con][2*(flag_lps^invertbit)]+refcon;
|
||||
else
|
||||
con=Mode2ContextTable[con][2*(flag_lps^invertbit)];
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b=realorder[(lps^inverts)&0x0F];
|
||||
out2 = (out2<<4) + ((out>>28)&0x0F);
|
||||
out = (out<<4) + b;
|
||||
}
|
||||
|
||||
//cludge to convert pixel data into bitplanes and respect len parameter for output buf
|
||||
*dataout = (BIT(out,31)<<7) + (BIT(out,27)<<6) + (BIT(out,23)<<5) + (BIT(out,19)<<4)
|
||||
+ (BIT(out,15)<<3) + (BIT(out,11)<<2) + (BIT(out,7)<<1) + BIT(out,3);
|
||||
dataout++;
|
||||
|
||||
if((i+1)<len)
|
||||
{
|
||||
*dataout = (BIT(out,30)<<7) + (BIT(out,26)<<6) + (BIT(out,22)<<5) + (BIT(out,18)<<4)
|
||||
+ (BIT(out,14)<<3) + (BIT(out,10)<<2) + (BIT(out,6)<<1) + BIT(out,2);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
bitplanebuffer[buf_idx++] =
|
||||
(BIT(out,29)<<7) + (BIT(out,25)<<6) + (BIT(out,21)<<5) + (BIT(out,17)<<4)
|
||||
+ (BIT(out,13)<<3) + (BIT(out,9)<<2) + (BIT(out,5)<<1) + BIT(out,1);
|
||||
bitplanebuffer[buf_idx++] =
|
||||
(BIT(out,28)<<7) + (BIT(out,24)<<6) + (BIT(out,20)<<5) + (BIT(out,16)<<4)
|
||||
+ (BIT(out,12)<<3) + (BIT(out,8)<<2) + (BIT(out,4)<<1) + BIT(out,0);
|
||||
|
||||
if(buf_idx==16)
|
||||
{
|
||||
for(m=0;m<16 && i+2<len;m++,i++)
|
||||
{
|
||||
*dataout = bitplanebuffer[m];
|
||||
dataout++;
|
||||
}
|
||||
|
||||
buf_idx=0;
|
||||
}
|
||||
}
|
||||
|
||||
if(in_count==8)
|
||||
in_len--;
|
||||
//printf("Used %d bytes of input.\n",in_len);
|
||||
//return in_len;
|
||||
}
|
||||
|
||||
#undef PROB
|
||||
#undef NEXT_LPS
|
||||
#undef NEXT_MPS
|
||||
#undef TOGGLE_INVERT
|
||||
#undef BIT
|
||||
|
||||
SPC7110Codec::SPC7110Codec() {
|
||||
buffer = new(zeromemory) uint8_t[65536];
|
||||
output = new(zeromemory) uint8_t[65536];
|
||||
}
|
||||
|
||||
SPC7110Codec::~SPC7110Codec() {
|
||||
delete[] buffer;
|
||||
delete[] output;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
class SPC7110Codec {
|
||||
public:
|
||||
uint8_t *buffer;
|
||||
uint8_t *output;
|
||||
|
||||
void decomp_mode0(int len);
|
||||
void decomp_mode1(int len);
|
||||
void decomp_mode2(int len);
|
||||
|
||||
SPC7110Codec();
|
||||
~SPC7110Codec();
|
||||
|
||||
private:
|
||||
static const uint8_t EvolutionTable[53][4];
|
||||
static const uint8_t Mode2ContextTable[32][4];
|
||||
struct ContextState {
|
||||
uint8_t index;
|
||||
uint8_t invert;
|
||||
} Contexts[32];
|
||||
};
|
|
@ -0,0 +1,690 @@
|
|||
#include "../../base.h"
|
||||
#define SPC7110_CPP
|
||||
|
||||
#include "codec.cpp"
|
||||
|
||||
const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
void SPC7110::init() {}
|
||||
|
||||
void SPC7110::enable() {
|
||||
uint16_t limit = (cartridge.info.spc7110rtc ? 0x4842 : 0x483f);
|
||||
for(uint16_t i = 0x4800; i <= limit; i++) memory::mmio.map(i, *this);
|
||||
}
|
||||
|
||||
void SPC7110::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SPC7110::reset() {
|
||||
r4801 = 0x00;
|
||||
r4802 = 0x00;
|
||||
r4803 = 0x00;
|
||||
r4804 = 0x00;
|
||||
r4805 = 0x00;
|
||||
r4806 = 0x00;
|
||||
r4807 = 0x00;
|
||||
r4808 = 0x00;
|
||||
r4809 = 0x00;
|
||||
r480a = 0x00;
|
||||
r480b = 0x00;
|
||||
r480c = 0x00;
|
||||
|
||||
memset(codec.output, 0, 65536);
|
||||
memset(codec.buffer, 0, 65536);
|
||||
decomp_offset = 0;
|
||||
|
||||
r4811 = 0x00;
|
||||
r4812 = 0x00;
|
||||
r4813 = 0x00;
|
||||
r4814 = 0x00;
|
||||
r4815 = 0x00;
|
||||
r4816 = 0x00;
|
||||
r4817 = 0x00;
|
||||
r4818 = 0x00;
|
||||
|
||||
r481x = 0x00;
|
||||
r4814_latch = false;
|
||||
r4815_latch = false;
|
||||
|
||||
r4820 = 0x00;
|
||||
r4821 = 0x00;
|
||||
r4822 = 0x00;
|
||||
r4823 = 0x00;
|
||||
r4824 = 0x00;
|
||||
r4825 = 0x00;
|
||||
r4826 = 0x00;
|
||||
r4827 = 0x00;
|
||||
r4828 = 0x00;
|
||||
r4829 = 0x00;
|
||||
r482a = 0x00;
|
||||
r482b = 0x00;
|
||||
r482c = 0x00;
|
||||
r482d = 0x00;
|
||||
r482e = 0x00;
|
||||
r482f = 0x00;
|
||||
|
||||
r4830 = 0x00;
|
||||
mmio_write(0x4831, 0);
|
||||
mmio_write(0x4832, 1);
|
||||
mmio_write(0x4833, 2);
|
||||
r4834 = 0x00;
|
||||
|
||||
r4840 = 0x00;
|
||||
r4841 = 0x00;
|
||||
r4842 = 0x00;
|
||||
|
||||
if(cartridge.info.spc7110rtc) {
|
||||
rtc_state = RTCS_Inactive;
|
||||
rtc_mode = RTCM_Linear;
|
||||
rtc_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SPC7110::datarom_addr(unsigned addr) {
|
||||
unsigned size = memory::cartrom.size() - 0x100000;
|
||||
while(addr >= size) addr -= size;
|
||||
return addr + 0x100000;
|
||||
}
|
||||
|
||||
unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); }
|
||||
unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); }
|
||||
unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); }
|
||||
void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; }
|
||||
void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; }
|
||||
|
||||
void SPC7110::update_time(int offset) {
|
||||
time_t rtc_time;
|
||||
rtc_time = memory::cartrtc.read(16);
|
||||
rtc_time |= memory::cartrtc.read(17) << 8;
|
||||
rtc_time |= memory::cartrtc.read(18) << 16;
|
||||
rtc_time |= memory::cartrtc.read(19) << 24;
|
||||
|
||||
bool update = true;
|
||||
if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set
|
||||
if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set
|
||||
|
||||
time_t current_time = time(0) - offset;
|
||||
if(update && current_time > rtc_time) {
|
||||
unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
|
||||
unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10;
|
||||
unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10;
|
||||
unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
|
||||
unsigned month = memory::cartrtc.read( 8) + memory::cartrtc.read( 9) * 10;
|
||||
unsigned year = memory::cartrtc.read(10) + memory::cartrtc.read(11) * 10;
|
||||
unsigned weekday = memory::cartrtc.read(12);
|
||||
|
||||
day--;
|
||||
month--;
|
||||
year += (year >= 90) ? 1900 : 2000; //range = 1990-2089
|
||||
|
||||
second += (unsigned)(current_time - rtc_time);
|
||||
while(second >= 60) {
|
||||
second -= 60;
|
||||
|
||||
minute++;
|
||||
if(minute < 60) continue;
|
||||
minute = 0;
|
||||
|
||||
hour++;
|
||||
if(hour < 24) continue;
|
||||
hour = 0;
|
||||
|
||||
day++;
|
||||
weekday = (weekday + 1) % 7;
|
||||
unsigned days = months[month % 12];
|
||||
if(days == 28) {
|
||||
bool leapyear = false;
|
||||
if((year % 4) == 0) {
|
||||
leapyear = true;
|
||||
if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
|
||||
}
|
||||
if(leapyear) days++;
|
||||
}
|
||||
if(day < days) continue;
|
||||
day = 0;
|
||||
|
||||
month++;
|
||||
if(month < 12) continue;
|
||||
month = 0;
|
||||
|
||||
year++;
|
||||
}
|
||||
|
||||
day++;
|
||||
month++;
|
||||
year %= 100;
|
||||
|
||||
memory::cartrtc.write( 0, second % 10);
|
||||
memory::cartrtc.write( 1, second / 10);
|
||||
memory::cartrtc.write( 2, minute % 10);
|
||||
memory::cartrtc.write( 3, minute / 10);
|
||||
memory::cartrtc.write( 4, hour % 10);
|
||||
memory::cartrtc.write( 5, hour / 10);
|
||||
memory::cartrtc.write( 6, day % 10);
|
||||
memory::cartrtc.write( 7, day / 10);
|
||||
memory::cartrtc.write( 8, month % 10);
|
||||
memory::cartrtc.write( 9, month / 10);
|
||||
memory::cartrtc.write(10, year % 10);
|
||||
memory::cartrtc.write(11, (year / 10) % 10);
|
||||
memory::cartrtc.write(12, weekday % 7);
|
||||
}
|
||||
|
||||
memory::cartrtc.write(16, current_time);
|
||||
memory::cartrtc.write(17, current_time >> 8);
|
||||
memory::cartrtc.write(18, current_time >> 16);
|
||||
memory::cartrtc.write(19, current_time >> 24);
|
||||
}
|
||||
|
||||
uint8 SPC7110::mmio_read(uint addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
//==================
|
||||
//decompression unit
|
||||
//==================
|
||||
|
||||
case 0x4800: {
|
||||
uint16 counter = (r4809 + (r480a << 8));
|
||||
counter--;
|
||||
r4809 = counter;
|
||||
r480a = counter >> 8;
|
||||
return codec.output[(decomp_offset++) & 0xffff];
|
||||
}
|
||||
case 0x4801: return r4801;
|
||||
case 0x4802: return r4802;
|
||||
case 0x4803: return r4803;
|
||||
case 0x4804: return r4804;
|
||||
case 0x4805: return r4805;
|
||||
case 0x4806: return r4806;
|
||||
case 0x4807: return r4807;
|
||||
case 0x4808: return r4808;
|
||||
case 0x4809: return r4809;
|
||||
case 0x480a: return r480a;
|
||||
case 0x480b: return r480b;
|
||||
case 0x480c: {
|
||||
uint8 status = r480c;
|
||||
r480c &= 0x7f;
|
||||
return status;
|
||||
}
|
||||
|
||||
//==============
|
||||
//data port unit
|
||||
//==============
|
||||
|
||||
case 0x4810: {
|
||||
if(r481x != 0x1f) return 0x00;
|
||||
|
||||
unsigned addr = data_pointer();
|
||||
unsigned adjust = data_adjust();
|
||||
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
|
||||
|
||||
unsigned adjustaddr = addr;
|
||||
if(r4818 & 2) {
|
||||
adjustaddr += adjust;
|
||||
set_data_adjust(adjust + 1);
|
||||
}
|
||||
|
||||
uint8 data = memory::cartrom.read(datarom_addr(adjustaddr));
|
||||
if(!(r4818 & 2)) {
|
||||
unsigned increment = (r4818 & 1) ? data_increment() : 1;
|
||||
if(r4818 & 4) increment = (int16)increment; //16-bit sign extend
|
||||
|
||||
if((r4818 & 16) == 0) {
|
||||
set_data_pointer(addr + increment);
|
||||
} else {
|
||||
set_data_adjust(adjust + increment);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
case 0x4811: return r4811;
|
||||
case 0x4812: return r4812;
|
||||
case 0x4813: return r4813;
|
||||
case 0x4814: return r4814;
|
||||
case 0x4815: return r4815;
|
||||
case 0x4816: return r4816;
|
||||
case 0x4817: return r4817;
|
||||
case 0x4818: return r4818;
|
||||
case 0x481a: {
|
||||
if(r481x != 0x1f) return 0x00;
|
||||
|
||||
unsigned addr = data_pointer();
|
||||
unsigned adjust = data_adjust();
|
||||
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
|
||||
|
||||
uint8 data = memory::cartrom.read(datarom_addr(addr + adjust));
|
||||
if((r4818 & 0x60) == 0x60) {
|
||||
if((r4818 & 16) == 0) {
|
||||
set_data_pointer(addr + adjust);
|
||||
} else {
|
||||
set_data_adjust(adjust + adjust);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//=========
|
||||
//math unit
|
||||
//=========
|
||||
|
||||
case 0x4820: return r4820;
|
||||
case 0x4821: return r4821;
|
||||
case 0x4822: return r4822;
|
||||
case 0x4823: return r4823;
|
||||
case 0x4824: return r4824;
|
||||
case 0x4825: return r4825;
|
||||
case 0x4826: return r4826;
|
||||
case 0x4827: return r4827;
|
||||
case 0x4828: return r4828;
|
||||
case 0x4829: return r4829;
|
||||
case 0x482a: return r482a;
|
||||
case 0x482b: return r482b;
|
||||
case 0x482c: return r482c;
|
||||
case 0x482d: return r482d;
|
||||
case 0x482e: return r482e;
|
||||
case 0x482f: {
|
||||
uint8 status = r482f;
|
||||
r482f &= 0x7f;
|
||||
return status;
|
||||
}
|
||||
|
||||
//===================
|
||||
//memory mapping unit
|
||||
//===================
|
||||
|
||||
case 0x4830: return r4830;
|
||||
case 0x4831: return r4831;
|
||||
case 0x4832: return r4832;
|
||||
case 0x4833: return r4833;
|
||||
case 0x4834: return r4834;
|
||||
|
||||
//====================
|
||||
//real-time clock unit
|
||||
//====================
|
||||
|
||||
case 0x4840: return r4840;
|
||||
case 0x4841: {
|
||||
if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00;
|
||||
|
||||
r4842 = 0x80;
|
||||
uint8 data = memory::cartrtc.read(rtc_index);
|
||||
rtc_index = (rtc_index + 1) & 15;
|
||||
return data;
|
||||
}
|
||||
case 0x4842: {
|
||||
uint8 status = r4842;
|
||||
r4842 &= 0x7f;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void SPC7110::mmio_write(uint addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
//==================
|
||||
//decompression unit
|
||||
//==================
|
||||
|
||||
case 0x4801: r4801 = data; break;
|
||||
case 0x4802: r4802 = data; break;
|
||||
case 0x4803: r4803 = data; break;
|
||||
case 0x4804: r4804 = data; break;
|
||||
case 0x4805: r4805 = data; break;
|
||||
case 0x4806: {
|
||||
r4806 = data;
|
||||
|
||||
unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16));
|
||||
unsigned index = (r4804 << 2);
|
||||
unsigned length = (r4809 + (r480a << 8));
|
||||
unsigned addr = datarom_addr(table + index);
|
||||
unsigned mode = (memory::cartrom.read(addr + 0));
|
||||
unsigned offset = (memory::cartrom.read(addr + 1) << 16)
|
||||
+ (memory::cartrom.read(addr + 2) << 8)
|
||||
+ (memory::cartrom.read(addr + 3) << 0);
|
||||
|
||||
//this can technically be 65536, but it has never been observed higher than 32768 ...
|
||||
//really need a way to determine both compressed and decompressed lengths, though.
|
||||
static const unsigned max_length = 32768;
|
||||
|
||||
offset = datarom_addr(offset);
|
||||
for(unsigned i = 0; i < max_length; i++) codec.buffer[i] = memory::cartrom.read(offset + i);
|
||||
|
||||
#if 0
|
||||
printf("decompression: 4805=$%0.2x,4806=$%0.2x,4807=$%0.2x,4808=$%0.2x,480b=$%0.2x\n",
|
||||
r4805, r4806, r4807, r4808, r480b);
|
||||
printf("table=$%0.6x,index=%3d,length=%5d,mode=%d,offset=$%0.6x\n",
|
||||
table, r4804, length, mode, offset);
|
||||
#endif
|
||||
|
||||
switch(mode) {
|
||||
case 0: codec.decomp_mode0(max_length); break;
|
||||
case 1: codec.decomp_mode1(max_length); break;
|
||||
case 2: codec.decomp_mode2(max_length); break;
|
||||
}
|
||||
|
||||
decomp_offset = (r4805 + (r4806 << 8)) << mode;
|
||||
r480c = 0x80;
|
||||
} break;
|
||||
|
||||
case 0x4807: r4807 = data; break;
|
||||
case 0x4808: r4808 = data; break;
|
||||
case 0x4809: r4809 = data; break;
|
||||
case 0x480a: r480a = data; break;
|
||||
case 0x480b: r480b = data; break;
|
||||
|
||||
//==============
|
||||
//data port unit
|
||||
//==============
|
||||
|
||||
case 0x4811: r4811 = data; r481x |= 0x01; break;
|
||||
case 0x4812: r4812 = data; r481x |= 0x02; break;
|
||||
case 0x4813: r4813 = data; r481x |= 0x04; break;
|
||||
case 0x4814: {
|
||||
r4814 = data;
|
||||
r481x |= 0x08;
|
||||
r4814_latch = true;
|
||||
if(!r4815_latch) break;
|
||||
if(!(r4818 & 2)) break;
|
||||
if(r4818 & 0x10) break;
|
||||
|
||||
if((r4818 & 0x60) == 0x20) {
|
||||
unsigned increment = data_adjust() & 0xff;
|
||||
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
|
||||
set_data_pointer(data_pointer() + increment);
|
||||
} else if((r4818 & 0x60) == 0x40) {
|
||||
unsigned increment = data_adjust();
|
||||
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
|
||||
set_data_pointer(data_pointer() + increment);
|
||||
}
|
||||
} break;
|
||||
case 0x4815: {
|
||||
r4815 = data;
|
||||
r481x |= 0x10;
|
||||
r4815_latch = true;
|
||||
if(!r4814_latch) break;
|
||||
if(!(r4818 & 2)) break;
|
||||
if(r4818 & 0x10) break;
|
||||
|
||||
if((r4818 & 0x60) == 0x20) {
|
||||
unsigned increment = data_adjust() & 0xff;
|
||||
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
|
||||
set_data_pointer(data_pointer() + increment);
|
||||
} else if((r4818 & 0x60) == 0x40) {
|
||||
unsigned increment = data_adjust();
|
||||
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
|
||||
set_data_pointer(data_pointer() + increment);
|
||||
}
|
||||
} break;
|
||||
case 0x4816: r4816 = data; break;
|
||||
case 0x4817: r4817 = data; break;
|
||||
case 0x4818: {
|
||||
if(r481x != 0x1f) break;
|
||||
|
||||
r4818 = data;
|
||||
r4814_latch = r4815_latch = false;
|
||||
} break;
|
||||
|
||||
//=========
|
||||
//math unit
|
||||
//=========
|
||||
|
||||
case 0x4820: r4820 = data; break;
|
||||
case 0x4821: r4821 = data; break;
|
||||
case 0x4822: r4822 = data; break;
|
||||
case 0x4823: r4823 = data; break;
|
||||
case 0x4824: r4824 = data; break;
|
||||
case 0x4825: {
|
||||
r4825 = data;
|
||||
|
||||
if(r482e & 1) {
|
||||
//signed 16-bit x 16-bit multiplication
|
||||
int16 r0 = (int16)(r4824 + (r4825 << 8));
|
||||
int16 r1 = (int16)(r4820 + (r4821 << 8));
|
||||
|
||||
signed result = r0 * r1;
|
||||
r4828 = result;
|
||||
r4829 = result >> 8;
|
||||
r482a = result >> 16;
|
||||
r482b = result >> 24;
|
||||
} else {
|
||||
//unsigned 16-bit x 16-bit multiplication
|
||||
uint16 r0 = (uint16)(r4824 + (r4825 << 8));
|
||||
uint16 r1 = (uint16)(r4820 + (r4821 << 8));
|
||||
|
||||
unsigned result = r0 * r1;
|
||||
r4828 = result;
|
||||
r4829 = result >> 8;
|
||||
r482a = result >> 16;
|
||||
r482b = result >> 24;
|
||||
}
|
||||
|
||||
r482f = 0x80;
|
||||
} break;
|
||||
case 0x4826: r4826 = data; break;
|
||||
case 0x4827: {
|
||||
r4827 = data;
|
||||
|
||||
if(r482e & 1) {
|
||||
//signed 32-bit x 16-bit division
|
||||
int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
|
||||
int16 divisor = (int16)(r4826 + (r4827 << 8));
|
||||
|
||||
int32 quotient;
|
||||
int16 remainder;
|
||||
|
||||
if(divisor) {
|
||||
quotient = (int32)(dividend / divisor);
|
||||
remainder = (int32)(dividend % divisor);
|
||||
} else {
|
||||
//illegal division by zero
|
||||
quotient = 0;
|
||||
remainder = dividend & 0xffff;
|
||||
}
|
||||
|
||||
r4828 = quotient;
|
||||
r4829 = quotient >> 8;
|
||||
r482a = quotient >> 16;
|
||||
r482b = quotient >> 24;
|
||||
|
||||
r482c = remainder;
|
||||
r482d = remainder >> 8;
|
||||
} else {
|
||||
//unsigned 32-bit x 16-bit division
|
||||
uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
|
||||
uint16 divisor = (uint16)(r4826 + (r4827 << 8));
|
||||
|
||||
uint32 quotient;
|
||||
uint16 remainder;
|
||||
|
||||
if(divisor) {
|
||||
quotient = (uint32)(dividend / divisor);
|
||||
remainder = (uint16)(dividend % divisor);
|
||||
} else {
|
||||
//illegal division by zero
|
||||
quotient = 0;
|
||||
remainder = dividend & 0xffff;
|
||||
}
|
||||
|
||||
r4828 = quotient;
|
||||
r4829 = quotient >> 8;
|
||||
r482a = quotient >> 16;
|
||||
r482b = quotient >> 24;
|
||||
|
||||
r482c = remainder;
|
||||
r482d = remainder >> 8;
|
||||
}
|
||||
|
||||
r482f = 0x80;
|
||||
} break;
|
||||
|
||||
case 0x482e: {
|
||||
//reset math unit
|
||||
r4820 = r4821 = r4822 = r4823 = 0;
|
||||
r4824 = r4825 = r4826 = r4827 = 0;
|
||||
r4828 = r4829 = r482a = r482b = 0;
|
||||
r482c = r482d = 0;
|
||||
|
||||
r482e = data;
|
||||
} break;
|
||||
|
||||
//===================
|
||||
//memory mapping unit
|
||||
//===================
|
||||
|
||||
case 0x4830: r4830 = data; break;
|
||||
|
||||
case 0x4831: {
|
||||
r4831 = data;
|
||||
dx_offset = datarom_addr((data & 7) * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4832: {
|
||||
r4832 = data;
|
||||
ex_offset = datarom_addr((data & 7) * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4833: {
|
||||
r4833 = data;
|
||||
fx_offset = datarom_addr((data & 7) * 0x100000);
|
||||
} break;
|
||||
|
||||
case 0x4834: r4834 = data; break;
|
||||
|
||||
//====================
|
||||
//real-time clock unit
|
||||
//====================
|
||||
|
||||
case 0x4840: {
|
||||
r4840 = data;
|
||||
if(!(r4840 & 1)) {
|
||||
//disable RTC
|
||||
rtc_state = RTCS_Inactive;
|
||||
update_time();
|
||||
} else {
|
||||
//enable RTC
|
||||
r4842 = 0x80;
|
||||
rtc_state = RTCS_ModeSelect;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 0x4841: {
|
||||
r4841 = data;
|
||||
|
||||
switch(rtc_state) {
|
||||
case RTCS_ModeSelect: {
|
||||
if(data == RTCM_Linear || data == RTCM_Indexed) {
|
||||
r4842 = 0x80;
|
||||
rtc_state = RTCS_IndexSelect;
|
||||
rtc_mode = (RTC_Mode)data;
|
||||
rtc_index = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case RTCS_IndexSelect: {
|
||||
r4842 = 0x80;
|
||||
rtc_index = data & 15;
|
||||
if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write;
|
||||
} break;
|
||||
|
||||
case RTCS_Write: {
|
||||
r4842 = 0x80;
|
||||
|
||||
//control register 0
|
||||
if(rtc_index == 13) {
|
||||
//increment second counter
|
||||
if(data & 2) update_time(+1);
|
||||
|
||||
//round minute counter
|
||||
if(data & 8) {
|
||||
update_time();
|
||||
|
||||
unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
|
||||
//clear seconds
|
||||
memory::cartrtc.write(0, 0);
|
||||
memory::cartrtc.write(1, 0);
|
||||
|
||||
if(second >= 30) update_time(+60);
|
||||
}
|
||||
}
|
||||
|
||||
//control register 2
|
||||
if(rtc_index == 15) {
|
||||
//disable timer and clear second counter
|
||||
if((data & 1) && !(memory::cartrtc.read(15) & 1)) {
|
||||
update_time();
|
||||
|
||||
//clear seconds
|
||||
memory::cartrtc.write(0, 0);
|
||||
memory::cartrtc.write(1, 0);
|
||||
}
|
||||
|
||||
//disable timer
|
||||
if((data & 2) && !(memory::cartrtc.read(15) & 2)) {
|
||||
update_time();
|
||||
}
|
||||
}
|
||||
|
||||
memory::cartrtc.write(rtc_index, data & 15);
|
||||
rtc_index = (rtc_index + 1) & 15;
|
||||
} break;
|
||||
} //switch(rtc_state)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 SPC7110::read(uint addr) {
|
||||
if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
|
||||
//$[00|30]:[6000-7fff]
|
||||
return memory::cartram.read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0x708000) == 0x008000) {
|
||||
//$[00-0f|80-8f]:[8000-ffff]
|
||||
return memory::cartrom.read(addr & 0x0fffff);
|
||||
}
|
||||
|
||||
if((addr & 0xff0000) == 0x500000) {
|
||||
//$[50]:[0000-ffff]
|
||||
return mmio_read(0x4800);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xc00000) {
|
||||
//$[c0-cf]:[0000-ffff]
|
||||
return memory::cartrom.read(addr & 0x0fffff);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xd00000) {
|
||||
//$[d0-df]:[0000-ffff]
|
||||
return memory::cartrom.read(dx_offset + (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xe00000) {
|
||||
//$[e0-ef]:[0000-ffff]
|
||||
return memory::cartrom.read(ex_offset + (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xf00000) {
|
||||
//$[f0-ff]:[0000-ffff]
|
||||
return memory::cartrom.read(fx_offset + (addr & 0x0fffff));
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void SPC7110::write(uint addr, uint8 data) {
|
||||
if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
|
||||
//$[00|30]:[6000-7fff]
|
||||
if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SPC7110::SPC7110() {
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*****
|
||||
* SPC7110 emulator - version 0.1 (2008-07-19)
|
||||
* Copyright (c) 2008, byuu and neviksti
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* The software is provided "as is" and the author disclaims all warranties
|
||||
* with regard to this software including all implied warranties of
|
||||
* merchantibility and fitness, in no event shall the author be liable for
|
||||
* any special, direct, indirect, or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether in an
|
||||
* action of contract, negligence or other tortious action, arising out of
|
||||
* or in connection with the use or performance of this software.
|
||||
*****/
|
||||
|
||||
#include "codec.h"
|
||||
|
||||
class SPC7110 : public MMIO, public Memory {
|
||||
public:
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
unsigned datarom_addr(unsigned addr);
|
||||
|
||||
unsigned data_pointer();
|
||||
unsigned data_adjust();
|
||||
unsigned data_increment();
|
||||
void set_data_pointer(unsigned addr);
|
||||
void set_data_adjust(unsigned addr);
|
||||
|
||||
void update_time(int offset = 0);
|
||||
time_t create_time();
|
||||
|
||||
uint8 mmio_read (uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
uint8 read (uint addr);
|
||||
void write(uint addr, uint8 data);
|
||||
|
||||
SPC7110();
|
||||
|
||||
private:
|
||||
//==================
|
||||
//decompression unit
|
||||
//==================
|
||||
uint8 r4801; //compression table low
|
||||
uint8 r4802; //compression table high
|
||||
uint8 r4803; //compression table bank
|
||||
uint8 r4804; //compression table index
|
||||
uint8 r4805; //decompression buffer index low
|
||||
uint8 r4806; //decompression buffer index high
|
||||
uint8 r4807; //???
|
||||
uint8 r4808; //???
|
||||
uint8 r4809; //compression length low
|
||||
uint8 r480a; //compression length high
|
||||
uint8 r480b; //decompression control register
|
||||
uint8 r480c; //decompression status
|
||||
|
||||
SPC7110Codec codec;
|
||||
uint16 decomp_offset;
|
||||
|
||||
//==============
|
||||
//data port unit
|
||||
//==============
|
||||
uint8 r4811; //data pointer low
|
||||
uint8 r4812; //data pointer high
|
||||
uint8 r4813; //data pointer bank
|
||||
uint8 r4814; //data adjust low
|
||||
uint8 r4815; //data adjust high
|
||||
uint8 r4816; //data increment low
|
||||
uint8 r4817; //data increment high
|
||||
uint8 r4818; //data port control register
|
||||
|
||||
uint8 r481x;
|
||||
|
||||
bool r4814_latch;
|
||||
bool r4815_latch;
|
||||
|
||||
//=========
|
||||
//math unit
|
||||
//=========
|
||||
uint8 r4820; //16-bit multiplicand B0, 32-bit dividend B0
|
||||
uint8 r4821; //16-bit multiplicand B1, 32-bit dividend B1
|
||||
uint8 r4822; //32-bit dividend B2
|
||||
uint8 r4823; //32-bit dividend B3
|
||||
uint8 r4824; //16-bit multiplier B0
|
||||
uint8 r4825; //16-bit multiplier B1
|
||||
uint8 r4826; //16-bit divisor B0
|
||||
uint8 r4827; //16-bit divisor B1
|
||||
uint8 r4828; //32-bit product B0, 32-bit quotient B0
|
||||
uint8 r4829; //32-bit product B1, 32-bit quotient B1
|
||||
uint8 r482a; //32-bit product B2, 32-bit quotient B2
|
||||
uint8 r482b; //32-bit product B3, 32-bit quotient B3
|
||||
uint8 r482c; //16-bit remainder B0
|
||||
uint8 r482d; //16-bit remainder B1
|
||||
uint8 r482e; //math control register
|
||||
uint8 r482f; //math status
|
||||
|
||||
//===================
|
||||
//memory mapping unit
|
||||
//===================
|
||||
uint8 r4830; //SRAM write enable
|
||||
uint8 r4831; //$[d0-df]:[0000-ffff] mapping
|
||||
uint8 r4832; //$[e0-ef]:[0000-ffff] mapping
|
||||
uint8 r4833; //$[f0-ff]:[0000-ffff] mapping
|
||||
uint8 r4834; //???
|
||||
|
||||
unsigned dx_offset;
|
||||
unsigned ex_offset;
|
||||
unsigned fx_offset;
|
||||
|
||||
//====================
|
||||
//real-time clock unit
|
||||
//====================
|
||||
uint8 r4840; //RTC latch
|
||||
uint8 r4841; //RTC index/data port
|
||||
uint8 r4842; //RTC status
|
||||
|
||||
enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write } rtc_state;
|
||||
enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c } rtc_mode;
|
||||
unsigned rtc_index;
|
||||
|
||||
static const unsigned months[12];
|
||||
};
|
||||
|
||||
extern SPC7110 spc7110;
|
|
@ -1,189 +1,213 @@
|
|||
/*
|
||||
S-RTC chip emulation
|
||||
Used by Hudson Soft in Dai Kaijuu Monogatari II and Far East of Eden Zero.
|
||||
Currently, only the former is supported by bsnes.
|
||||
|
||||
Original S-RTC emulation code via John Weidman/SNES9x
|
||||
Rewritten for compatibility with bsnes via byuu
|
||||
|
||||
The S-RTC is a real-time clock chip that was added to the above two carts
|
||||
to allow the games to maintain the current time, even when the game was not
|
||||
powered on. Thus allowing special events at certain times, and on certain
|
||||
dates. Hudson Soft called this the PLG (Player's Life Gameplay System).
|
||||
|
||||
This chip is a special case to the term 'emulation' itself.
|
||||
There are a few different ways to go about emulating this chip, and each
|
||||
result in a different style of emulation.
|
||||
|
||||
The first is to simply return the current PC system time when the S-RTC is
|
||||
read from. This emulates the original S-RTC in the sense that it always
|
||||
returns the true current time, ignoring the speed that the SNES itself is
|
||||
running at. The downside to this method is that you lose the ability to set
|
||||
the time to whatever you choose inside the game itself. It will always return
|
||||
the true time, regardless. This can be overcome by changing the PC system time,
|
||||
which actually adds a greater degree of control over event timing, very useful
|
||||
for emulation. It also has a timeshifting flaw discussed below.
|
||||
|
||||
The second is to run the S-RTC relative to the SNES speed. This means that
|
||||
if the emulator is sped up (via fast forward key, frameskipping, etc), or
|
||||
slowed down (via slowdown key, system bottlenecking, etc); the time increments
|
||||
slower, thus ~60 frames on the SNES equal one second. Without this, timeshifting
|
||||
will occur between the S-RTC and the real SNES.
|
||||
|
||||
The third and final method is to save a copy of the local system time when the
|
||||
S-RTC is initially set, and compare the current system time against this value
|
||||
when setting the S-RTC time. This overcomes the first methods' shortcoming of
|
||||
not allowing the player to set the time in-game, however a new problem arises.
|
||||
You now have to save the time when the RTC was initially set to both savestates
|
||||
and to save-game data. This would require an extra file, or the breaking of
|
||||
perhaps the only standard format (.srm savegame backups) in the entire SNES
|
||||
emulation scene. You also give up the control of being able to override the
|
||||
RTC clock at will via the PC system time outside of emulation.
|
||||
The first method has another advantage over the third: Dai Kaijuu Monogatari II
|
||||
only allows dates in the range of the years 1996-2199. The first method gets
|
||||
around this limitation. But who knows, maybe it will break something in the
|
||||
game if the date exceeds 2199... I guess we'll worry about that in two hundred
|
||||
years from now.
|
||||
|
||||
For my implementation, I chose to go with the first method. Both for simplicity
|
||||
and because I did not wish to create a new method for saving the system time
|
||||
whenever the RTC is set.
|
||||
*/
|
||||
|
||||
#include "../../base.h"
|
||||
|
||||
void SRTC::set_time() {
|
||||
time_t rawtime;
|
||||
tm *t;
|
||||
::time(&rawtime);
|
||||
t = localtime(&rawtime);
|
||||
|
||||
//see srtc.h for format of srtc.data[]
|
||||
srtc.data[0] = t->tm_sec % 10;
|
||||
srtc.data[1] = t->tm_sec / 10;
|
||||
srtc.data[2] = t->tm_min % 10;
|
||||
srtc.data[3] = t->tm_min / 10;
|
||||
srtc.data[4] = t->tm_hour % 10;
|
||||
srtc.data[5] = t->tm_hour / 10;
|
||||
srtc.data[6] = t->tm_mday % 10;
|
||||
srtc.data[7] = t->tm_mday / 10;
|
||||
srtc.data[8] = t->tm_mon + 1;
|
||||
srtc.data[9] = t->tm_year % 10;
|
||||
srtc.data[10] = (t->tm_year / 10) % 10;
|
||||
srtc.data[11] = 9 + (t->tm_year / 100);
|
||||
srtc.data[12] = t->tm_wday;
|
||||
}
|
||||
|
||||
void SRTC::init() {}
|
||||
|
||||
void SRTC::enable() {
|
||||
memory::mmio.map(0x2800, *this);
|
||||
memory::mmio.map(0x2801, *this);
|
||||
}
|
||||
|
||||
void SRTC::power() {
|
||||
memset(&srtc, 0, sizeof(srtc));
|
||||
reset();
|
||||
}
|
||||
|
||||
void SRTC::reset() {
|
||||
srtc.index = -1;
|
||||
srtc.mode = SRTC_READ;
|
||||
}
|
||||
|
||||
uint8 SRTC::mmio_read(uint addr) {
|
||||
switch(addr & 0xffff) {
|
||||
|
||||
case 0x2800: {
|
||||
if(srtc.mode == SRTC_READ) {
|
||||
if(srtc.index < 0) {
|
||||
set_time();
|
||||
srtc.index++;
|
||||
return 0x0f; //send start message
|
||||
} else if(srtc.index > MAX_SRTC_INDEX) {
|
||||
srtc.index = -1;
|
||||
return 0x0f; //send finished message
|
||||
} else {
|
||||
return srtc.data[srtc.index++];
|
||||
}
|
||||
} else {
|
||||
return 0x00;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 0x2801: {
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
//Please see notes above about the implementation of the S-RTC
|
||||
//Writes are stored the srtc.data[] array, but they are ignored
|
||||
//as reads will refresh the data array with the current system
|
||||
//time. The write method is only here for the sake of faux
|
||||
//emulation of the real hardware.
|
||||
void SRTC::mmio_write(uint addr, uint8 data) {
|
||||
switch(addr & 0xffff) {
|
||||
|
||||
case 0x2800: {
|
||||
} break;
|
||||
|
||||
case 0x2801: {
|
||||
data &= 0x0f; //only the low four bits are used
|
||||
|
||||
if(data >= 0x0d) {
|
||||
switch(data) {
|
||||
case 0x0d:
|
||||
srtc.mode = SRTC_READ;
|
||||
srtc.index = -1;
|
||||
break;
|
||||
case 0x0e:
|
||||
srtc.mode = SRTC_COMMAND;
|
||||
break;
|
||||
case 0x0f:
|
||||
//unknown behaviour
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(srtc.mode == SRTC_WRITE) {
|
||||
if(srtc.index >= 0 && srtc.index < MAX_SRTC_INDEX) {
|
||||
srtc.data[srtc.index++] = data;
|
||||
|
||||
if(srtc.index == MAX_SRTC_INDEX) {
|
||||
//all S-RTC data has been loaded by program
|
||||
srtc.data[srtc.index++] = 0x00; //day_of_week
|
||||
}
|
||||
}
|
||||
} else if(srtc.mode == SRTC_COMMAND) {
|
||||
switch(data) {
|
||||
case SRTC_COMMAND_CLEAR:
|
||||
memset(srtc.data, 0, MAX_SRTC_INDEX + 1);
|
||||
srtc.index = -1;
|
||||
srtc.mode = SRTC_READY;
|
||||
break;
|
||||
case SRTC_COMMAND_WRITE:
|
||||
srtc.index = 0;
|
||||
srtc.mode = SRTC_WRITE;
|
||||
break;
|
||||
default:
|
||||
//unknown behaviour
|
||||
srtc.mode = SRTC_READY;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(srtc.mode == SRTC_READ) {
|
||||
//ignore writes while in read mode
|
||||
} else if(srtc.mode == SRTC_READY) {
|
||||
//unknown behaviour
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SRTC::SRTC() {}
|
||||
#include "../../base.h"
|
||||
|
||||
const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
void SRTC::init() {
|
||||
}
|
||||
|
||||
void SRTC::enable() {
|
||||
memory::mmio.map(0x2800, *this);
|
||||
memory::mmio.map(0x2801, *this);
|
||||
}
|
||||
|
||||
void SRTC::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SRTC::reset() {
|
||||
rtc_mode = RTCM_Read;
|
||||
rtc_index = -1;
|
||||
update_time();
|
||||
}
|
||||
|
||||
void SRTC::update_time() {
|
||||
time_t rtc_time;
|
||||
rtc_time = memory::cartrtc.read(16);
|
||||
rtc_time |= memory::cartrtc.read(17) << 8;
|
||||
rtc_time |= memory::cartrtc.read(18) << 16;
|
||||
rtc_time |= memory::cartrtc.read(19) << 24;
|
||||
|
||||
time_t current_time = time(0);
|
||||
if(current_time > rtc_time) {
|
||||
unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10;
|
||||
unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10;
|
||||
unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10;
|
||||
unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
|
||||
unsigned month = memory::cartrtc.read( 8);
|
||||
unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
|
||||
unsigned weekday = memory::cartrtc.read(12);
|
||||
|
||||
day--;
|
||||
month--;
|
||||
year += 1000;
|
||||
|
||||
second += (unsigned)(current_time - rtc_time);
|
||||
|
||||
while(second >= 60) {
|
||||
second -= 60;
|
||||
|
||||
minute++;
|
||||
if(minute < 60) continue;
|
||||
minute = 0;
|
||||
|
||||
hour++;
|
||||
if(hour < 24) continue;
|
||||
hour = 0;
|
||||
|
||||
day++;
|
||||
weekday = (weekday + 1) % 7;
|
||||
unsigned days = months[month % 12];
|
||||
if(days == 28) {
|
||||
bool leapyear = false;
|
||||
if((year % 4) == 0) {
|
||||
leapyear = true;
|
||||
if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
|
||||
}
|
||||
if(leapyear) days++;
|
||||
}
|
||||
if(day < days) continue;
|
||||
day = 0;
|
||||
|
||||
month++;
|
||||
if(month < 12) continue;
|
||||
month = 0;
|
||||
|
||||
year++;
|
||||
}
|
||||
|
||||
day++;
|
||||
month++;
|
||||
year -= 1000;
|
||||
|
||||
memory::cartrtc.write( 0, second % 10);
|
||||
memory::cartrtc.write( 1, second / 10);
|
||||
memory::cartrtc.write( 2, minute % 10);
|
||||
memory::cartrtc.write( 3, minute / 10);
|
||||
memory::cartrtc.write( 4, hour % 10);
|
||||
memory::cartrtc.write( 5, hour / 10);
|
||||
memory::cartrtc.write( 6, day % 10);
|
||||
memory::cartrtc.write( 7, day / 10);
|
||||
memory::cartrtc.write( 8, month);
|
||||
memory::cartrtc.write( 9, year % 10);
|
||||
memory::cartrtc.write(10, (year / 10) % 10);
|
||||
memory::cartrtc.write(11, year / 100);
|
||||
memory::cartrtc.write(12, weekday % 7);
|
||||
}
|
||||
|
||||
memory::cartrtc.write(16, current_time);
|
||||
memory::cartrtc.write(17, current_time >> 8);
|
||||
memory::cartrtc.write(18, current_time >> 16);
|
||||
memory::cartrtc.write(19, current_time >> 24);
|
||||
}
|
||||
|
||||
//returns day of week for specified date
|
||||
//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday
|
||||
//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008
|
||||
unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) {
|
||||
unsigned y = 1900, m = 1; //epoch is 1900-01-01
|
||||
unsigned sum = 0; //number of days passed since epoch
|
||||
|
||||
year = max(1900, year);
|
||||
month = max(1, min(12, month));
|
||||
day = max(1, min(31, day));
|
||||
|
||||
while(y < year) {
|
||||
bool leapyear = false;
|
||||
if((y % 4) == 0) {
|
||||
leapyear = true;
|
||||
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
||||
}
|
||||
sum += leapyear ? 366 : 365;
|
||||
y++;
|
||||
}
|
||||
|
||||
while(m < month) {
|
||||
unsigned days = months[m - 1];
|
||||
if(days == 28) {
|
||||
bool leapyear = false;
|
||||
if((y % 4) == 0) {
|
||||
leapyear = true;
|
||||
if((y % 100) == 0 && (y % 400) != 0) leapyear = false;
|
||||
}
|
||||
if(leapyear) days++;
|
||||
}
|
||||
sum += days;
|
||||
m++;
|
||||
}
|
||||
|
||||
sum += day - 1;
|
||||
return (sum + 1) % 7; //1900-01-01 was a Monday
|
||||
}
|
||||
|
||||
uint8 SRTC::mmio_read(uint addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2800) {
|
||||
if(rtc_mode != RTCM_Read) return 0x00;
|
||||
|
||||
if(rtc_index < 0) {
|
||||
update_time();
|
||||
rtc_index++;
|
||||
return 0x0f;
|
||||
} else if(rtc_index > 12) {
|
||||
rtc_index = -1;
|
||||
return 0x0f;
|
||||
} else {
|
||||
return memory::cartrtc.read(rtc_index++);
|
||||
}
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void SRTC::mmio_write(uint addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2801) {
|
||||
data &= 0x0f; //only the low four bits are used
|
||||
|
||||
if(data == 0x0d) {
|
||||
rtc_mode = RTCM_Read;
|
||||
rtc_index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(data == 0x0e) {
|
||||
rtc_mode = RTCM_Command;
|
||||
return;
|
||||
}
|
||||
|
||||
if(data == 0x0f) return; //unknown behavior
|
||||
|
||||
if(rtc_mode == RTCM_Write) {
|
||||
if(rtc_index >= 0 && rtc_index < 12) {
|
||||
memory::cartrtc.write(rtc_index++, data);
|
||||
|
||||
if(rtc_index == 12) {
|
||||
//day of week is automatically calculated and written
|
||||
unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10;
|
||||
unsigned month = memory::cartrtc.read( 8);
|
||||
unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100;
|
||||
year += 1000;
|
||||
|
||||
memory::cartrtc.write(rtc_index++, weekday(year, month, day));
|
||||
}
|
||||
}
|
||||
} else if(rtc_mode == RTCM_Command) {
|
||||
if(data == 0) {
|
||||
rtc_mode = RTCM_Write;
|
||||
rtc_index = 0;
|
||||
} else if(data == 4) {
|
||||
rtc_mode = RTCM_Ready;
|
||||
rtc_index = -1;
|
||||
for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0);
|
||||
} else {
|
||||
//unknown behavior
|
||||
rtc_mode = RTCM_Ready;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SRTC::SRTC() {
|
||||
}
|
||||
|
|
|
@ -1,52 +1,22 @@
|
|||
class SRTC : public MMIO {
|
||||
public:
|
||||
enum { MAX_SRTC_INDEX = 0x0c };
|
||||
|
||||
enum {
|
||||
SRTC_READ = 0,
|
||||
SRTC_WRITE,
|
||||
SRTC_COMMAND,
|
||||
SRTC_READY
|
||||
};
|
||||
|
||||
enum {
|
||||
SRTC_COMMAND_WRITE = 0,
|
||||
SRTC_COMMAND_CLEAR = 4
|
||||
};
|
||||
|
||||
/******************************
|
||||
[srtc.data structure]
|
||||
Index Description Range
|
||||
----- ----------- -----
|
||||
0 Seconds low 0-9
|
||||
1 Seconds high 0-5
|
||||
2 Minutes low 0-9
|
||||
3 Minutes high 0-5
|
||||
4 Hour low 0-9
|
||||
5 Hour high 0-2
|
||||
6 Day low 0-9
|
||||
7 Day high 0-3
|
||||
8 Month 1-12
|
||||
9 Year ones 0-9
|
||||
10 Year tens 0-9
|
||||
11 Year hundreds 9-11 (9=19xx, 10=20xx, 11=21xx)
|
||||
12 Day of week 0-6 (0=Sunday, ...)
|
||||
******************************/
|
||||
struct {
|
||||
int8 index;
|
||||
uint8 mode;
|
||||
uint8 data[MAX_SRTC_INDEX + 1];
|
||||
} srtc;
|
||||
void set_time();
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 mmio_read (uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
SRTC();
|
||||
};
|
||||
|
||||
extern SRTC srtc;
|
||||
class SRTC : public MMIO {
|
||||
public:
|
||||
void update_time();
|
||||
unsigned weekday(unsigned year, unsigned month, unsigned day);
|
||||
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 mmio_read (uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
SRTC();
|
||||
|
||||
private:
|
||||
static const unsigned months[12];
|
||||
enum RTC_Mode { RTCM_Ready, RTCM_Command, RTCM_Read, RTCM_Write } rtc_mode;
|
||||
signed rtc_index;
|
||||
};
|
||||
|
||||
extern SRTC srtc;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
mingw32-g++ -c test/test.cpp -I. -I../
|
||||
mingw32-g++ -c hiro.cpp -I. -I../
|
||||
mingw32-g++ -c ../nall/string.cpp -I. -I../
|
||||
mingw32-g++ test.o hiro.o string.o -o test_app.exe -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32
|
||||
@pause
|
||||
@del *.o
|
|
@ -0,0 +1,6 @@
|
|||
clear
|
||||
g++ -c test/test.cpp -I. -I../
|
||||
g++ -c hiro.cpp `pkg-config --cflags gtk+-2.0` -I. -I../
|
||||
g++ -c ../nall/string.cpp -I. -I../
|
||||
g++ test.o hiro.o string.o -o test_app `pkg-config --libs gtk+-2.0` -lXtst
|
||||
rm *.o
|
|
@ -0,0 +1,237 @@
|
|||
#include "../hiro.h"
|
||||
using namespace libhiro;
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
using nall::min;
|
||||
using nall::max;
|
||||
|
||||
bool kill_ = false;
|
||||
|
||||
uint32_t windowicon[16 * 16] = {
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
|
||||
};
|
||||
|
||||
class SubWindow : public Window {
|
||||
public:
|
||||
Button button;
|
||||
|
||||
uintptr_t tick(Event) {
|
||||
hide();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
create(0, 595, 80);
|
||||
|
||||
button.create(0, 595, 80, "SubWindow (click to hide)");
|
||||
button.on_tick = bind(&SubWindow::tick, this);
|
||||
attach(button, 0, 0);
|
||||
}
|
||||
} subwindow;
|
||||
|
||||
class MainWindow : public Window {
|
||||
public:
|
||||
MenuGroup menu_file;
|
||||
MenuGroup menu_file_disk;
|
||||
MenuItem menu_file_disk_load;
|
||||
MenuItem menu_file_disk_save;
|
||||
MenuSeparator menu_file_separator;
|
||||
MenuItem menu_file_exit;
|
||||
MenuGroup menu_help;
|
||||
MenuCheckItem menu_help_check1, menu_help_check2;
|
||||
MenuSeparator menu_help_separator1;
|
||||
MenuRadioItem menu_help_radio1, menu_help_radio2, menu_help_radio3;
|
||||
MenuSeparator menu_help_separator2;
|
||||
MenuItem menu_help_about;
|
||||
Label label;
|
||||
Editbox editbox, editbox_multi;
|
||||
Button button_ok;
|
||||
Button button_exit;
|
||||
Checkbox check1, check2;
|
||||
Radiobox radio1, radio2;
|
||||
Progressbar progress;
|
||||
Combobox combobox;
|
||||
Listbox listbox;
|
||||
Slider hslider, vslider;
|
||||
Frame frame;
|
||||
Canvas canvas;
|
||||
|
||||
uintptr_t change(Event e) {
|
||||
printf("change(%d)\n", (uint)e.param);
|
||||
}
|
||||
|
||||
uintptr_t activate(Event e) {
|
||||
printf("activate(%d)\n", (uint)e.param);
|
||||
}
|
||||
|
||||
uintptr_t tick(Event e) {
|
||||
printf("tick(%d)\n", e.param);
|
||||
if(e.widget == &button_ok) {
|
||||
char t[4096];
|
||||
editbox.get_text(t, 4096);
|
||||
printf("'%s'\n", t);
|
||||
}
|
||||
if(e.widget == &menu_file_disk_load) {
|
||||
char t[4096] = "";
|
||||
hiro().file_open(0, t, "", "Source files\t*.cpp,*.h\nAll Files\t*.*");
|
||||
printf("'%s'\n", t);
|
||||
}
|
||||
if(e.widget == &menu_file_disk_save) {
|
||||
char t[4096] = "";
|
||||
hiro().file_save(0, t, "", "Source files\t*.cpp,*.h\nAll Files\t*.*");
|
||||
printf("'%s'\n", t);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t keydown(Event e) {
|
||||
static bool fs = false;
|
||||
if(e.param == nall::keyboard::f11) {
|
||||
fs = !fs;
|
||||
fs ? fullscreen() : unfullscreen();
|
||||
printf("%d -> %4d, %4d\n", fs, get_width(), get_height());
|
||||
} else if(e.param == nall::keyboard::escape) {
|
||||
menu.show(!menu.visible());
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t close(Event) {
|
||||
printf("close()\n");
|
||||
return kill_ = true;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
create(Window::AutoCenter, 605, 320, "hiro test application");
|
||||
//set_opacity(224);
|
||||
//set_background_color(0, 0, 0);
|
||||
set_icon(16, 16, windowicon);
|
||||
|
||||
attach(menu_file.create("File"));
|
||||
menu_file.attach(menu_file_disk.create("Disk"));
|
||||
menu_file_disk.attach(menu_file_disk_load.create("Load ..."));
|
||||
menu_file_disk.attach(menu_file_disk_save.create("Save ..."));
|
||||
menu_file.attach(menu_file_separator.create());
|
||||
menu_file.attach(menu_file_exit.create("Exit"));
|
||||
attach(menu_help.create("Help"));
|
||||
menu_help.attach(menu_help_check1.create("Check 1"));
|
||||
menu_help.attach(menu_help_check2.create("Check 2"));
|
||||
menu_help.attach(menu_help_separator1.create());
|
||||
{ MenuRadioItemGroup group;
|
||||
group.add(&menu_help_radio1);
|
||||
group.add(&menu_help_radio2);
|
||||
group.add(&menu_help_radio3);
|
||||
menu_help.attach(menu_help_radio1.create(group, "Radio 1"));
|
||||
menu_help.attach(menu_help_radio2.create(group, "Radio 2"));
|
||||
menu_help.attach(menu_help_radio3.create(group, "Radio 3"));
|
||||
} menu_help.attach(menu_help_separator2.create());
|
||||
menu_help.attach(menu_help_about.create("About ..."));
|
||||
menu_help_about.disable();
|
||||
label.create(0, 200, 35, "hiro test application\n~ byuu");
|
||||
editbox.create(0, 200, 25);
|
||||
button_ok.create(0, 100, 30, "Ok");
|
||||
button_exit.create(0, 100, 30, "Exit");
|
||||
editbox_multi.create(Editbox::Multiline | Editbox::VerticalScrollAlways, 200, 95);
|
||||
check1.create(0, 100, 20, "Check 1");
|
||||
check2.create(0, 100, 20, "Check 2");
|
||||
{ RadioboxGroup group;
|
||||
group.add(&radio1);
|
||||
group.add(&radio2);
|
||||
radio1.create(group, 0, 100, 20, "Radio 1");
|
||||
radio2.create(group, 0, 100, 20, "Radio 2");
|
||||
} progress.create(0, 200, 30);
|
||||
progress.set_progress(50);
|
||||
combobox.create(0, 200, 30);
|
||||
combobox.add_item("Option 1");
|
||||
combobox.add_item("Option 2");
|
||||
combobox.add_item("Option 3");
|
||||
listbox.create(Listbox::Header | Listbox::VerticalScrollAlways, 200, 100, "Name\tValue");
|
||||
listbox.add_item("a\ttrue");
|
||||
listbox.add_item("b\tfalse");
|
||||
hslider.create(Slider::Horizontal, 425, 25, 10);
|
||||
vslider.create(Slider::Vertical, 25, 200, 10);
|
||||
frame.create(0, 155, 225, "Canvas:");
|
||||
|
||||
canvas.create(0, 135, 195);
|
||||
for(uint y = 0; y < 195; y++) {
|
||||
uint32_t *p = canvas.buffer() + y * 135;
|
||||
for(uint x = 0; x < 135; x++) {
|
||||
double dx = 128.0 / 135.0 * double(x);
|
||||
double dy = 128.0 / 195.0 * double(y);
|
||||
uint32_t c = uint32_t(dx) + uint32_t(dy);
|
||||
*p++ = (max(0U, min(c, 255U)) ^ 0xff) << 16;
|
||||
}
|
||||
}
|
||||
|
||||
status.show();
|
||||
status.set_text("Statusbar");
|
||||
|
||||
on_close = bind(&MainWindow::close, this);
|
||||
on_keydown = bind(&MainWindow::keydown, this);
|
||||
|
||||
menu_file_disk_load.on_tick =
|
||||
menu_file_disk_save.on_tick = bind(&MainWindow::tick, this);
|
||||
menu_file_exit.on_tick = on_close;
|
||||
menu_help_check1.on_tick = menu_help_check2.on_tick =
|
||||
menu_help_radio1.on_tick = menu_help_radio2.on_tick =
|
||||
menu_help_radio3.on_tick = bind(&MainWindow::tick, this);
|
||||
menu_help_about.on_tick = bind(&MainWindow::tick, this);
|
||||
button_ok.on_tick = bind(&MainWindow::tick, this);
|
||||
button_exit.on_tick = bind(&MainWindow::close, this);
|
||||
check1.on_tick = check2.on_tick =
|
||||
radio1.on_tick = radio2.on_tick = bind(&MainWindow::tick, this);
|
||||
combobox.on_change = bind(&MainWindow::change, this);
|
||||
listbox.on_change = bind(&MainWindow::change, this);
|
||||
listbox.on_activate = bind(&MainWindow::activate, this);
|
||||
hslider.on_change = bind(&MainWindow::change, this);
|
||||
vslider.on_change = bind(&MainWindow::change, this);
|
||||
|
||||
attach(label, 5, 5);
|
||||
attach(editbox, 5, 40);
|
||||
attach(button_ok, 5, 70);
|
||||
attach(button_exit, 105, 70);
|
||||
attach(editbox_multi, 210, 5);
|
||||
attach(check1, 5, 105);
|
||||
attach(check2, 105, 105);
|
||||
attach(radio1, 5, 125);
|
||||
attach(radio2, 105, 125);
|
||||
attach(progress, 5, 145);
|
||||
attach(combobox, 5, 175);
|
||||
attach(listbox, 210, 105);
|
||||
attach(hslider, 5, 205);
|
||||
attach(vslider, 415, 5);
|
||||
|
||||
attach(frame, 445, 5);
|
||||
attach(canvas, 455, 25);
|
||||
|
||||
attach(subwindow, 5, 235);
|
||||
}
|
||||
} window;
|
||||
|
||||
int main() {
|
||||
hiro().init();
|
||||
hiro().disable_screensaver();
|
||||
|
||||
subwindow.setup();
|
||||
window.setup();
|
||||
window.show();
|
||||
|
||||
window.check1.check();
|
||||
|
||||
while(kill_ == false) hiro().run();
|
||||
|
||||
hiro().term();
|
||||
return 0;
|
||||
}
|
|
@ -8,6 +8,11 @@ void pCanvas::create(uint style, uint width, uint height) {
|
|||
}
|
||||
|
||||
void pCanvas::redraw() {
|
||||
PAINTSTRUCT ps;
|
||||
BeginPaint(hwnd, &ps);
|
||||
SetDIBitsToDevice(ps.hdc, 0, 0, iwidth, iheight, 0, 0, 0, iheight, (void*)ibuffer, &bmi, DIB_RGB_COLORS);
|
||||
EndPaint(hwnd, &ps);
|
||||
InvalidateRect(hwnd, 0, FALSE);
|
||||
}
|
||||
|
||||
uint32_t* pCanvas::buffer() {
|
||||
|
@ -30,13 +35,6 @@ pCanvas::~pCanvas() {
|
|||
|
||||
/* internal */
|
||||
|
||||
void pCanvas::blit() {
|
||||
PAINTSTRUCT ps;
|
||||
BeginPaint(hwnd, &ps);
|
||||
SetDIBitsToDevice(ps.hdc, 0, 0, iwidth, iheight, 0, 0, 0, iheight, (void*)ibuffer, &bmi, DIB_RGB_COLORS);
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
|
||||
void pCanvas::resize(uint width, uint height) {
|
||||
if(ibuffer) free(ibuffer);
|
||||
|
||||
|
|
|
@ -12,6 +12,5 @@ public:
|
|||
BITMAPINFO bmi;
|
||||
uint32_t *ibuffer;
|
||||
uint ipitch, iwidth, iheight;
|
||||
void blit();
|
||||
void resize(uint width, uint height);
|
||||
};
|
||||
|
|
|
@ -312,7 +312,7 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||
} break;
|
||||
|
||||
case WM_PAINT: {
|
||||
if(p && p->self.type == Widget::CanvasType) ((pCanvas*)p)->blit();
|
||||
if(p && p->self.type == Widget::CanvasType) ((pCanvas*)p)->redraw();
|
||||
} break;
|
||||
|
||||
case WM_COMMAND: {
|
||||
|
|
|
@ -30,6 +30,13 @@ uint16_t pHiro::translate_key(uint key) {
|
|||
case '8': return keyboard::num_8;
|
||||
case '9': return keyboard::num_9;
|
||||
|
||||
case VK_INSERT: return keyboard::insert;
|
||||
case VK_DELETE: return keyboard::delete_;
|
||||
case VK_HOME: return keyboard::home;
|
||||
case VK_END: return keyboard::end;
|
||||
case VK_PRIOR: return keyboard::page_up;
|
||||
case VK_NEXT: return keyboard::page_down;
|
||||
|
||||
case 'A': return keyboard::a;
|
||||
case 'B': return keyboard::b;
|
||||
case 'C': return keyboard::c;
|
||||
|
|
|
@ -13,15 +13,16 @@ public:
|
|||
struct {
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_format_t format;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_uframes_t period_size;
|
||||
int channels;
|
||||
const char *name;
|
||||
unsigned latency;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
uint16_t *data;
|
||||
uint32_t *data;
|
||||
unsigned length;
|
||||
unsigned size;
|
||||
} buffer;
|
||||
|
||||
struct {
|
||||
|
@ -53,30 +54,27 @@ public:
|
|||
void sample(uint16_t left, uint16_t right) {
|
||||
if(!device.handle) return;
|
||||
|
||||
buffer.data[buffer.length++] = left;
|
||||
buffer.data[buffer.length++] = right;
|
||||
if(buffer.length + 2 < buffer.size) return; //will crash in some cases if not stopped two before
|
||||
|
||||
snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer.data, buffer.length);
|
||||
if(written < 0) {
|
||||
snd_pcm_recover(device.handle, written, 1);
|
||||
//no samples written, drop one sample to prevent possible emulation stall
|
||||
buffer.length -= 2;
|
||||
memmove(buffer.data, buffer.data + 2, buffer.length * sizeof(uint16_t));
|
||||
} else if(written < buffer.length) {
|
||||
//only some samples written
|
||||
buffer.length -= written;
|
||||
memmove(buffer.data, buffer.data + written, buffer.length * sizeof(uint16_t));
|
||||
} else {
|
||||
//all samples written
|
||||
buffer.length = 0;
|
||||
}
|
||||
buffer.data[buffer.length++] = left + (right << 16);
|
||||
if(buffer.length < device.period_size) return;
|
||||
uint32_t *buffer_ptr = buffer.data;
|
||||
do {
|
||||
snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length);
|
||||
if(written < 0) {
|
||||
//no samples written
|
||||
snd_pcm_recover(device.handle, written, 1);
|
||||
} else if(written < buffer.length) {
|
||||
//only some samples written
|
||||
buffer.length -= written;
|
||||
buffer_ptr += written;
|
||||
} else {
|
||||
//all samples written
|
||||
buffer.length = 0;
|
||||
}
|
||||
} while(buffer.length > 0);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
buffer.data = new uint16_t[buffer.size];
|
||||
|
||||
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) {
|
||||
if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
|
||||
//failed to initialize
|
||||
term();
|
||||
return false;
|
||||
|
@ -89,6 +87,11 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) {
|
||||
device.period_size = device.latency * 1e-6 * settings.frequency / 4;
|
||||
}
|
||||
|
||||
buffer.data = new uint32_t[device.period_size];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -110,11 +113,10 @@ public:
|
|||
device.format = SND_PCM_FORMAT_S16_LE;
|
||||
device.channels = 2;
|
||||
device.name = "default";
|
||||
device.latency = 90;
|
||||
device.latency = 100000;
|
||||
|
||||
buffer.data = 0;
|
||||
buffer.length = 0;
|
||||
buffer.size = device.latency * 32;
|
||||
|
||||
settings.frequency = 22050;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
audio.alsa (2008-05-04)
|
||||
author: Nach
|
||||
audio.alsa (2008-06-01)
|
||||
authors: Nach, RedDwarf
|
||||
*/
|
||||
|
||||
class pAudioALSA;
|
||||
|
|
|
@ -47,16 +47,23 @@ public:
|
|||
|
||||
bool init() {
|
||||
driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver)
|
||||
if(driver_id < 0) driver_id = ao_default_driver_id(); //fallback on default if driver doesn't exist
|
||||
if(driver_id < 0) return false;
|
||||
|
||||
driver_format.bits = 16;
|
||||
driver_format.channels = 2;
|
||||
driver_format.rate = settings.frequency;
|
||||
driver_format.byte_format = AO_FMT_LITTLE;
|
||||
|
||||
audio_device = ao_open_live(driver_id, &driver_format, 0);
|
||||
ao_option *options = 0;
|
||||
ao_info *di = ao_driver_info(driver_id);
|
||||
if(!di) return false;
|
||||
if(!strcmp(di->short_name, "alsa")) {
|
||||
ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms)
|
||||
}
|
||||
|
||||
audio_device = ao_open_live(driver_id, &driver_format, options);
|
||||
if(!audio_device) return false;
|
||||
|
||||
ao_info *di = ao_driver_info(driver_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
audio.ao (2007-12-26)
|
||||
author: Nach
|
||||
audio.ao (2008-06-01)
|
||||
authors: Nach, RedDwarf
|
||||
*/
|
||||
|
||||
class pAudioAO;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
rm -r nall
|
||||
rm -r hiro
|
||||
rm -r ruby
|
||||
cp -r ../../../nall ./nall
|
||||
cp -r ../../../hiro ./hiro
|
||||
cp -r ../../../ruby ./ruby
|
|
@ -32,9 +32,18 @@ void sBus::map_generic() {
|
|||
map_generic_sram();
|
||||
} break;
|
||||
|
||||
case Cartridge::SPC7110ROM: {
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110);
|
||||
map(MapDirect, 0x00, 0x0f, 0x8000, 0xffff, spc7110);
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110);
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110);
|
||||
map(MapDirect, 0x80, 0x8f, 0x8000, 0xffff, spc7110);
|
||||
map(MapDirect, 0xc0, 0xff, 0x0000, 0xffff, spc7110);
|
||||
} break;
|
||||
|
||||
case Cartridge::BSXROM: {
|
||||
//full map is dynamically mapped by:
|
||||
//src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map();
|
||||
//full map is dynamically mapped by:
|
||||
//src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map();
|
||||
map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom);
|
||||
map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom);
|
||||
} break;
|
||||
|
|
|
@ -7,6 +7,7 @@ BSXCart bsxcart;
|
|||
BSXFlash bsxflash;
|
||||
SRTC srtc;
|
||||
SDD1 sdd1;
|
||||
SPC7110 spc7110;
|
||||
Cx4 cx4;
|
||||
DSP1 dsp1;
|
||||
DSP2 dsp2;
|
||||
|
@ -34,6 +35,7 @@ void SNES::init() {
|
|||
bsxflash.init();
|
||||
srtc.init();
|
||||
sdd1.init();
|
||||
spc7110.init();
|
||||
cx4.init();
|
||||
dsp1.init();
|
||||
dsp2.init();
|
||||
|
@ -67,6 +69,7 @@ void SNES::power() {
|
|||
if(cartridge.info.bsxflash) bsxflash.power();
|
||||
if(cartridge.info.srtc) srtc.power();
|
||||
if(cartridge.info.sdd1) sdd1.power();
|
||||
if(cartridge.info.spc7110) spc7110.power();
|
||||
if(cartridge.info.cx4) cx4.power();
|
||||
if(cartridge.info.dsp1) dsp1.power();
|
||||
if(cartridge.info.dsp2) dsp2.power();
|
||||
|
@ -87,6 +90,7 @@ void SNES::power() {
|
|||
if(cartridge.info.bsxflash) bsxflash.enable();
|
||||
if(cartridge.info.srtc) srtc.enable();
|
||||
if(cartridge.info.sdd1) sdd1.enable();
|
||||
if(cartridge.info.spc7110) spc7110.enable();
|
||||
if(cartridge.info.cx4) cx4.enable();
|
||||
if(cartridge.info.dsp1) dsp1.enable();
|
||||
if(cartridge.info.dsp2) dsp2.enable();
|
||||
|
@ -112,6 +116,7 @@ void SNES::reset() {
|
|||
if(cartridge.info.bsxflash) bsxflash.reset();
|
||||
if(cartridge.info.srtc) srtc.reset();
|
||||
if(cartridge.info.sdd1) sdd1.reset();
|
||||
if(cartridge.info.spc7110) spc7110.reset();
|
||||
if(cartridge.info.cx4) cx4.reset();
|
||||
if(cartridge.info.dsp1) dsp1.reset();
|
||||
if(cartridge.info.dsp2) dsp2.reset();
|
||||
|
|
|
@ -4,7 +4,7 @@ uintptr_t AboutWindow::close(Event) {
|
|||
}
|
||||
|
||||
void AboutWindow::setup() {
|
||||
create(Window::AutoCenter, 320, 130, translate["About bsnes ..."]);
|
||||
create(Window::AutoCenter, 360, 135, translate["About bsnes ..."]);
|
||||
set_icon(48, 48, (uint32_t*)resource::icon48);
|
||||
|
||||
icon.create(0, 48, 48);
|
||||
|
@ -13,10 +13,10 @@ void AboutWindow::setup() {
|
|||
<< translate["Author"] << ": byuu\n"
|
||||
<< translate["Project began: October 14th, 2004"]
|
||||
);
|
||||
contributors.create(0, 275, 70, string()
|
||||
contributors.create(0, 350, 75, string()
|
||||
<< translate["Contributors:"] << "\n"
|
||||
<< " anomie, blargg, DMV27, GIGO, kode54, Nach,\n"
|
||||
<< " Overload, Richard Bannister, TRAC, zones\n"
|
||||
<< " Andreas Naive, anomie, blargg, DMV27, GIGO, kode54,\n"
|
||||
<< " neviksti, Nach, Overload, Richard Bannister, TRAC, zones\n"
|
||||
<< "\n"
|
||||
<< translate["Localization by: byuu"]
|
||||
);
|
||||
|
|
|
@ -318,7 +318,6 @@ void load_cart_normal(const char *filename) {
|
|||
const char *name;
|
||||
if(cartridge.info.superfx) { name = "SuperFX"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.sa1) { name = "SA-1"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.spc7110) { name = "SPC7110"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.st011) { name = "ST011"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.st018) { name = "ST018"; replace(message, "$", name); alert(message); }
|
||||
|
||||
|
|
Loading…
Reference in New Issue