mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-07 17:01:59 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9133129209 | ||
|
|
7d83cde40a |
No files matched your search
+2
-1
@@ -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
|
||||
|
||||
+11
-18
@@ -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
|
||||
|
||||
+12
-11
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
#define BSNES_VERSION "0.032a"
|
||||
#define BSNES_VERSION "0.033"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
|
||||
+20
-17
@@ -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; }
|
||||
|
||||
+7
-4
@@ -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;
|
||||
+213
-189
@@ -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() {
|
||||
}
|
||||
+22
-52
@@ -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;
|
||||
|
||||
+27
-25
@@ -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); }
|
||||
|
||||
|
||||
Reference in new issue
Block a user