mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-08 01:08:24 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd83559786 | ||
|
|
100ef3a271 | ||
|
|
bccc5b5a12 | ||
|
|
acee547da9 | ||
|
|
b1b146fd7d | ||
|
|
53e913e225 | ||
|
|
0cf16ce784 | ||
|
|
ce38d577ef | ||
|
|
0a87b99370 | ||
|
|
82d5761705 | ||
|
|
9133129209 | ||
|
|
7d83cde40a |
No files matched your search
+4
-6
@@ -53,21 +53,19 @@ information in the header. The lack of such a header indicates said file falls
|
||||
under the bsnes license.
|
||||
|
||||
HQ2x filter, author: MaxST, license: LGPL
|
||||
JMA decompressor, author: NSRT Team, license: GPL (*)
|
||||
JMA decompressor, author: NSRT Team, license: GPL*
|
||||
NTSC filter, author: blargg, license: LGPL
|
||||
zlib decompressor, license: zlib license
|
||||
|
||||
(*) bsnes has received an exemption from the copyright holder to use this work.
|
||||
(* bsnes has received an exemption from the copyright holder to use this work.)
|
||||
|
||||
The software also includes works which have been released to the public domain,
|
||||
which are not bound to any licensing agreements. Below is a complete list of all
|
||||
such software.
|
||||
|
||||
libco, author: byuu
|
||||
libui, author: byuu
|
||||
OBC-1 emu, author: byuu
|
||||
S-DD1 emu, author: Andreas Naive
|
||||
S-RTC emu, author: byuu
|
||||
S-DD1 decompressor, author: Andreas Naive
|
||||
SPC7110 decompressor, author: neviksti
|
||||
|
||||
Any software listed above as exemptions may be relicensed individually from
|
||||
bsnes under their respective terms. However, no bsnes licensed portions can be
|
||||
|
||||
+25
-19
@@ -1,10 +1,11 @@
|
||||
bsnes
|
||||
Version: 0.032a
|
||||
Version: 0.034
|
||||
Author: byuu
|
||||
|
||||
--------
|
||||
========
|
||||
General:
|
||||
--------
|
||||
========
|
||||
|
||||
bsnes is a Super Nintendo / Super Famicom emulator that began on
|
||||
October 14th, 2004.
|
||||
|
||||
@@ -13,9 +14,10 @@ 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,11 +38,13 @@ 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
|
||||
- "Glitch" when reading joypad registers during auto polling not implemented
|
||||
|
||||
S-PPU
|
||||
- Uses scanline-based renderer. This is very inaccurate, but few (if any)
|
||||
@@ -56,9 +60,10 @@ 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 +80,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,10 +89,18 @@ 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
|
||||
Multitap (4-port and 5-port)
|
||||
|
||||
=============
|
||||
Contributors:
|
||||
=============
|
||||
|
||||
Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom,
|
||||
mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister, tetsuo55, TRAC,
|
||||
zones
|
||||
+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
-14
@@ -1,4 +1,4 @@
|
||||
#define BSNES_VERSION "0.032a"
|
||||
#define BSNES_VERSION "0.034"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
@@ -39,18 +39,5 @@ using namespace nall;
|
||||
//platform-specific global functions
|
||||
void alert(const char*, ...);
|
||||
void dprintf(const char*, ...);
|
||||
void dprintf(uint, const char*, ...);
|
||||
|
||||
namespace source {
|
||||
enum {
|
||||
none = 0,
|
||||
debug,
|
||||
cpu,
|
||||
ppu,
|
||||
smp,
|
||||
dsp,
|
||||
bus,
|
||||
};
|
||||
};
|
||||
|
||||
#include "interface.h"
|
||||
+21
-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,37 +27,39 @@ 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;
|
||||
|
||||
info.type = cart_type;
|
||||
info.patched = false;
|
||||
|
||||
info.bsxbase = false;
|
||||
info.bsxcart = false;
|
||||
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 +75,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 +113,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; }
|
||||
|
||||
+8
-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 {
|
||||
@@ -70,6 +71,7 @@ public:
|
||||
uint32 crc32;
|
||||
char filename[PATH_MAX * 4];
|
||||
char name[128];
|
||||
bool patched;
|
||||
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
@@ -85,6 +87,7 @@ public:
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
@@ -142,12 +145,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;
|
||||
|
||||
@@ -87,8 +87,6 @@ char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
|
||||
}
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) {
|
||||
dprintf("* Loading \"%s\" ...", fn);
|
||||
|
||||
if(fexists(fn) == false) return false;
|
||||
|
||||
Reader::Type filetype = Reader::Normal;
|
||||
|
||||
@@ -5,6 +5,7 @@ void Cartridge::read_header() {
|
||||
uint index = info.header_index;
|
||||
uint8 mapper = rom[index + MAPPER];
|
||||
uint8 rom_type = rom[index + ROM_TYPE];
|
||||
uint8 rom_size = rom[index + ROM_SIZE];
|
||||
uint8 company = rom[index + COMPANY];
|
||||
uint8 region = rom[index + REGION] & 0x7f;
|
||||
|
||||
@@ -52,8 +53,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) {
|
||||
@@ -98,13 +100,14 @@ void Cartridge::read_header() {
|
||||
info.obc1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6) {
|
||||
//TODO: both ST010 and ST011 share the same mapper + rom_type.
|
||||
//need way to determine which is which.
|
||||
//for now, default to supported ST010.
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
info.st010 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
info.st011 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf5) {
|
||||
info.st018 = true;
|
||||
}
|
||||
@@ -179,7 +182,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) {
|
||||
|
||||
@@ -23,6 +23,7 @@ void Cartridge::load_cart_normal(const char *filename) {
|
||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
info.patched = true;
|
||||
}
|
||||
|
||||
info.crc32 = crc32_calculate(cart.rom, cart.rom_size);
|
||||
@@ -40,6 +41,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 +58,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
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
@make platform=win compiler=mingw32-gcc
|
||||
::@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
::@make platform=win compiler=mingw32-gcc
|
||||
@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
@pause
|
||||
@@ -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,511 @@
|
||||
#ifdef SPC7110_CPP
|
||||
|
||||
uint8 SPC7110Decomp::read() {
|
||||
if(decomp_buffer_length == 0) {
|
||||
//decompress at least (decomp_buffer_size / 2) bytes to the buffer
|
||||
switch(decomp_mode) {
|
||||
case 0: mode0(false); break;
|
||||
case 1: mode1(false); break;
|
||||
case 2: mode2(false); break;
|
||||
default: return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 data = decomp_buffer[decomp_buffer_rdoffset++];
|
||||
decomp_buffer_rdoffset &= decomp_buffer_size - 1;
|
||||
decomp_buffer_length--;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SPC7110Decomp::write(uint8 data) {
|
||||
decomp_buffer[decomp_buffer_wroffset++] = data;
|
||||
decomp_buffer_wroffset &= decomp_buffer_size - 1;
|
||||
decomp_buffer_length++;
|
||||
}
|
||||
|
||||
uint8 SPC7110Decomp::dataread() {
|
||||
unsigned size = memory::cartrom.size() - 0x100000;
|
||||
while(decomp_offset >= size) decomp_offset -= size;
|
||||
return memory::cartrom.read(0x100000 + decomp_offset++);
|
||||
}
|
||||
|
||||
void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) {
|
||||
decomp_mode = mode;
|
||||
decomp_offset = offset;
|
||||
|
||||
decomp_buffer_rdoffset = 0;
|
||||
decomp_buffer_wroffset = 0;
|
||||
decomp_buffer_length = 0;
|
||||
|
||||
//reset context states
|
||||
for(unsigned i = 0; i < 32; i++) {
|
||||
context[i].index = 0;
|
||||
context[i].invert = 0;
|
||||
}
|
||||
|
||||
switch(decomp_mode) {
|
||||
case 0: mode0(true); break;
|
||||
case 1: mode1(true); break;
|
||||
case 2: mode2(true); break;
|
||||
}
|
||||
|
||||
//decompress up to requested output data index
|
||||
while(index--) read();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void SPC7110Decomp::mode0(bool init) {
|
||||
static uint8 val, in, span;
|
||||
static int out, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
out = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned bit = 0; bit < 8; bit++) {
|
||||
//get context
|
||||
uint8 mask = (1 << (bit & 3)) - 1;
|
||||
uint8 con = mask + ((inverts & mask) ^ (lps & mask));
|
||||
if(bit > 3) con += 15;
|
||||
|
||||
//get prob and mps
|
||||
unsigned prob = probability(con);
|
||||
unsigned mps = (((out >> 15) & 1) ^ context[con].invert);
|
||||
|
||||
//get bit
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
out = (out << 1) + mps;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
out = (out << 1) + 1 - mps;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
inverts = (inverts << 1) + context[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
}
|
||||
|
||||
//save byte
|
||||
write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Decomp::mode1(bool init) {
|
||||
static int pixelorder[4], realorder[4];
|
||||
static uint8 in, val, span;
|
||||
static int out, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
for(unsigned i = 0; i < 4; i++) pixelorder[i] = i;
|
||||
out = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned pixel = 0; pixel < 8; pixel++) {
|
||||
//get first symbol context
|
||||
unsigned a = ((out >> (1 * 2)) & 3);
|
||||
unsigned b = ((out >> (7 * 2)) & 3);
|
||||
unsigned c = ((out >> (8 * 2)) & 3);
|
||||
unsigned con = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c);
|
||||
|
||||
//update pixel order
|
||||
unsigned m, n;
|
||||
for(m = 0; m < 4; m++) if(pixelorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1];
|
||||
pixelorder[0] = a;
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m = 0; m < 4; m++) realorder[m] = pixelorder[m];
|
||||
|
||||
//rotate reference pixel c value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == c) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = c;
|
||||
|
||||
//rotate reference pixel b value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == b) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = b;
|
||||
|
||||
//rotate reference pixel a value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = a;
|
||||
|
||||
//get 2 symbols
|
||||
for(unsigned bit = 0; bit < 2; bit++) {
|
||||
//get prob
|
||||
unsigned prob = probability(con);
|
||||
|
||||
//get symbol
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
inverts = (inverts << 1) + context[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
|
||||
//get next context
|
||||
con = 5 + (con << 1) + ((lps ^ inverts) & 1);
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b = realorder[(lps ^ inverts) & 3];
|
||||
out = (out << 2) + b;
|
||||
}
|
||||
|
||||
//turn pixel data into bitplanes
|
||||
unsigned data = morton_2x8(out);
|
||||
write(data >> 8);
|
||||
write(data >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Decomp::mode2(bool init) {
|
||||
static int pixelorder[16], realorder[16];
|
||||
static uint8 bitplanebuffer[16], buffer_index;
|
||||
static uint8 in, val, span;
|
||||
static int out0, out1, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
for(unsigned i = 0; i < 16; i++) pixelorder[i] = i;
|
||||
buffer_index = 0;
|
||||
out0 = out1 = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned pixel = 0; pixel < 8; pixel++) {
|
||||
//get first symbol context
|
||||
unsigned a = ((out0 >> (0 * 4)) & 15);
|
||||
unsigned b = ((out0 >> (7 * 4)) & 15);
|
||||
unsigned c = ((out1 >> (0 * 4)) & 15);
|
||||
unsigned con = 0;
|
||||
unsigned refcon = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c);
|
||||
|
||||
//update pixel order
|
||||
unsigned m, n;
|
||||
for(m = 0; m < 16; m++) if(pixelorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1];
|
||||
pixelorder[0] = a;
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m = 0; m < 16; m++) realorder[m] = pixelorder[m];
|
||||
|
||||
//rotate reference pixel c value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == c) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = c;
|
||||
|
||||
//rotate reference pixel b value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == b) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = b;
|
||||
|
||||
//rotate reference pixel a value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = a;
|
||||
|
||||
//get 4 symbols
|
||||
for(unsigned bit = 0; bit < 4; bit++) {
|
||||
//get prob
|
||||
unsigned prob = probability(con);
|
||||
|
||||
//get symbol
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
unsigned invertbit = context[con].invert;
|
||||
inverts = (inverts << 1) + invertbit;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
|
||||
//get next context
|
||||
con = mode2_context_table[con][flag_lps ^ invertbit] + (con == 1 ? refcon : 0);
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b = realorder[(lps ^ inverts) & 0x0f];
|
||||
out1 = (out1 << 4) + ((out0 >> 28) & 0x0f);
|
||||
out0 = (out0 << 4) + b;
|
||||
}
|
||||
|
||||
//convert pixel data into bitplanes
|
||||
unsigned data = morton_4x8(out0);
|
||||
write(data >> 24);
|
||||
write(data >> 16);
|
||||
bitplanebuffer[buffer_index++] = data >> 8;
|
||||
bitplanebuffer[buffer_index++] = data >> 0;
|
||||
|
||||
if(buffer_index == 16) {
|
||||
for(unsigned i = 0; i < 16; i++) write(bitplanebuffer[i]);
|
||||
buffer_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const uint8 SPC7110Decomp::evolution_table[53][4] = {
|
||||
//{ prob, nextlps, nextmps, toggle invert },
|
||||
|
||||
{ 0x5a, 1, 1, 1 },
|
||||
{ 0x25, 6, 2, 0 },
|
||||
{ 0x11, 8, 3, 0 },
|
||||
{ 0x08, 10, 4, 0 },
|
||||
{ 0x03, 12, 5, 0 },
|
||||
{ 0x01, 15, 5, 0 },
|
||||
|
||||
{ 0x5a, 7, 7, 1 },
|
||||
{ 0x3f, 19, 8, 0 },
|
||||
{ 0x2c, 21, 9, 0 },
|
||||
{ 0x20, 22, 10, 0 },
|
||||
{ 0x17, 23, 11, 0 },
|
||||
{ 0x11, 25, 12, 0 },
|
||||
{ 0x0c, 26, 13, 0 },
|
||||
{ 0x09, 28, 14, 0 },
|
||||
{ 0x07, 29, 15, 0 },
|
||||
{ 0x05, 31, 16, 0 },
|
||||
{ 0x04, 32, 17, 0 },
|
||||
{ 0x03, 34, 18, 0 },
|
||||
{ 0x02, 35, 5, 0 },
|
||||
|
||||
{ 0x5a, 20, 20, 1 },
|
||||
{ 0x48, 39, 21, 0 },
|
||||
{ 0x3a, 40, 22, 0 },
|
||||
{ 0x2e, 42, 23, 0 },
|
||||
{ 0x26, 44, 24, 0 },
|
||||
{ 0x1f, 45, 25, 0 },
|
||||
{ 0x19, 46, 26, 0 },
|
||||
{ 0x15, 25, 27, 0 },
|
||||
{ 0x11, 26, 28, 0 },
|
||||
{ 0x0e, 26, 29, 0 },
|
||||
{ 0x0b, 27, 30, 0 },
|
||||
{ 0x09, 28, 31, 0 },
|
||||
{ 0x08, 29, 32, 0 },
|
||||
{ 0x07, 30, 33, 0 },
|
||||
{ 0x05, 31, 34, 0 },
|
||||
{ 0x04, 33, 35, 0 },
|
||||
{ 0x04, 33, 36, 0 },
|
||||
{ 0x03, 34, 37, 0 },
|
||||
{ 0x02, 35, 38, 0 },
|
||||
{ 0x02, 36, 5, 0 },
|
||||
|
||||
{ 0x58, 39, 40, 1 },
|
||||
{ 0x4d, 47, 41, 0 },
|
||||
{ 0x43, 48, 42, 0 },
|
||||
{ 0x3b, 49, 43, 0 },
|
||||
{ 0x34, 50, 44, 0 },
|
||||
{ 0x2e, 51, 45, 0 },
|
||||
{ 0x29, 44, 46, 0 },
|
||||
{ 0x25, 45, 24, 0 },
|
||||
|
||||
{ 0x56, 47, 48, 1 },
|
||||
{ 0x4f, 47, 49, 0 },
|
||||
{ 0x47, 48, 50, 0 },
|
||||
{ 0x41, 49, 51, 0 },
|
||||
{ 0x3c, 50, 52, 0 },
|
||||
{ 0x37, 51, 43, 0 },
|
||||
};
|
||||
|
||||
const uint8 SPC7110Decomp::mode2_context_table[32][2] = {
|
||||
//{ next 0, next 1 },
|
||||
|
||||
{ 1, 2 },
|
||||
|
||||
{ 3, 8 },
|
||||
{ 13, 14 },
|
||||
|
||||
{ 15, 16 },
|
||||
{ 17, 18 },
|
||||
{ 19, 20 },
|
||||
{ 21, 22 },
|
||||
{ 23, 24 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 27, 28 },
|
||||
{ 29, 30 },
|
||||
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
|
||||
{ 31, 31 },
|
||||
};
|
||||
|
||||
uint8 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; }
|
||||
uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; }
|
||||
uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; }
|
||||
bool SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; }
|
||||
|
||||
unsigned SPC7110Decomp::morton_2x8(unsigned data) {
|
||||
//reverse morton lookup: de-interleave two 8-bit values
|
||||
//15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8
|
||||
//14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0
|
||||
return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255];
|
||||
}
|
||||
|
||||
unsigned SPC7110Decomp::morton_4x8(unsigned data) {
|
||||
//reverse morton lookup: de-interleave four 8-bit values
|
||||
//31, 27, 23, 19, 15, 11, 7, 3 -> 31-24
|
||||
//30, 26, 22, 18, 14, 10, 6, 2 -> 23-16
|
||||
//29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8
|
||||
//28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0
|
||||
return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255]
|
||||
+ morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void SPC7110Decomp::reset() {
|
||||
//mode 3 is invalid; this is treated as a special case to always return 0x00
|
||||
//set to mode 3 so that reading decomp port before starting first decomp will return 0x00
|
||||
decomp_mode = 3;
|
||||
|
||||
decomp_buffer_rdoffset = 0;
|
||||
decomp_buffer_wroffset = 0;
|
||||
decomp_buffer_length = 0;
|
||||
}
|
||||
|
||||
SPC7110Decomp::SPC7110Decomp() {
|
||||
decomp_buffer = new uint8_t[decomp_buffer_size];
|
||||
reset();
|
||||
|
||||
//initialize reverse morton lookup tables
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
#define map(x, y) (((i >> x) & 1) << y)
|
||||
//2x8-bit
|
||||
morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6)
|
||||
+ map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4);
|
||||
morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2)
|
||||
+ map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0);
|
||||
//4x8-bit
|
||||
morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7)
|
||||
+ map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6);
|
||||
morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5)
|
||||
+ map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4);
|
||||
morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3)
|
||||
+ map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2);
|
||||
morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1)
|
||||
+ map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0);
|
||||
#undef map
|
||||
}
|
||||
}
|
||||
|
||||
SPC7110Decomp::~SPC7110Decomp() {
|
||||
delete[] decomp_buffer;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
class SPC7110Decomp {
|
||||
public:
|
||||
uint8 read();
|
||||
void init(unsigned mode, unsigned offset, unsigned index);
|
||||
void reset();
|
||||
|
||||
SPC7110Decomp();
|
||||
~SPC7110Decomp();
|
||||
|
||||
private:
|
||||
unsigned decomp_mode;
|
||||
unsigned decomp_offset;
|
||||
|
||||
//read() will spool chunks half the size of decomp_buffer_size
|
||||
enum { decomp_buffer_size = 64 }; //must be >= 64, and must be a power of two
|
||||
uint8 *decomp_buffer;
|
||||
unsigned decomp_buffer_rdoffset;
|
||||
unsigned decomp_buffer_wroffset;
|
||||
unsigned decomp_buffer_length;
|
||||
|
||||
void write(uint8 data);
|
||||
uint8 dataread();
|
||||
|
||||
void mode0(bool init);
|
||||
void mode1(bool init);
|
||||
void mode2(bool init);
|
||||
|
||||
static const uint8 evolution_table[53][4];
|
||||
static const uint8 mode2_context_table[32][2];
|
||||
|
||||
struct ContextState {
|
||||
uint8 index;
|
||||
uint8 invert;
|
||||
} context[32];
|
||||
|
||||
uint8 probability(unsigned n);
|
||||
uint8 next_lps(unsigned n);
|
||||
uint8 next_mps(unsigned n);
|
||||
bool toggle_invert(unsigned n);
|
||||
|
||||
unsigned morton16[2][256];
|
||||
unsigned morton32[4][256];
|
||||
unsigned morton_2x8(unsigned data);
|
||||
unsigned morton_4x8(unsigned data);
|
||||
};
|
||||
@@ -0,0 +1,658 @@
|
||||
#include "../../base.h"
|
||||
#define SPC7110_CPP
|
||||
|
||||
#include "decomp.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;
|
||||
|
||||
decomp.reset();
|
||||
|
||||
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 decomp.read();
|
||||
}
|
||||
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 != 0x07) 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 != 0x07) 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);
|
||||
|
||||
decomp.init(mode, 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;
|
||||
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;
|
||||
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 != 0x07) 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) {
|
||||
//$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom
|
||||
|
||||
if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
|
||||
//$[00|30]:[6000-7fff]
|
||||
return memory::cartram.read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0xff0000) == 0x500000) {
|
||||
//$[50]:[0000-ffff]
|
||||
return mmio_read(0x4800);
|
||||
}
|
||||
|
||||
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,133 @@
|
||||
/*****
|
||||
* SPC7110 emulator - version 0.03 (2008-08-10)
|
||||
* 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 "decomp.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);
|
||||
|
||||
//spc7110decomp
|
||||
void decomp_init();
|
||||
uint8 decomp_read();
|
||||
|
||||
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
|
||||
|
||||
SPC7110Decomp decomp;
|
||||
|
||||
//==============
|
||||
//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;
|
||||
+7
-10
@@ -58,15 +58,15 @@ string_setting Path::cheat(config(), "path.cheat",
|
||||
string_setting Path::bsx(config(), "path.bsx", "", "");
|
||||
string_setting Path::st(config(), "path.st", "", "");
|
||||
|
||||
integral_setting SNES::controller_port0(config(), "snes.controller_port_1",
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad1);
|
||||
integral_setting SNES::controller_port1(config(), "snes.controller_port_2",
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad2);
|
||||
integral_setting SNES::controller_port1(config(), "snes.controller_port1",
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||
integral_setting SNES::controller_port2(config(), "snes.controller_port2",
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||
|
||||
integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate",
|
||||
"NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272);
|
||||
integral_setting CPU::pal_clock_rate(config(), "cpu.pal_clock_rate",
|
||||
"PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370);
|
||||
"PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370);
|
||||
integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value",
|
||||
"Value to initialize 128k WRAM to upon power cycle.\n"
|
||||
"Note that on real hardware, this value is undefined; meaning it can vary\n"
|
||||
@@ -81,13 +81,10 @@ integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value",
|
||||
"which do not properly initialize WRAM upon power cycle.\n",
|
||||
integral_setting::hex, 0x55);
|
||||
|
||||
integral_setting CPU::hdma_enable("cpu.hdma_enable",
|
||||
"Enable HDMA effects", integral_setting::boolean, true);
|
||||
|
||||
integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate",
|
||||
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 24606720);
|
||||
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
||||
integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate",
|
||||
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 24606720);
|
||||
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
||||
|
||||
integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position",
|
||||
"Approximate HCLOCK position to render at for scanline-based renderers",
|
||||
|
||||
+1
-2
@@ -15,14 +15,13 @@ extern struct Path {
|
||||
} path;
|
||||
|
||||
extern struct SNES {
|
||||
static integral_setting controller_port0;
|
||||
static integral_setting controller_port1;
|
||||
static integral_setting controller_port2;
|
||||
} snes;
|
||||
|
||||
extern struct CPU {
|
||||
static integral_setting ntsc_clock_rate, pal_clock_rate;
|
||||
static integral_setting wram_init_value;
|
||||
static integral_setting hdma_enable;
|
||||
} cpu;
|
||||
|
||||
extern struct SMP {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
#include "dcpu.cpp"
|
||||
|
||||
CPU::CPU() {
|
||||
cpu_version = 1;
|
||||
cpu_version = 2;
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ public:
|
||||
virtual uint16 hcounter() = 0;
|
||||
virtual uint16 hdot() = 0;
|
||||
|
||||
virtual uint8 pio_status() = 0;
|
||||
virtual uint8 pio() = 0;
|
||||
virtual bool joylatch() = 0;
|
||||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
|
||||
+70
-55
@@ -5,52 +5,51 @@ void sCPU::dma_add_clocks(uint clocks) {
|
||||
add_clocks(clocks);
|
||||
}
|
||||
|
||||
/*****
|
||||
* used by both DMA and HDMA
|
||||
*
|
||||
* DMA address bus A cannot read from or write to the following addresses :
|
||||
* $[00-3f|80-bf]:43[00-7f] <DMA control registers>
|
||||
* $[00-3f|80-bf]:420b <DMA enable register>
|
||||
* $[00-3f|80-bf]:420c <HDMA enable register>
|
||||
*
|
||||
* WRAM<>WRAM transfers via $2180 are also illegal
|
||||
*****/
|
||||
bool sCPU::dma_addr_valid(uint32 abus) {
|
||||
//reads from B-bus or S-CPU registers are invalid
|
||||
if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff]
|
||||
if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff]
|
||||
if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f]
|
||||
if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f]
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 sCPU::dma_read(uint32 abus) {
|
||||
if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR
|
||||
return bus.read(abus);
|
||||
}
|
||||
|
||||
void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
|
||||
if(direction == 0) {
|
||||
//a->b transfer (to $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//illegal WRAM->WRAM transfer (bus conflict)
|
||||
//read most likely occurs; no write occurs
|
||||
//read is irrelevant, as it has no observable effect on emulation
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(0x2100 | bbus, regs.mdr); //TODO: verify if MDR is written here
|
||||
//read is irrelevent, as it cannot be observed by software
|
||||
dma_add_clocks(8);
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(0x2100 | bbus, bus.read(abus));
|
||||
dma_add_clocks(4);
|
||||
uint8 data = dma_read(abus);
|
||||
dma_add_clocks(4);
|
||||
bus.write(0x2100 | bbus, data);
|
||||
}
|
||||
} else {
|
||||
//b->a transfer (from $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//illegal WRAM->WRAM transfer (bus conflict)
|
||||
//no read occurs; write does occur
|
||||
//does not write MDR as expected
|
||||
//TODO: 0x00 was observed on hardware; verify if other values are possible
|
||||
bus.write(abus, 0x00);
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(abus, regs.mdr); //TODO: verify if MDR is written here
|
||||
dma_add_clocks(8);
|
||||
bus.write(abus, 0x00); //does not write S-CPU MDR
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(abus, bus.read(0x2100 | bbus));
|
||||
dma_add_clocks(4);
|
||||
uint8 data = bus.read(0x2100 | bbus);
|
||||
dma_add_clocks(4);
|
||||
if(dma_addr_valid(abus) == true) {
|
||||
bus.write(abus, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//each byte *always* consumes 8 clocks, even if transfer is invalid and no read and/or write occurs
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
|
||||
@@ -120,10 +119,22 @@ inline void sCPU::dma_write(uint8 i, uint8 index) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8 sCPU::dma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::dma_run() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled == false) continue;
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
|
||||
if(cartridge.info.sdd1 == true) {
|
||||
sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize);
|
||||
@@ -138,7 +149,7 @@ void sCPU::dma_run() {
|
||||
channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536);
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
unsigned index = 0;
|
||||
do {
|
||||
dma_write(i, dma_bbus(i, index++));
|
||||
} while(channel[i].dma_enabled && channel[i].xfersize);
|
||||
@@ -158,7 +169,7 @@ inline bool sCPU::hdma_active(uint8 i) {
|
||||
}
|
||||
|
||||
inline bool sCPU::hdma_active_after(uint8 i) {
|
||||
for(int n = i + 1; n < 8; n++) {
|
||||
for(unsigned n = i + 1; n < 8; n++) {
|
||||
if(hdma_active(n) == true) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -166,7 +177,7 @@ inline bool sCPU::hdma_active_after(uint8 i) {
|
||||
|
||||
inline uint8 sCPU::hdma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].hdma_enabled) r++;
|
||||
}
|
||||
return r;
|
||||
@@ -174,55 +185,57 @@ inline uint8 sCPU::hdma_enabled_channels() {
|
||||
|
||||
inline uint8 sCPU::hdma_active_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == true) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::hdma_update(uint8 i) {
|
||||
channel[i].hdma_line_counter = bus.read(hdma_addr(i));
|
||||
channel[i].hdma_line_counter = dma_read(hdma_addr(i));
|
||||
dma_add_clocks(8);
|
||||
|
||||
channel[i].hdma_completed = (channel[i].hdma_line_counter == 0);
|
||||
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8;
|
||||
channel[i].hdma_iaddr = dma_read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(!channel[i].hdma_completed || hdma_active_after(i)) {
|
||||
channel[i].hdma_iaddr >>= 8;
|
||||
channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8;
|
||||
channel[i].hdma_iaddr |= dma_read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_run() {
|
||||
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
for(int i = 0; i < 8; i++) {
|
||||
dma_add_clocks(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false) continue;
|
||||
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(channel[i].hdma_do_transfer) {
|
||||
int xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(int index = 0; index < xferlen; index++) {
|
||||
if(bool(config::cpu.hdma_enable) == true) {
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index),
|
||||
!channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i));
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
unsigned length = transfer_length[channel[i].xfermode];
|
||||
for(unsigned index = 0; index < length; index++) {
|
||||
unsigned addr = !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i);
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index), addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false) continue;
|
||||
|
||||
channel[i].hdma_line_counter--;
|
||||
channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80);
|
||||
if((channel[i].hdma_line_counter & 0x7f) == 0) {
|
||||
hdma_update(i);
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,15 +243,17 @@ void sCPU::hdma_run() {
|
||||
}
|
||||
|
||||
void sCPU::hdma_init_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_init() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled)continue;
|
||||
dma_add_clocks(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled) continue;
|
||||
channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
|
||||
|
||||
channel[i].hdma_addr = channel[i].srcaddr;
|
||||
@@ -253,7 +268,7 @@ void sCPU::hdma_init() {
|
||||
*****/
|
||||
|
||||
void sCPU::dma_power() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dmap = 0xff;
|
||||
channel[i].direction = 1;
|
||||
channel[i].hdma_indirect = true;
|
||||
@@ -277,7 +292,7 @@ void sCPU::dma_power() {
|
||||
}
|
||||
|
||||
void sCPU::dma_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = false;
|
||||
channel[i].hdma_enabled = false;
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
} channel[8];
|
||||
|
||||
void dma_add_clocks(uint clocks);
|
||||
bool dma_addr_valid(uint32 abus);
|
||||
uint8 dma_read(uint32 abus);
|
||||
void dma_transfer(bool direction, uint8 bbus, uint32 abus);
|
||||
|
||||
uint8 dma_bbus(uint8 i, uint8 index);
|
||||
@@ -56,6 +58,7 @@
|
||||
void dma_transfertobusb(uint8 i, uint8 bbus);
|
||||
void dma_transfertobusa(uint8 i, uint8 bbus);
|
||||
void dma_write(uint8 i, uint8 index);
|
||||
uint8 dma_enabled_channels();
|
||||
void dma_run();
|
||||
|
||||
bool hdma_active(uint8 i);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
uint8 sCPU::pio_status() {
|
||||
return status.pio;
|
||||
}
|
||||
uint8 sCPU::pio() { return status.pio; }
|
||||
bool sCPU::joylatch() { return status.joypad_strobe_latch; }
|
||||
|
||||
//WMDATA
|
||||
uint8 sCPU::mmio_r2180() {
|
||||
@@ -55,7 +54,7 @@ void sCPU::mmio_w4016(uint8 data) {
|
||||
//realtime or buffered status of joypadN.b
|
||||
uint8 sCPU::mmio_r4016() {
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= (uint8)snes.input.port_read(0);
|
||||
r |= snes.input.port_read(0) & 3;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -65,7 +64,7 @@ uint8 sCPU::mmio_r4016() {
|
||||
//1-0 = Joypad serial data
|
||||
uint8 sCPU::mmio_r4017() {
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= (uint8)snes.input.port_read(1);
|
||||
r |= snes.input.port_read(1) & 3;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -143,19 +142,16 @@ void sCPU::mmio_w420a(uint8 data) {
|
||||
|
||||
//DMAEN
|
||||
void sCPU::mmio_w420b(uint8 data) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = !!(data & (1 << i));
|
||||
}
|
||||
if(data) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.dma_pending = true;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = data & (1 << i);
|
||||
}
|
||||
if(data) status.dma_pending = true;
|
||||
}
|
||||
|
||||
//HDMAEN
|
||||
void sCPU::mmio_w420c(uint8 data) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].hdma_enabled = !!(data & (1 << i));
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].hdma_enabled = data & (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
uint8 mmio_read(uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
uint8 pio_status();
|
||||
uint8 pio();
|
||||
bool joylatch();
|
||||
|
||||
uint8 mmio_r2180();
|
||||
uint8 mmio_r4016();
|
||||
|
||||
+38
-43
@@ -15,13 +15,13 @@ public:
|
||||
} event;
|
||||
|
||||
struct {
|
||||
uint nmi_hold;
|
||||
uint irq_hold;
|
||||
unsigned nmi_hold;
|
||||
unsigned irq_hold;
|
||||
|
||||
uint nmi_fire;
|
||||
uint irq_fire;
|
||||
uint irq_delay;
|
||||
uint hw_math;
|
||||
unsigned nmi_fire;
|
||||
unsigned irq_fire;
|
||||
unsigned irq_delay;
|
||||
unsigned hw_math;
|
||||
|
||||
alwaysinline void set(uint &ctr, uint clocks) {
|
||||
if(clocks >= ctr) { ctr = clocks; }
|
||||
@@ -36,55 +36,50 @@ public:
|
||||
}
|
||||
} counter;
|
||||
|
||||
enum {
|
||||
DMASTATE_INACTIVE,
|
||||
DMASTATE_DMASYNC,
|
||||
DMASTATE_RUN,
|
||||
DMASTATE_CPUSYNC,
|
||||
};
|
||||
enum DMA_State { DMA_Inactive, DMA_Run, DMA_CPUsync };
|
||||
|
||||
struct {
|
||||
//core
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
|
||||
uint clock_count;
|
||||
unsigned clock_count;
|
||||
|
||||
//timing
|
||||
uint16 vcounter, hcounter;
|
||||
uint16 field_lines, line_clocks;
|
||||
|
||||
bool line_rendered;
|
||||
bool line_rendered;
|
||||
uint16 line_render_position;
|
||||
|
||||
bool dram_refreshed;
|
||||
bool dram_refreshed;
|
||||
uint16 dram_refresh_position;
|
||||
|
||||
bool hdmainit_triggered;
|
||||
bool hdmainit_triggered;
|
||||
uint16 hdmainit_trigger_position;
|
||||
|
||||
bool hdma_triggered;
|
||||
bool hdma_triggered;
|
||||
|
||||
uint16 irq_delay;
|
||||
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
|
||||
uint16 virq_trigger_pos, hirq_trigger_pos;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
|
||||
//dma
|
||||
uint dma_counter;
|
||||
uint dma_clocks;
|
||||
uint dma_state;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdmainit_pending;
|
||||
unsigned dma_counter;
|
||||
unsigned dma_clocks;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdma_mode; //0 = init, 1 = run
|
||||
DMA_State dma_state;
|
||||
|
||||
//mmio
|
||||
|
||||
@@ -92,20 +87,20 @@ public:
|
||||
uint32 wram_addr;
|
||||
|
||||
//$4016-$4017
|
||||
bool joypad_strobe_latch;
|
||||
bool joypad_strobe_latch;
|
||||
uint32 joypad1_bits;
|
||||
uint32 joypad2_bits;
|
||||
|
||||
//$4200
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
|
||||
//$4201
|
||||
uint8 pio;
|
||||
uint8 pio;
|
||||
|
||||
//$4202-$4203
|
||||
uint8 mul_a, mul_b;
|
||||
uint8 mul_a, mul_b;
|
||||
|
||||
//$4204-$4206
|
||||
uint16 div_a;
|
||||
@@ -119,10 +114,10 @@ public:
|
||||
uint16 r4216;
|
||||
|
||||
//$4218-$421f
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
} status;
|
||||
|
||||
void power();
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPU::run_auto_joypad_poll() {
|
||||
uint16_t joy1 = 0, joy2 = 0;
|
||||
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
joy1 |= (uint16_t)snes.input.port_read(0) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (uint16_t)snes.input.port_read(1) ? (0x8000 >> i) : 0;
|
||||
uint8 port0 = snes.input.port_read(0);
|
||||
uint8 port1 = snes.input.port_read(1);
|
||||
|
||||
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;
|
||||
joy3 |= (port0 & 2) ? (0x8000 >> i) : 0;
|
||||
joy4 |= (port1 & 2) ? (0x8000 >> i) : 0;
|
||||
}
|
||||
|
||||
status.joy1l = joy1;
|
||||
@@ -13,11 +18,11 @@ void sCPU::run_auto_joypad_poll() {
|
||||
status.joy2l = joy2;
|
||||
status.joy2h = joy2 >> 8;
|
||||
|
||||
status.joy3l = 0x00;
|
||||
status.joy3h = 0x00;
|
||||
status.joy3l = joy3;
|
||||
status.joy3h = joy3 >> 8;
|
||||
|
||||
status.joy4l = 0x00;
|
||||
status.joy4h = 0x00;
|
||||
status.joy4l = joy4;
|
||||
status.joy4h = joy4 >> 8;
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
@@ -1,9 +1,4 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||
ppu.interlace() == false && ppu.field() == 1 \
|
||||
)
|
||||
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
@@ -22,6 +17,12 @@ uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
|
||||
* Dot 323 range = { 1292, 1294, 1296 }
|
||||
* Dot 327 range = { 1310, 1312, 1314 }
|
||||
*****/
|
||||
|
||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||
ppu.interlace() == false && ppu.field() == 1 \
|
||||
)
|
||||
|
||||
uint16 sCPU::hdot() {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||
@@ -55,15 +56,7 @@ void sCPU::scanline() {
|
||||
|
||||
//dram refresh occurs once every scanline
|
||||
status.dram_refreshed = false;
|
||||
if(cpu_version == 2) {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == false) {
|
||||
if(status.dram_refresh_position == 534) {
|
||||
status.dram_refresh_position = 538;
|
||||
} else {
|
||||
status.dram_refresh_position = 534;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
|
||||
|
||||
//hdma triggers once every visible scanline
|
||||
status.line_rendered = false;
|
||||
@@ -86,8 +79,7 @@ void sCPU::frame() {
|
||||
|
||||
status.vcounter = 0;
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
//interlaced even fields have one extra scanline
|
||||
//(263+262=525 NTSC, 313+312=625 PAL)
|
||||
//interlaced even fields have one extra scanline (263+262=525 NTSC, 313+312=625 PAL)
|
||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
@@ -101,20 +93,19 @@ void sCPU::frame() {
|
||||
/*****
|
||||
* precycle_edge()
|
||||
*
|
||||
* Used for DMA/HDMA bus synchronization
|
||||
* Used for H/DMA bus synchronization
|
||||
*****/
|
||||
alwaysinline void sCPU::precycle_edge() {
|
||||
if(status.dma_state == DMASTATE_CPUSYNC) {
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
uint n = status.clock_count - (status.dma_clocks % status.clock_count);
|
||||
add_clocks(n ? n : status.clock_count);
|
||||
void sCPU::precycle_edge() {
|
||||
if(status.dma_state == DMA_CPUsync) {
|
||||
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
|
||||
status.dma_state = DMA_Inactive;
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* cycle_edge()
|
||||
*
|
||||
* Used to test for HDMA, which can trigger on the edge of every opcode cycle.
|
||||
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
|
||||
*****/
|
||||
void sCPU::cycle_edge() {
|
||||
if(status.line_rendered == false) {
|
||||
@@ -129,47 +120,55 @@ void sCPU::cycle_edge() {
|
||||
status.hdmainit_triggered = true;
|
||||
hdma_init_reset();
|
||||
if(hdma_enabled_channels()) {
|
||||
if(status.dma_state == DMASTATE_INACTIVE) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.hdmainit_pending = true;
|
||||
} else {
|
||||
hdma_init();
|
||||
}
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status.hdma_triggered == false) {
|
||||
if(status.hcounter >= 1106) {
|
||||
if(status.hcounter >= 1104) {
|
||||
status.hdma_triggered = true;
|
||||
if(hdma_active_channels()) {
|
||||
if(status.dma_state == DMASTATE_INACTIVE) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.hdma_pending = true;
|
||||
} else {
|
||||
hdma_run();
|
||||
}
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_INACTIVE: break;
|
||||
|
||||
case DMASTATE_DMASYNC: {
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
} break;
|
||||
|
||||
case DMASTATE_RUN: {
|
||||
status.dma_state = DMASTATE_CPUSYNC;
|
||||
status.dma_clocks = 8 - dma_counter() + 8;
|
||||
add_clocks(status.dma_clocks);
|
||||
|
||||
if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; }
|
||||
if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; }
|
||||
if(status.dma_pending) { dma_run(); status.dma_pending = false; }
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
//H/DMA pending && DMA inactive?
|
||||
//.. Run one full CPU cycle
|
||||
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
|
||||
//.. DMA pending && DMA enabled ? DMA sync + DMA run
|
||||
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
|
||||
//.. Run one bus CPU cycle
|
||||
//.. CPU sync
|
||||
|
||||
if(status.dma_state == DMA_Run) {
|
||||
if(status.hdma_pending) {
|
||||
status.hdma_pending = false;
|
||||
if(hdma_enabled_channels()) {
|
||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||
status.hdma_mode == 0 ? hdma_init() : hdma_run();
|
||||
status.dma_state = DMA_CPUsync;
|
||||
}
|
||||
}
|
||||
|
||||
if(status.dma_pending) {
|
||||
status.dma_pending = false;
|
||||
if(dma_enabled_channels()) {
|
||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||
dma_run();
|
||||
status.dma_state = DMA_CPUsync;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status.dma_state == DMA_Inactive) {
|
||||
if(status.dma_pending || status.hdma_pending) {
|
||||
status.dma_clocks = 0;
|
||||
status.dma_state = DMA_Run;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +211,7 @@ void sCPU::timing_reset() {
|
||||
status.line_clocks = 1364;
|
||||
|
||||
status.line_rendered = false;
|
||||
status.line_render_position = min(1112U, (uint)config::ppu.hack.render_scanline_position);
|
||||
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
|
||||
|
||||
status.dram_refreshed = false;
|
||||
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
||||
@@ -236,11 +235,12 @@ void sCPU::timing_reset() {
|
||||
|
||||
update_interrupts();
|
||||
|
||||
status.dma_counter = 0;
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdmainit_pending = false;
|
||||
status.dma_counter = 0;
|
||||
status.dma_clocks = 0;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdma_mode = 0;
|
||||
status.dma_state = DMA_Inactive;
|
||||
|
||||
history.reset();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
void sDSP::brr_decode(voice_t &v) {
|
||||
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
|
||||
int nybbles = (state.t_brr_byte << 8) + ram[(uint16)(v.brr_addr + v.brr_offset + 1)];
|
||||
int nybbles = (state.t_brr_byte << 8) + memory::apuram.read(uint16(v.brr_addr + v.brr_offset + 1));
|
||||
|
||||
const int filter = (state.t_brr_header >> 2) & 3;
|
||||
const int scale = (state.t_brr_header >> 4);
|
||||
|
||||
@@ -12,17 +12,19 @@ int sDSP::echo_output(bool channel) {
|
||||
}
|
||||
|
||||
void sDSP::echo_read(bool channel) {
|
||||
uint8 *in = &ram[state.t_echo_ptr + channel * 2];
|
||||
int s = (int16)((in[1] << 8) + in[0]);
|
||||
unsigned addr = state.t_echo_ptr + channel * 2;
|
||||
uint8 lo = memory::apuram.read(uint16(addr + 0));
|
||||
uint8 hi = memory::apuram.read(uint16(addr + 1));
|
||||
int s = (int16)((hi << 8) + lo);
|
||||
state.echo_hist[channel].write(state.echo_hist_pos, s >> 1);
|
||||
}
|
||||
|
||||
void sDSP::echo_write(bool channel) {
|
||||
if(!(state.t_echo_disabled & 0x20)) {
|
||||
uint8 *out = &ram[state.t_echo_ptr + channel * 2];
|
||||
unsigned addr = state.t_echo_ptr + channel * 2;
|
||||
int s = state.t_echo_out[channel];
|
||||
out[0] = (uint8)(s);
|
||||
out[1] = (uint8)(s >> 8);
|
||||
memory::apuram.write(uint16(addr + 0), (uint8)(s >> 0));
|
||||
memory::apuram.write(uint16(addr + 1), (uint8)(s >> 8));
|
||||
}
|
||||
|
||||
state.t_echo_out[channel] = 0;
|
||||
|
||||
@@ -249,8 +249,6 @@ void sDSP::write(uint8 addr, uint8 data) {
|
||||
/* initialization */
|
||||
|
||||
void sDSP::power() {
|
||||
ram = (uint8*)smp.get_spcram_handle(); //TODO: move to sMemory
|
||||
|
||||
memset(&state.regs, 0, sizeof state.regs);
|
||||
state.echo_hist_pos = 0;
|
||||
state.every_other_sample = false;
|
||||
|
||||
@@ -12,9 +12,6 @@ public:
|
||||
~sDSP();
|
||||
|
||||
private:
|
||||
//external
|
||||
uint8 *ram;
|
||||
|
||||
//USE_STATE_MACHINE variable
|
||||
unsigned phase_index;
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ void sDSP::voice_1(voice_t &v) {
|
||||
|
||||
void sDSP::voice_2(voice_t &v) {
|
||||
//read sample pointer (ignored if not needed)
|
||||
const uint8 *entry = &ram[state.t_dir_addr];
|
||||
if(!v.kon_delay) entry += 2;
|
||||
state.t_brr_next_addr = (entry[1] << 8) + entry[0];
|
||||
uint16 addr = state.t_dir_addr;
|
||||
if(!v.kon_delay) addr += 2;
|
||||
uint8 lo = memory::apuram.read(uint16(addr + 0));
|
||||
uint8 hi = memory::apuram.read(uint16(addr + 1));
|
||||
state.t_brr_next_addr = ((hi << 8) + lo);
|
||||
|
||||
state.t_adsr0 = VREG(adsr0);
|
||||
|
||||
@@ -43,8 +45,8 @@ void sDSP::voice_3a(voice_t &v) {
|
||||
}
|
||||
|
||||
void sDSP::voice_3b(voice_t &v) {
|
||||
state.t_brr_byte = ram[(uint16)(v.brr_addr + v.brr_offset)];
|
||||
state.t_brr_header = ram[(uint16)(v.brr_addr)];
|
||||
state.t_brr_byte = memory::apuram.read(uint16(v.brr_addr + v.brr_offset));
|
||||
state.t_brr_header = memory::apuram.read(uint16(v.brr_addr));
|
||||
}
|
||||
|
||||
void sDSP::voice_3c(voice_t &v) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+31
-40
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
@@ -27,21 +28,21 @@ private:
|
||||
|
||||
public:
|
||||
inline operator T() const { return data; }
|
||||
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
|
||||
inline T& operator ++() { data = uclip<bits>(data + 1); return *this; }
|
||||
inline T& operator --() { data = uclip<bits>(data - 1); return *this; }
|
||||
inline T& operator =(const T i) { data = uclip<bits>(i); return *this; }
|
||||
inline T& operator |=(const T i) { data = uclip<bits>(data | i); return *this; }
|
||||
inline T& operator ^=(const T i) { data = uclip<bits>(data ^ i); return *this; }
|
||||
inline T& operator &=(const T i) { data = uclip<bits>(data & i); return *this; }
|
||||
inline T& operator<<=(const T i) { data = uclip<bits>(data << i); return *this; }
|
||||
inline T& operator>>=(const T i) { data = uclip<bits>(data >> i); return *this; }
|
||||
inline T& operator +=(const T i) { data = uclip<bits>(data + i); return *this; }
|
||||
inline T& operator -=(const T i) { data = uclip<bits>(data - i); return *this; }
|
||||
inline T& operator *=(const T i) { data = uclip<bits>(data * i); return *this; }
|
||||
inline T& operator /=(const T i) { data = uclip<bits>(data / i); return *this; }
|
||||
inline T& operator %=(const T i) { data = uclip<bits>(data % i); return *this; }
|
||||
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
|
||||
inline T operator ++() { return data = uclip<bits>(data + 1); }
|
||||
inline T operator --() { return data = uclip<bits>(data - 1); }
|
||||
inline T operator =(const T i) { return data = uclip<bits>(i); }
|
||||
inline T operator |=(const T i) { return data = uclip<bits>(data | i); }
|
||||
inline T operator ^=(const T i) { return data = uclip<bits>(data ^ i); }
|
||||
inline T operator &=(const T i) { return data = uclip<bits>(data & i); }
|
||||
inline T operator<<=(const T i) { return data = uclip<bits>(data << i); }
|
||||
inline T operator>>=(const T i) { return data = uclip<bits>(data >> i); }
|
||||
inline T operator +=(const T i) { return data = uclip<bits>(data + i); }
|
||||
inline T operator -=(const T i) { return data = uclip<bits>(data - i); }
|
||||
inline T operator *=(const T i) { return data = uclip<bits>(data * i); }
|
||||
inline T operator /=(const T i) { return data = uclip<bits>(data / i); }
|
||||
inline T operator %=(const T i) { return data = uclip<bits>(data % i); }
|
||||
|
||||
inline uint_t() : data(0) {}
|
||||
inline uint_t(const T i) : data(uclip<bits>(i)) {}
|
||||
@@ -68,36 +69,26 @@ private:
|
||||
|
||||
public:
|
||||
inline operator T() const { return data; }
|
||||
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
|
||||
inline T& operator ++() { data = sclip<bits>(data + 1); return *this; }
|
||||
inline T& operator --() { data = sclip<bits>(data - 1); return *this; }
|
||||
inline T& operator =(const T i) { data = sclip<bits>(i); return *this; }
|
||||
inline T& operator |=(const T i) { data = sclip<bits>(data | i); return *this; }
|
||||
inline T& operator ^=(const T i) { data = sclip<bits>(data ^ i); return *this; }
|
||||
inline T& operator &=(const T i) { data = sclip<bits>(data & i); return *this; }
|
||||
inline T& operator<<=(const T i) { data = sclip<bits>(data << i); return *this; }
|
||||
inline T& operator>>=(const T i) { data = sclip<bits>(data >> i); return *this; }
|
||||
inline T& operator +=(const T i) { data = sclip<bits>(data + i); return *this; }
|
||||
inline T& operator -=(const T i) { data = sclip<bits>(data - i); return *this; }
|
||||
inline T& operator *=(const T i) { data = sclip<bits>(data * i); return *this; }
|
||||
inline T& operator /=(const T i) { data = sclip<bits>(data / i); return *this; }
|
||||
inline T& operator %=(const T i) { data = sclip<bits>(data % i); return *this; }
|
||||
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
|
||||
inline T operator ++() { return data = sclip<bits>(data + 1); }
|
||||
inline T operator --() { return data = sclip<bits>(data - 1); }
|
||||
inline T operator =(const T i) { return data = sclip<bits>(i); }
|
||||
inline T operator |=(const T i) { return data = sclip<bits>(data | i); }
|
||||
inline T operator ^=(const T i) { return data = sclip<bits>(data ^ i); }
|
||||
inline T operator &=(const T i) { return data = sclip<bits>(data & i); }
|
||||
inline T operator<<=(const T i) { return data = sclip<bits>(data << i); }
|
||||
inline T operator>>=(const T i) { return data = sclip<bits>(data >> i); }
|
||||
inline T operator +=(const T i) { return data = sclip<bits>(data + i); }
|
||||
inline T operator -=(const T i) { return data = sclip<bits>(data - i); }
|
||||
inline T operator *=(const T i) { return data = sclip<bits>(data * i); }
|
||||
inline T operator /=(const T i) { return data = sclip<bits>(data / i); }
|
||||
inline T operator %=(const T i) { return data = sclip<bits>(data % i); }
|
||||
|
||||
inline int_t() : data(0) {}
|
||||
inline int_t(const T i) : data(sclip<bits>(i)) {}
|
||||
};
|
||||
|
||||
typedef int_t<24> int24_t;
|
||||
typedef uint_t<24> uint24_t;
|
||||
typedef int_t<48> int48_t;
|
||||
typedef uint_t<48> uint48_t;
|
||||
|
||||
} //namespace nall
|
||||
|
||||
using nall::int24_t;
|
||||
using nall::uint24_t;
|
||||
using nall::int48_t;
|
||||
using nall::uint48_t;
|
||||
|
||||
#endif //ifndef NALL_VARINT_HPP
|
||||
+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;
|
||||
|
||||
@@ -2,6 +2,7 @@ class Input {
|
||||
public:
|
||||
enum Setting {
|
||||
Handle,
|
||||
AnalogAxisResistance,
|
||||
};
|
||||
|
||||
virtual bool cap(Setting) { return false; }
|
||||
|
||||
@@ -25,15 +25,18 @@ public:
|
||||
|
||||
struct {
|
||||
HWND handle;
|
||||
unsigned analog_axis_resistance;
|
||||
} settings;
|
||||
|
||||
bool cap(Input::Setting setting) {
|
||||
if(setting == Input::Handle) return true;
|
||||
if(setting == Input::AnalogAxisResistance) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t get(Input::Setting setting) {
|
||||
if(setting == Input::Handle) return (uintptr_t)settings.handle;
|
||||
if(setting == Input::AnalogAxisResistance) return settings.analog_axis_resistance;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,6 +45,12 @@ public:
|
||||
settings.handle = (HWND)param;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(setting == Input::AnalogAxisResistance) {
|
||||
settings.analog_axis_resistance = param;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -79,9 +88,9 @@ public:
|
||||
memcpy(keystate + index, js.rgbButtons, 128);
|
||||
|
||||
//map d-pad axes
|
||||
int resistance = 75; //config::input.axis_resistance;
|
||||
int resistance = settings.analog_axis_resistance;
|
||||
resistance = max(1, min(99, resistance));
|
||||
resistance = int32_t(double(resistance) * 32768.0 / 100.0);
|
||||
resistance = int(double(resistance) * 32768.0 / 100.0);
|
||||
int resistance_lo = 0x7fff - resistance;
|
||||
int resistance_hi = 0x8000 + resistance;
|
||||
keystate[index + 0x80] = (js.lY <= resistance_lo) ? 0x80 : 0x00;
|
||||
@@ -297,6 +306,9 @@ public:
|
||||
di = 0;
|
||||
di_key = 0;
|
||||
for(int i = 0; i < DIRECTINPUT_JOYMAX; i++) di_joy[i] = 0;
|
||||
|
||||
settings.handle = 0;
|
||||
settings.analog_axis_resistance = 75;
|
||||
}
|
||||
|
||||
~pInputDI() { term(); }
|
||||
|
||||
@@ -54,6 +54,29 @@ public:
|
||||
Display *display;
|
||||
#endif
|
||||
|
||||
struct {
|
||||
unsigned analog_axis_resistance;
|
||||
} settings;
|
||||
|
||||
bool cap(Input::Setting setting) {
|
||||
if(setting == Input::AnalogAxisResistance) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t get(Input::Setting setting) {
|
||||
if(setting == Input::AnalogAxisResistance) return settings.analog_axis_resistance;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(Input::Setting setting, uintptr_t param) {
|
||||
if(setting == Input::AnalogAxisResistance) {
|
||||
settings.analog_axis_resistance = param;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool key_down(uint16_t key) {
|
||||
#if !defined(_WIN32)
|
||||
#define map(i) (keystate[i >> 3] & (1 << (i & 7)))
|
||||
@@ -220,6 +243,10 @@ public:
|
||||
joystate[i][joy_left] = false;
|
||||
joystate[i][joy_right] = false;
|
||||
|
||||
int resistance = settings.analog_axis_resistance;
|
||||
resistance = max(1, min(99, resistance));
|
||||
resistance = int(double(resistance) * 32768.0 / 100.0);
|
||||
|
||||
//only poll X,Y axes for D-pad, left analog and right analog.
|
||||
//note 1: right analog is swapped on some controllers, this cannot be helped.
|
||||
//note 2: some controllers report more axes than physically exist.
|
||||
@@ -229,11 +256,11 @@ public:
|
||||
for(int a = 0; a < min(axes, 6); a++) {
|
||||
int value = SDL_JoystickGetAxis(joy[i], a);
|
||||
if((a & 1) == 0) { //X axis
|
||||
joystate[i][joy_left] |= value < -16384;
|
||||
joystate[i][joy_right] |= value > +16384;
|
||||
joystate[i][joy_left] |= value < -resistance;
|
||||
joystate[i][joy_right] |= value > +resistance;
|
||||
} else { //Y axis
|
||||
joystate[i][joy_up] |= value < -16384;
|
||||
joystate[i][joy_down] |= value > +16384;
|
||||
joystate[i][joy_up] |= value < -resistance;
|
||||
joystate[i][joy_down] |= value > +resistance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,10 +295,15 @@ public:
|
||||
pInputSDL(InputSDL &self_) : self(self_) {
|
||||
for(int i = 0; i < 16; i++) joy[i] = 0;
|
||||
clear();
|
||||
|
||||
settings.analog_axis_resistance = 75;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool InputSDL::cap(Setting setting) { return p.cap(setting); }
|
||||
uintptr_t InputSDL::get(Setting setting) { return p.get(setting); }
|
||||
bool InputSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
|
||||
bool InputSDL::key_down(uint16_t key) { return p.key_down(key); }
|
||||
void InputSDL::clear() { p.clear(); }
|
||||
void InputSDL::poll() { p.poll(); }
|
||||
|
||||
@@ -2,6 +2,10 @@ class pInputSDL;
|
||||
|
||||
class InputSDL : public Input {
|
||||
public:
|
||||
bool cap(Setting);
|
||||
uintptr_t get(Setting);
|
||||
bool set(Setting, uintptr_t);
|
||||
|
||||
bool key_down(uint16_t key);
|
||||
|
||||
void clear();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
video.wgl
|
||||
author: byuu, krom
|
||||
authors: byuu, krom, mudlord
|
||||
license: public domain
|
||||
date: 2008-03-26
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace memory {
|
||||
MMIOAccess mmio;
|
||||
StaticRAM wram(128 * 1024);
|
||||
StaticRAM apuram(64 * 1024);
|
||||
|
||||
UnmappedMemory memory_unmapped;
|
||||
UnmappedMMIO mmio_unmapped;
|
||||
|
||||
+3
-2
@@ -115,8 +115,9 @@ class Bus { public:
|
||||
};
|
||||
|
||||
namespace memory {
|
||||
extern MMIOAccess mmio;
|
||||
extern StaticRAM wram;
|
||||
extern MMIOAccess mmio; //S-CPU, S-PPU
|
||||
extern StaticRAM wram; //S-CPU
|
||||
extern StaticRAM apuram; //S-SMP, S-DSP
|
||||
|
||||
extern UnmappedMemory memory_unmapped;
|
||||
extern UnmappedMMIO mmio_unmapped;
|
||||
|
||||
@@ -32,9 +32,19 @@ void sBus::map_generic() {
|
||||
map_generic_sram();
|
||||
} break;
|
||||
|
||||
case Cartridge::SPC7110ROM: {
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
|
||||
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
|
||||
} 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;
|
||||
|
||||
@@ -578,7 +578,7 @@ uint32 r;
|
||||
|
||||
//SLHV
|
||||
uint8 bPPU::mmio_r2137() {
|
||||
if(cpu.pio_status() & 0x80) {
|
||||
if(cpu.pio() & 0x80) {
|
||||
latch_counters();
|
||||
}
|
||||
return cpu.regs.mdr;
|
||||
@@ -679,7 +679,7 @@ uint8 r = 0x00;
|
||||
regs.latch_vcounter = 0;
|
||||
|
||||
r |= field() << 7;
|
||||
if(!(cpu.pio_status() & 0x80)) {
|
||||
if(!(cpu.pio() & 0x80)) {
|
||||
r |= 0x40;
|
||||
} else if(regs.counters_latched == true) {
|
||||
r |= 0x40;
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ PPU::PPU() {
|
||||
status.frames_executed = 0;
|
||||
|
||||
ppu1_version = 1;
|
||||
ppu2_version = 1;
|
||||
ppu2_version = 3;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
|
||||
+1
-3
@@ -5,7 +5,6 @@ public:
|
||||
virtual void enter() = 0;
|
||||
|
||||
SMPRegs regs;
|
||||
uint8 spcram[65536];
|
||||
static const uint8 iplrom[64];
|
||||
enum {
|
||||
FLAG_N = 0x80, FLAG_V = 0x40, FLAG_P = 0x20, FLAG_B = 0x10,
|
||||
@@ -18,7 +17,6 @@ public:
|
||||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
virtual uint8* get_spcram_handle() { return spcram; }
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
@@ -27,6 +25,6 @@ public:
|
||||
void disassemble_opcode(char *output);
|
||||
inline uint16 __relb(int8 offset, int op_len);
|
||||
|
||||
SMP() { memset(spcram, 0, 65536); }
|
||||
SMP() {}
|
||||
virtual ~SMP() {}
|
||||
};
|
||||
@@ -2,27 +2,27 @@
|
||||
|
||||
alwaysinline
|
||||
uint8 sSMP::ram_read(uint16 addr) {
|
||||
if(addr < 0xffc0) return spcram[addr];
|
||||
if(status.iplrom_enabled == false) return spcram[addr];
|
||||
if(addr < 0xffc0) return memory::apuram.read(addr);
|
||||
if(status.iplrom_enabled == false) return memory::apuram.read(addr);
|
||||
return iplrom[addr & 0x3f];
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
void sSMP::ram_write(uint16 addr, uint8 data) {
|
||||
//writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled
|
||||
spcram[addr] = data;
|
||||
memory::apuram.write(addr, data);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
alwaysinline
|
||||
uint8 sSMP::port_read(uint8 port) {
|
||||
return spcram[0xf4 + (port & 3)];
|
||||
return memory::apuram.read(0xf4 + (port & 3));
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
void sSMP::port_write(uint8 port, uint8 data) {
|
||||
spcram[0xf4 + (port & 3)] = data;
|
||||
memory::apuram.write(0xf4 + (port & 3), data);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -87,14 +87,8 @@ uint8 sSMP::op_busread(uint16 addr) {
|
||||
t2.stage3_ticks = 0;
|
||||
} break;
|
||||
}
|
||||
} else if(addr < 0xffc0) {
|
||||
r = spcram[addr];
|
||||
} else {
|
||||
if(status.iplrom_enabled == true) {
|
||||
r = iplrom[addr & 0x3f];
|
||||
} else {
|
||||
r = spcram[addr];
|
||||
}
|
||||
r = ram_read(addr);
|
||||
}
|
||||
|
||||
return r;
|
||||
@@ -112,17 +106,17 @@ void sSMP::op_buswrite(uint16 addr, uint8 data) {
|
||||
|
||||
//multiplier table may not be 100% accurate, some settings crash
|
||||
//the processor due to S-SMP <> S-DSP bus access misalignment
|
||||
static uint8 clock_speed_tbl[16] =
|
||||
{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 };
|
||||
//static uint8 clock_speed_tbl[16] =
|
||||
//{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 };
|
||||
|
||||
status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3;
|
||||
//status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3;
|
||||
status.mmio_disabled = !!(data & 0x04);
|
||||
status.ram_writable = !!(data & 0x02);
|
||||
|
||||
if((data >> 4) != 0) {
|
||||
dprintf(source::smp, "* S-SMP critical warning: $00f0 (TEST) clock speed control modified!");
|
||||
dprintf(source::smp, "* S-SMP may crash on hardware as a result!");
|
||||
}
|
||||
//if((data >> 4) != 0) {
|
||||
//dprintf("* S-SMP critical warning: $00f0 (TEST) clock speed control modified!");
|
||||
//dprintf("* S-SMP may crash on hardware as a result!");
|
||||
//}
|
||||
} break;
|
||||
|
||||
case 0xf1: { //CONTROL
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#include "timing/timing.cpp"
|
||||
|
||||
void sSMP::power() {
|
||||
//SNES hardware SPCRAM contains pseudo-random data upon power up
|
||||
//for(unsigned i = 0; i < 65536; i += 64) {
|
||||
// memset(spcram + i, 0x00, 32);
|
||||
// memset(spcram + i + 32, 0xff, 32);
|
||||
//}
|
||||
|
||||
memset(spcram, 0x00, 65536);
|
||||
for(unsigned i = 0; i < 65536; i++) {
|
||||
//SNES hardware APURAM contains pseudo-random data upon power up
|
||||
//memory::apuram.write(i, (i & 32) ? 0xff : 0x00);
|
||||
memory::apuram.write(i, 0x00);
|
||||
}
|
||||
|
||||
//targets not initialized/changed upon reset
|
||||
t0.target = 0;
|
||||
|
||||
+63
-87
@@ -1,102 +1,78 @@
|
||||
#ifdef SNES_CPP
|
||||
|
||||
bool SNES::Input::port_read(bool port) {
|
||||
if(port == 0) {
|
||||
switch(input.port0_device) {
|
||||
case DeviceNone: return false;
|
||||
#ifdef SNES_CPP
|
||||
|
||||
default: {
|
||||
if(input.port0_devicebitpos < input.port0_devicebits) {
|
||||
return input.port0_bits[input.port0_devicebitpos++];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(input.port1_device) {
|
||||
case DeviceNone: return false;
|
||||
uint8 SNES::Input::port_read(bool port) {
|
||||
switch(port_device[port]) {
|
||||
case DeviceJoypad: {
|
||||
if(port_counter0[port] >= 16) return 1;
|
||||
|
||||
default: {
|
||||
if(input.port1_devicebitpos < input.port1_devicebits) {
|
||||
return input.port1_bits[input.port1_devicebitpos++];
|
||||
unsigned deviceid = port == 0 ? DeviceIDJoypad1 : DeviceIDJoypad2;
|
||||
return snesinterface.input_poll(deviceid, port_counter0[port]++);
|
||||
} //case DeviceJoypad
|
||||
|
||||
case DeviceMultitap: {
|
||||
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
|
||||
|
||||
unsigned deviceidx, deviceid0, deviceid1;
|
||||
if(port == 0) {
|
||||
if(cpu.pio() & 0x40) {
|
||||
deviceidx = port_counter0[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter0[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap1A;
|
||||
deviceid1 = DeviceIDMultitap1B;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
deviceidx = port_counter1[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter1[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap1C;
|
||||
deviceid1 = DeviceIDMultitap1D;
|
||||
}
|
||||
} else {
|
||||
if(cpu.pio() & 0x80) {
|
||||
deviceidx = port_counter0[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter0[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap2A;
|
||||
deviceid1 = DeviceIDMultitap2B;
|
||||
} else {
|
||||
deviceidx = port_counter1[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter1[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap2C;
|
||||
deviceid1 = DeviceIDMultitap2D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (snesinterface.input_poll(deviceid0, deviceidx) << 0)
|
||||
| (snesinterface.input_poll(deviceid1, deviceidx) << 1);
|
||||
} //case DeviceMultitap
|
||||
}
|
||||
|
||||
//no device connected
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SNES::Input::port_set_deviceid(bool port, uint deviceid) {
|
||||
if(port == 0) {
|
||||
switch(deviceid) {
|
||||
case DeviceIDNone: {
|
||||
input.port0_device = DeviceNone;
|
||||
} break;
|
||||
|
||||
case DeviceIDJoypad1:
|
||||
case DeviceIDJoypad2: {
|
||||
input.port0_device = DeviceJoypad;
|
||||
input.port0_devicebits = 16;
|
||||
} break;
|
||||
}
|
||||
|
||||
memset(input.port0_bits, 0, sizeof(input.port0_bits));
|
||||
input.port0_devicebitpos = 0;
|
||||
input.port0_deviceid = deviceid;
|
||||
} else {
|
||||
switch(deviceid) {
|
||||
case DeviceIDNone: {
|
||||
input.port1_device = DeviceNone;
|
||||
} break;
|
||||
|
||||
case DeviceIDJoypad1:
|
||||
case DeviceIDJoypad2: {
|
||||
input.port1_device = DeviceJoypad;
|
||||
input.port1_devicebits = 16;
|
||||
} break;
|
||||
}
|
||||
|
||||
memset(input.port1_bits, 0, sizeof(input.port1_bits));
|
||||
input.port1_devicebitpos = 0;
|
||||
input.port1_deviceid = deviceid;
|
||||
}
|
||||
void SNES::Input::port_set_device(bool port, unsigned device) {
|
||||
port_device[port] = device;
|
||||
port_counter0[port] = 0;
|
||||
port_counter1[port] = 0;
|
||||
}
|
||||
|
||||
void SNES::Input::poll() {
|
||||
snesinterface.input_poll();
|
||||
|
||||
bool *p0 = input.port0_bits;
|
||||
bool *p1 = input.port1_bits;
|
||||
|
||||
switch(input.port0_device) {
|
||||
case DeviceNone: break;
|
||||
|
||||
default: {
|
||||
for(int i = 0; i < input.port0_devicebits; i++) {
|
||||
*p0++ = snesinterface.input_poll(input.port0_deviceid, i);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
switch(input.port1_device) {
|
||||
case DeviceNone: break;
|
||||
|
||||
default: {
|
||||
for(int i = 0; i < input.port1_devicebits; i++) {
|
||||
*p1++ = snesinterface.input_poll(input.port1_deviceid, i);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
input.port0_devicebitpos = 0;
|
||||
input.port1_devicebitpos = 0;
|
||||
port_counter0[0] = 0;
|
||||
port_counter1[0] = 0;
|
||||
port_counter0[1] = 0;
|
||||
port_counter1[1] = 0;
|
||||
}
|
||||
|
||||
void SNES::Input::init() {
|
||||
port_set_deviceid(0, config::snes.controller_port0);
|
||||
port_set_deviceid(1, config::snes.controller_port1);
|
||||
}
|
||||
|
||||
port_set_device(0, config::snes.controller_port1);
|
||||
port_set_device(1, config::snes.controller_port2);
|
||||
}
|
||||
|
||||
#endif //ifdef SNES_CPP
|
||||
+14
-10
@@ -3,12 +3,21 @@ public:
|
||||
enum Device {
|
||||
DeviceNone,
|
||||
DeviceJoypad,
|
||||
DeviceMultitap,
|
||||
};
|
||||
|
||||
enum {
|
||||
DeviceIDNone,
|
||||
DeviceIDJoypad1,
|
||||
DeviceIDJoypad2,
|
||||
DeviceIDMultitap1A,
|
||||
DeviceIDMultitap1B,
|
||||
DeviceIDMultitap1C,
|
||||
DeviceIDMultitap1D,
|
||||
DeviceIDMultitap2A,
|
||||
DeviceIDMultitap2B,
|
||||
DeviceIDMultitap2C,
|
||||
DeviceIDMultitap2D,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -20,16 +29,11 @@ public:
|
||||
JoypadL = 10, JoypadR = 11,
|
||||
};
|
||||
|
||||
struct {
|
||||
uint port0_device, port0_devicebits, port0_devicebitpos, port0_deviceid;
|
||||
uint port1_device, port1_devicebits, port1_devicebitpos, port1_deviceid;
|
||||
|
||||
bool port0_bits[256];
|
||||
bool port1_bits[256];
|
||||
} input;
|
||||
|
||||
bool port_read(bool port);
|
||||
void port_set_deviceid(bool port, uint deviceid);
|
||||
uint8 port_read(bool port);
|
||||
void port_set_device(bool port, unsigned device);
|
||||
void init();
|
||||
void poll();
|
||||
|
||||
private:
|
||||
unsigned port_device[2], port_counter0[2], port_counter1[2];
|
||||
} input;
|
||||
@@ -12,7 +12,7 @@ class SNESInterface { public:
|
||||
|
||||
function<bool ()> input_ready;
|
||||
void input_poll();
|
||||
bool input_poll(unsigned deviceid, unsigned button);
|
||||
bool input_poll(unsigned deviceid, unsigned id);
|
||||
|
||||
void init();
|
||||
void term();
|
||||
|
||||
@@ -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, 283, 76, translate["About bsnes ..."]);
|
||||
set_icon(48, 48, (uint32_t*)resource::icon48);
|
||||
|
||||
icon.create(0, 48, 48);
|
||||
@@ -13,17 +13,11 @@ void AboutWindow::setup() {
|
||||
<< translate["Author"] << ": byuu\n"
|
||||
<< translate["Project began: October 14th, 2004"]
|
||||
);
|
||||
contributors.create(0, 275, 70, string()
|
||||
<< translate["Contributors:"] << "\n"
|
||||
<< " anomie, blargg, DMV27, GIGO, kode54, Nach,\n"
|
||||
<< " Overload, Richard Bannister, TRAC, zones\n"
|
||||
<< "\n"
|
||||
<< translate["Localization by: byuu"]
|
||||
);
|
||||
localization.create(0, 273, 18, translate["Localization by: byuu"]);
|
||||
|
||||
attach(icon, 5, 5);
|
||||
attach(about, 58, 5);
|
||||
attach(contributors, 5, 58);
|
||||
attach(localization, 5, 58);
|
||||
|
||||
on_close = bind(&AboutWindow::close, this);
|
||||
|
||||
|
||||
+2
-4
@@ -1,10 +1,8 @@
|
||||
class AboutWindow : public Window {
|
||||
public:
|
||||
Canvas icon;
|
||||
Label about;
|
||||
Label contributors;
|
||||
static const char about_text[1024];
|
||||
static const char contributors_text[1024];
|
||||
Label about;
|
||||
Label localization;
|
||||
|
||||
void setup();
|
||||
uintptr_t close(Event);
|
||||
|
||||
+49
-3
@@ -49,6 +49,14 @@ uintptr_t MainWindow::event(Event e) {
|
||||
event::power();
|
||||
}
|
||||
|
||||
if(e.widget == &menu_system_controller_port1_none) { event::update_controller_port1(0); }
|
||||
if(e.widget == &menu_system_controller_port1_joypad) { event::update_controller_port1(1); }
|
||||
if(e.widget == &menu_system_controller_port1_multitap) { event::update_controller_port1(2); }
|
||||
|
||||
if(e.widget == &menu_system_controller_port2_none) { event::update_controller_port2(0); }
|
||||
if(e.widget == &menu_system_controller_port2_joypad) { event::update_controller_port2(1); }
|
||||
if(e.widget == &menu_system_controller_port2_multitap) { event::update_controller_port2(2); }
|
||||
|
||||
if(e.widget == &menu_file_exit) {
|
||||
event(Event(Event::Close));
|
||||
}
|
||||
@@ -133,11 +141,31 @@ void MainWindow::setup() {
|
||||
menu_file_load_special.attach(menu_file_load_bsc.create(string() << translate["Load BS-X Slotted Cartridge"] << " ..."));
|
||||
menu_file_load_special.attach(menu_file_load_st.create(string() << translate["Load Sufami Turbo Cartridge"] << " ..."));
|
||||
menu_file.attach(menu_file_unload.create(translate["Unload Cartridge"]));
|
||||
|
||||
menu_file.attach(menu_file_sep1.create());
|
||||
menu_file.attach(menu_file_reset.create(translate["Reset"]));
|
||||
menu_file_power.create(translate["Power Cycle"]);
|
||||
if(config::advanced.enable) menu_file.attach(menu_file_power);
|
||||
|
||||
menu_file.attach(menu_file_sep2.create());
|
||||
menu_file.attach(menu_system_controller_port1.create(translate["Controller Port 1"]));
|
||||
group.add(&menu_system_controller_port1_none);
|
||||
group.add(&menu_system_controller_port1_joypad);
|
||||
group.add(&menu_system_controller_port1_multitap);
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_none.create (group, translate["None"]));
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_joypad.create (group, translate["Joypad"]));
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_multitap.create(group, translate["Multitap"]));
|
||||
group.reset();
|
||||
menu_file.attach(menu_system_controller_port2.create(translate["Controller Port 2"]));
|
||||
group.add(&menu_system_controller_port2_none);
|
||||
group.add(&menu_system_controller_port2_joypad);
|
||||
group.add(&menu_system_controller_port2_multitap);
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_none.create (group, translate["None"]));
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_joypad.create (group, translate["Joypad"]));
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_multitap.create(group, translate["Multitap"]));
|
||||
group.reset();
|
||||
|
||||
menu_file.attach(menu_file_sep3.create());
|
||||
menu_file.attach(menu_file_exit.create(translate["Exit"]));
|
||||
|
||||
attach(menu_settings.create(translate["Settings"]));
|
||||
@@ -206,8 +234,8 @@ void MainWindow::setup() {
|
||||
|
||||
menu_settings.attach(menu_settings_sep1.create());
|
||||
menu_settings.attach(menu_settings_mute.create(translate["Mute Audio Output"]));
|
||||
menu_settings.attach(menu_settings_sep2.create());
|
||||
|
||||
menu_settings.attach(menu_settings_sep2.create());
|
||||
menu_settings.attach(menu_settings_emuspeed.create(translate["Emulation Speed"]));
|
||||
group.add(&menu_settings_emuspeed_slowest);
|
||||
group.add(&menu_settings_emuspeed_slow);
|
||||
@@ -222,8 +250,6 @@ void MainWindow::setup() {
|
||||
menu_settings_emuspeed.attach(menu_settings_emuspeed_fastest.create(group, translate["200%"]));
|
||||
menu_settings_emuspeed.attach(menu_settings_emuspeed_disabled.create(group, translate["Uncapped"]));
|
||||
group.reset();
|
||||
|
||||
menu_settings.attach(menu_settings_sep3.create());
|
||||
menu_settings.attach(menu_settings_config.create(string() << translate["Configuration"] << " ..."));
|
||||
|
||||
attach(menu_misc.create(translate["Misc"]));
|
||||
@@ -252,6 +278,14 @@ void MainWindow::setup() {
|
||||
menu_file_reset.on_tick =
|
||||
menu_file_power.on_tick =
|
||||
|
||||
menu_system_controller_port1_none.on_tick =
|
||||
menu_system_controller_port1_joypad.on_tick =
|
||||
menu_system_controller_port1_multitap.on_tick =
|
||||
|
||||
menu_system_controller_port2_none.on_tick =
|
||||
menu_system_controller_port2_joypad.on_tick =
|
||||
menu_system_controller_port2_multitap.on_tick =
|
||||
|
||||
menu_settings_videomode_1x.on_tick =
|
||||
menu_settings_videomode_2x.on_tick =
|
||||
menu_settings_videomode_3x.on_tick =
|
||||
@@ -301,6 +335,18 @@ void MainWindow::setup() {
|
||||
void MainWindow::sync() {
|
||||
event::load_video_settings();
|
||||
|
||||
switch(config::snes.controller_port1) { default:
|
||||
case SNES::Input::DeviceNone: menu_system_controller_port1_none.check(); break;
|
||||
case SNES::Input::DeviceJoypad: menu_system_controller_port1_joypad.check(); break;
|
||||
case SNES::Input::DeviceMultitap: menu_system_controller_port1_multitap.check(); break;
|
||||
}
|
||||
|
||||
switch(config::snes.controller_port2) {
|
||||
case SNES::Input::DeviceNone: menu_system_controller_port2_none.check(); break;
|
||||
case SNES::Input::DeviceJoypad: menu_system_controller_port2_joypad.check(); break;
|
||||
case SNES::Input::DeviceMultitap: menu_system_controller_port2_multitap.check(); break;
|
||||
}
|
||||
|
||||
switch(event::video_settings.multiplier) { default:
|
||||
case 1: menu_settings_videomode_1x.check(); break;
|
||||
case 2: menu_settings_videomode_2x.check(); break;
|
||||
|
||||
+9
-1
@@ -11,6 +11,15 @@ public:
|
||||
MenuItem menu_file_reset;
|
||||
MenuItem menu_file_power;
|
||||
MenuSeparator menu_file_sep2;
|
||||
MenuGroup menu_system_controller_port1;
|
||||
MenuRadioItem menu_system_controller_port1_none;
|
||||
MenuRadioItem menu_system_controller_port1_joypad;
|
||||
MenuRadioItem menu_system_controller_port1_multitap;
|
||||
MenuGroup menu_system_controller_port2;
|
||||
MenuRadioItem menu_system_controller_port2_none;
|
||||
MenuRadioItem menu_system_controller_port2_joypad;
|
||||
MenuRadioItem menu_system_controller_port2_multitap;
|
||||
MenuSeparator menu_file_sep3;
|
||||
MenuItem menu_file_exit;
|
||||
|
||||
MenuGroup menu_settings;
|
||||
@@ -55,7 +64,6 @@ public:
|
||||
MenuRadioItem menu_settings_emuspeed_fast;
|
||||
MenuRadioItem menu_settings_emuspeed_fastest;
|
||||
MenuRadioItem menu_settings_emuspeed_disabled;
|
||||
MenuSeparator menu_settings_sep3;
|
||||
MenuItem menu_settings_config;
|
||||
|
||||
MenuGroup menu_misc;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
uintptr_t MessageWindow::close(Event) {
|
||||
hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageWindow::setup() {
|
||||
create(Window::AutoCenter, 400, 100, "");
|
||||
message.create(0, 390, 60, "");
|
||||
ok.create(0, 100, 25, translate["Ok"]);
|
||||
attach(message, 5, 5);
|
||||
attach(ok, 295, 70);
|
||||
|
||||
on_close = ok.on_tick = bind(&MessageWindow::close, this);
|
||||
}
|
||||
|
||||
void MessageWindow::show(const char *message_, const char *title_) {
|
||||
message.set_text(message_);
|
||||
set_text(translate[title_]);
|
||||
focus();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
class MessageWindow : public Window {
|
||||
public:
|
||||
Label message;
|
||||
Button ok;
|
||||
|
||||
void setup();
|
||||
void show(const char *message, const char *title = "Warning");
|
||||
uintptr_t close(Event);
|
||||
} window_message;
|
||||
+45
-14
@@ -105,14 +105,19 @@ integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", inte
|
||||
struct Input {
|
||||
static integral_setting capture_mode;
|
||||
static integral_setting allow_invalid_input;
|
||||
static integral_setting analog_axis_resistance;
|
||||
|
||||
struct Joypad1 {
|
||||
static string_setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad1;
|
||||
struct Joypad1 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad1;
|
||||
struct Joypad2 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad2;
|
||||
|
||||
struct Joypad2 {
|
||||
static string_setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad2;
|
||||
struct Multitap1A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1a;
|
||||
struct Multitap1B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1b;
|
||||
struct Multitap1C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1c;
|
||||
struct Multitap1D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1d;
|
||||
struct Multitap2A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2a;
|
||||
struct Multitap2B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2b;
|
||||
struct Multitap2C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2c;
|
||||
struct Multitap2D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2d;
|
||||
|
||||
struct GUI {
|
||||
static string_setting load;
|
||||
@@ -144,6 +149,15 @@ integral_setting Input::allow_invalid_input(config(), "input.allow_invalid_input
|
||||
"Enabling this option can trigger bugs in certain games.\n",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
integral_setting Input::analog_axis_resistance(config(), "input.analog_axis_resistance",
|
||||
"Resistance required to activate analog stick in any given direction.\n"
|
||||
"This value ranges from 1 (1%, virtually no resistance) to 99 (99%, near full resistance.)\n"
|
||||
"For instance, a value of 50 means that to register 'left', the analog stick must be moved\n"
|
||||
"50% between the center and the left.\n"
|
||||
"Less resistance allows for more fluid movement; whereas more resistance helps to prevent\n"
|
||||
"accidental movements, eg attempting to press up whilst bumping the stick slightly left.",
|
||||
integral_setting::decimal, 50);
|
||||
|
||||
string_setting Input::Joypad1::up (config(), "input.joypad1.up", "", "up");
|
||||
string_setting Input::Joypad1::down (config(), "input.joypad1.down", "", "down");
|
||||
string_setting Input::Joypad1::left (config(), "input.joypad1.left", "", "left");
|
||||
@@ -170,6 +184,31 @@ string_setting Input::Joypad2::r (config(), "input.joypad2.r", "", "l")
|
||||
string_setting Input::Joypad2::select(config(), "input.joypad2.select", "", "lbracket");
|
||||
string_setting Input::Joypad2::start (config(), "input.joypad2.start", "", "rbracket");
|
||||
|
||||
#define DeclMultitap(uname, lname) \
|
||||
string_setting Input::uname::up (config(), "input.multitap" lname ".up", "", "none"); \
|
||||
string_setting Input::uname::down (config(), "input.multitap" lname ".down", "", "none"); \
|
||||
string_setting Input::uname::left (config(), "input.multitap" lname ".left", "", "none"); \
|
||||
string_setting Input::uname::right (config(), "input.multitap" lname ".right", "", "none"); \
|
||||
string_setting Input::uname::a (config(), "input.multitap" lname ".a", "", "none"); \
|
||||
string_setting Input::uname::b (config(), "input.multitap" lname ".b", "", "none"); \
|
||||
string_setting Input::uname::x (config(), "input.multitap" lname ".x", "", "none"); \
|
||||
string_setting Input::uname::y (config(), "input.multitap" lname ".y", "", "none"); \
|
||||
string_setting Input::uname::l (config(), "input.multitap" lname ".l", "", "none"); \
|
||||
string_setting Input::uname::r (config(), "input.multitap" lname ".r", "", "none"); \
|
||||
string_setting Input::uname::select(config(), "input.multitap" lname ".select", "", "none"); \
|
||||
string_setting Input::uname::start (config(), "input.multitap" lname ".start", "", "none");
|
||||
|
||||
DeclMultitap(Multitap1A, "1a")
|
||||
DeclMultitap(Multitap1B, "1b")
|
||||
DeclMultitap(Multitap1C, "1c")
|
||||
DeclMultitap(Multitap1D, "1d")
|
||||
DeclMultitap(Multitap2A, "2a")
|
||||
DeclMultitap(Multitap2B, "2b")
|
||||
DeclMultitap(Multitap2C, "2c")
|
||||
DeclMultitap(Multitap2D, "2d")
|
||||
|
||||
#undef DeclMultitap
|
||||
|
||||
string_setting Input::GUI::load (config(), "input.gui.load", "", "none");
|
||||
string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12");
|
||||
string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none");
|
||||
@@ -186,18 +225,10 @@ string_setting Input::GUI::toggle_statusbar (config(), "input.gui.toggle_status
|
||||
struct Misc {
|
||||
static integral_setting opacity;
|
||||
static integral_setting status_enable;
|
||||
static string_setting status_text;
|
||||
} misc;
|
||||
|
||||
integral_setting Misc::opacity(config(), "misc.opacity", "Opacity of user interface windows", integral_setting::decimal, 100);
|
||||
integral_setting Misc::status_enable(config(), "misc.status_enable", "Display information statusbar", integral_setting::boolean, true);
|
||||
string_setting Misc::status_text(config(), "misc.status_text",
|
||||
"Text to print inside statusbar\n"
|
||||
"%n = cartridge file name\n"
|
||||
"%t = internal cartridge header name\n"
|
||||
"%f = executed frames per second\n"
|
||||
"%m = maximum frames per second"
|
||||
"", "%n : %f / %m");
|
||||
|
||||
struct Advanced {
|
||||
static integral_setting enable;
|
||||
|
||||
+68
-37
@@ -7,7 +7,7 @@ void keydown(uint16_t key) {
|
||||
app.pause = !app.pause; //toggle pause state
|
||||
if(app.pause) {
|
||||
audio.clear();
|
||||
if(cartridge.loaded()) update_status();
|
||||
if(cartridge.loaded()) status.update();
|
||||
}
|
||||
}
|
||||
if(key == input_manager.gui.reset) reset();
|
||||
@@ -138,31 +138,38 @@ void update_emulation_speed(int speed) {
|
||||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_status() {
|
||||
if(!cartridge.loaded()) {
|
||||
window_main.status.set_text("");
|
||||
} else if(app.pause || app.autopause) {
|
||||
window_main.status.set_text("(paused)");
|
||||
} else if(ppu.status.frames_updated) {
|
||||
ppu.status.frames_updated = false;
|
||||
void update_controller_port1(int device) {
|
||||
unsigned current_device = config::snes.controller_port1;
|
||||
unsigned new_device;
|
||||
|
||||
unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50;
|
||||
switch(config::system.emulation_speed) {
|
||||
case 0: max_framerate = unsigned(0.50 * max_framerate); break;
|
||||
case 1: max_framerate = unsigned(0.75 * max_framerate); break;
|
||||
case 2: break;
|
||||
case 3: max_framerate = unsigned(1.50 * max_framerate); break;
|
||||
case 4: max_framerate = unsigned(2.00 * max_framerate); break;
|
||||
case 5: max_framerate = 0; break;
|
||||
}
|
||||
|
||||
string output = (const char*)config::misc.status_text;
|
||||
replace(output, "%f", string() << (int)ppu.status.frames_executed);
|
||||
replace(output, "%m", string() << (int)max_framerate);
|
||||
replace(output, "%n", cartridge.info.filename);
|
||||
replace(output, "%t", cartridge.info.name);
|
||||
window_main.status.set_text(output);
|
||||
switch(device) { default:
|
||||
case 0: new_device = SNES::Input::DeviceNone; break;
|
||||
case 1: new_device = SNES::Input::DeviceJoypad; break;
|
||||
case 2: new_device = SNES::Input::DeviceMultitap; break;
|
||||
}
|
||||
|
||||
if(new_device != current_device) {
|
||||
snes.input.port_set_device(0, config::snes.controller_port1 = new_device);
|
||||
}
|
||||
|
||||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_controller_port2(int device) {
|
||||
unsigned current_device = config::snes.controller_port2;
|
||||
unsigned new_device;
|
||||
|
||||
switch(device) { default:
|
||||
case 0: new_device = SNES::Input::DeviceNone; break;
|
||||
case 1: new_device = SNES::Input::DeviceJoypad; break;
|
||||
case 2: new_device = SNES::Input::DeviceMultitap; break;
|
||||
}
|
||||
|
||||
if(new_device != current_device) {
|
||||
snes.input.port_set_device(1, config::snes.controller_port2 = new_device);
|
||||
}
|
||||
|
||||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_video_settings() {
|
||||
@@ -313,21 +320,25 @@ void load_cart_normal(const char *filename) {
|
||||
if(cartridge.loaded() == true) cartridge.unload();
|
||||
cartridge.load_cart_normal(filename);
|
||||
|
||||
//warn if unsupported hardware detected
|
||||
string message = translate["Unsupported $ chip detected."];
|
||||
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); }
|
||||
|
||||
app.pause = false;
|
||||
snes.power();
|
||||
window_main.menu_file_unload.enable();
|
||||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
if(cartridge.info.patched) status.enqueue(translate["UPS patch applied."]);
|
||||
|
||||
//warn if unsupported hardware detected
|
||||
string message = translate["Warning: unsupported $ chip detected."];
|
||||
if(cartridge.info.superfx) { replace(message, "$", "SuperFX"); status.enqueue(message); }
|
||||
if(cartridge.info.sa1) { replace(message, "$", "SA-1"); status.enqueue(message); }
|
||||
if(cartridge.info.st011) { replace(message, "$", "ST011"); status.enqueue(message); }
|
||||
if(cartridge.info.st018) { replace(message, "$", "ST018"); status.enqueue(message); }
|
||||
}
|
||||
|
||||
void load_cart_bsx(const char *base, const char *slot) {
|
||||
@@ -342,6 +353,11 @@ void load_cart_bsx(const char *base, const char *slot) {
|
||||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void load_cart_bsc(const char *base, const char *slot) {
|
||||
@@ -356,6 +372,11 @@ void load_cart_bsc(const char *base, const char *slot) {
|
||||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
@@ -370,6 +391,11 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void unload_rom() {
|
||||
@@ -378,31 +404,36 @@ void unload_rom() {
|
||||
video.clear();
|
||||
audio.clear();
|
||||
}
|
||||
event::update_status();
|
||||
window_main.menu_file_unload.disable();
|
||||
window_main.menu_file_reset.disable();
|
||||
window_main.menu_file_power.disable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Unloaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(cartridge.loaded() == true) {
|
||||
snes.reset();
|
||||
dprintf("* Reset");
|
||||
status.flush();
|
||||
status.enqueue(translate["Reset"]);
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
if(cartridge.loaded() == true) {
|
||||
snes.power();
|
||||
dprintf("* Power");
|
||||
status.flush();
|
||||
status.enqueue(translate["Power cycle"]);
|
||||
}
|
||||
}
|
||||
|
||||
void quit() {
|
||||
app.term = true;
|
||||
window_about.hide();
|
||||
window_message.hide();
|
||||
window_settings.hide();
|
||||
window_bsxloader.hide();
|
||||
window_stloader.hide();
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@ void update_software_filter(uint);
|
||||
void update_frameskip(int);
|
||||
void update_emulation_speed(int);
|
||||
|
||||
void update_status();
|
||||
void update_controller_port1(int);
|
||||
void update_controller_port2(int);
|
||||
|
||||
void update_video_settings();
|
||||
void update_opacity();
|
||||
void toggle_fullscreen();
|
||||
|
||||
+84
-95
@@ -1,11 +1,15 @@
|
||||
class InputManager {
|
||||
public:
|
||||
public:
|
||||
// 0 = Joypad 1
|
||||
// 1 = Joypad 2
|
||||
//2-5 = Multitap 1
|
||||
//6-9 = Multitap 2
|
||||
struct Joypad {
|
||||
struct Button {
|
||||
uint16_t value;
|
||||
bool state;
|
||||
} up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad1, joypad2;
|
||||
} joypad[10];
|
||||
|
||||
struct GUI {
|
||||
uint16_t load;
|
||||
@@ -24,7 +28,7 @@ public:
|
||||
|
||||
void bind();
|
||||
void poll();
|
||||
bool get_status(uint device, uint button);
|
||||
bool get_status(unsigned deviceid, unsigned id);
|
||||
|
||||
void refresh();
|
||||
function<void (uint16_t)> on_keydown;
|
||||
@@ -53,41 +57,39 @@ void InputManager::refresh() {
|
||||
}
|
||||
|
||||
void InputManager::bind() {
|
||||
joypad1.up.value = input_find(config::input.joypad1.up);
|
||||
joypad1.down.value = input_find(config::input.joypad1.down);
|
||||
joypad1.left.value = input_find(config::input.joypad1.left);
|
||||
joypad1.right.value = input_find(config::input.joypad1.right);
|
||||
joypad1.a.value = input_find(config::input.joypad1.a);
|
||||
joypad1.b.value = input_find(config::input.joypad1.b);
|
||||
joypad1.x.value = input_find(config::input.joypad1.x);
|
||||
joypad1.y.value = input_find(config::input.joypad1.y);
|
||||
joypad1.l.value = input_find(config::input.joypad1.l);
|
||||
joypad1.r.value = input_find(config::input.joypad1.r);
|
||||
joypad1.select.value = input_find(config::input.joypad1.select);
|
||||
joypad1.start.value = input_find(config::input.joypad1.start);
|
||||
|
||||
joypad1.up.state = joypad1.down.state = joypad1.left.state = joypad1.right.state =
|
||||
joypad1.a.state = joypad1.b.state = joypad1.x.state = joypad1.y.state =
|
||||
joypad1.l.state = joypad1.r.state = joypad1.select.state = joypad1.start.state =
|
||||
false;
|
||||
#define map(i, n) \
|
||||
joypad[i].up.value = input_find(config::input.n.up); \
|
||||
joypad[i].down.value = input_find(config::input.n.down); \
|
||||
joypad[i].left.value = input_find(config::input.n.left); \
|
||||
joypad[i].right.value = input_find(config::input.n.right); \
|
||||
joypad[i].a.value = input_find(config::input.n.a); \
|
||||
joypad[i].b.value = input_find(config::input.n.b); \
|
||||
joypad[i].x.value = input_find(config::input.n.x); \
|
||||
joypad[i].y.value = input_find(config::input.n.y); \
|
||||
joypad[i].l.value = input_find(config::input.n.l); \
|
||||
joypad[i].r.value = input_find(config::input.n.r); \
|
||||
joypad[i].select.value = input_find(config::input.n.select); \
|
||||
joypad[i].start.value = input_find(config::input.n.start);
|
||||
|
||||
joypad2.up.value = input_find(config::input.joypad2.up);
|
||||
joypad2.down.value = input_find(config::input.joypad2.down);
|
||||
joypad2.left.value = input_find(config::input.joypad2.left);
|
||||
joypad2.right.value = input_find(config::input.joypad2.right);
|
||||
joypad2.a.value = input_find(config::input.joypad2.a);
|
||||
joypad2.b.value = input_find(config::input.joypad2.b);
|
||||
joypad2.x.value = input_find(config::input.joypad2.x);
|
||||
joypad2.y.value = input_find(config::input.joypad2.y);
|
||||
joypad2.l.value = input_find(config::input.joypad2.l);
|
||||
joypad2.r.value = input_find(config::input.joypad2.r);
|
||||
joypad2.select.value = input_find(config::input.joypad2.select);
|
||||
joypad2.start.value = input_find(config::input.joypad2.start);
|
||||
map(0, joypad1)
|
||||
map(1, joypad2)
|
||||
map(2, multitap1a)
|
||||
map(3, multitap1b)
|
||||
map(4, multitap1c)
|
||||
map(5, multitap1d)
|
||||
map(6, multitap2a)
|
||||
map(7, multitap2b)
|
||||
map(8, multitap2c)
|
||||
map(9, multitap2d)
|
||||
|
||||
joypad2.up.state = joypad2.down.state = joypad2.left.state = joypad2.right.state =
|
||||
joypad2.a.state = joypad2.b.state = joypad2.x.state = joypad2.y.state =
|
||||
joypad2.l.state = joypad2.r.state = joypad2.select.state = joypad2.start.state =
|
||||
false;
|
||||
#undef map
|
||||
|
||||
for(unsigned i = 0; i < 10; i++) {
|
||||
joypad[i].up.state = joypad[i].down.state = joypad[i].left.state = joypad[i].right.state =
|
||||
joypad[i].a.state = joypad[i].b.state = joypad[i].x.state = joypad[i].y.state =
|
||||
joypad[i].l.state = joypad[i].r.state = joypad[i].select.state = joypad[i].start.state =
|
||||
false;
|
||||
}
|
||||
|
||||
gui.load = input_find(config::input.gui.load);
|
||||
gui.pause = input_find(config::input.gui.pause);
|
||||
@@ -104,68 +106,55 @@ void InputManager::bind() {
|
||||
}
|
||||
|
||||
void InputManager::poll() {
|
||||
joypad1.up.state = input.key_down(joypad1.up.value);
|
||||
joypad1.down.state = input.key_down(joypad1.down.value);
|
||||
joypad1.left.state = input.key_down(joypad1.left.value);
|
||||
joypad1.right.state = input.key_down(joypad1.right.value);
|
||||
joypad1.a.state = input.key_down(joypad1.a.value);
|
||||
joypad1.b.state = input.key_down(joypad1.b.value);
|
||||
joypad1.x.state = input.key_down(joypad1.x.value);
|
||||
joypad1.y.state = input.key_down(joypad1.y.value);
|
||||
joypad1.l.state = input.key_down(joypad1.l.value);
|
||||
joypad1.r.state = input.key_down(joypad1.r.value);
|
||||
joypad1.select.state = input.key_down(joypad1.select.value);
|
||||
joypad1.start.state = input.key_down(joypad1.start.value);
|
||||
|
||||
joypad2.up.state = input.key_down(joypad2.up.value);
|
||||
joypad2.down.state = input.key_down(joypad2.down.value);
|
||||
joypad2.left.state = input.key_down(joypad2.left.value);
|
||||
joypad2.right.state = input.key_down(joypad2.right.value);
|
||||
joypad2.a.state = input.key_down(joypad2.a.value);
|
||||
joypad2.b.state = input.key_down(joypad2.b.value);
|
||||
joypad2.x.state = input.key_down(joypad2.x.value);
|
||||
joypad2.y.state = input.key_down(joypad2.y.value);
|
||||
joypad2.l.state = input.key_down(joypad2.l.value);
|
||||
joypad2.r.state = input.key_down(joypad2.r.value);
|
||||
joypad2.select.state = input.key_down(joypad2.select.value);
|
||||
joypad2.start.state = input.key_down(joypad2.start.value);
|
||||
for(unsigned i = 0; i < 10; i++) {
|
||||
joypad[i].up.state = input.key_down(joypad[i].up.value);
|
||||
joypad[i].down.state = input.key_down(joypad[i].down.value);
|
||||
joypad[i].left.state = input.key_down(joypad[i].left.value);
|
||||
joypad[i].right.state = input.key_down(joypad[i].right.value);
|
||||
joypad[i].a.state = input.key_down(joypad[i].a.value);
|
||||
joypad[i].b.state = input.key_down(joypad[i].b.value);
|
||||
joypad[i].x.state = input.key_down(joypad[i].x.value);
|
||||
joypad[i].y.state = input.key_down(joypad[i].y.value);
|
||||
joypad[i].l.state = input.key_down(joypad[i].l.value);
|
||||
joypad[i].r.state = input.key_down(joypad[i].r.value);
|
||||
joypad[i].select.state = input.key_down(joypad[i].select.value);
|
||||
joypad[i].start.state = input.key_down(joypad[i].start.value);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::get_status(uint device, uint button) {
|
||||
switch(device) {
|
||||
case SNES::Input::DeviceIDJoypad1: {
|
||||
switch(button) {
|
||||
case SNES::Input::JoypadUp: return joypad1.up.state;
|
||||
case SNES::Input::JoypadDown: return joypad1.down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad1.left.state;
|
||||
case SNES::Input::JoypadRight: return joypad1.right.state;
|
||||
case SNES::Input::JoypadA: return joypad1.a.state;
|
||||
case SNES::Input::JoypadB: return joypad1.b.state;
|
||||
case SNES::Input::JoypadX: return joypad1.x.state;
|
||||
case SNES::Input::JoypadY: return joypad1.y.state;
|
||||
case SNES::Input::JoypadL: return joypad1.l.state;
|
||||
case SNES::Input::JoypadR: return joypad1.r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad1.select.state;
|
||||
case SNES::Input::JoypadStart: return joypad1.start.state;
|
||||
}
|
||||
} break;
|
||||
bool InputManager::get_status(unsigned deviceid, unsigned id) {
|
||||
//=======
|
||||
//Joypads
|
||||
//=======
|
||||
int index = -1;
|
||||
switch(deviceid) {
|
||||
case SNES::Input::DeviceIDJoypad1: index = 0; break;
|
||||
case SNES::Input::DeviceIDJoypad2: index = 1; break;
|
||||
case SNES::Input::DeviceIDMultitap1A: index = 2; break;
|
||||
case SNES::Input::DeviceIDMultitap1B: index = 3; break;
|
||||
case SNES::Input::DeviceIDMultitap1C: index = 4; break;
|
||||
case SNES::Input::DeviceIDMultitap1D: index = 5; break;
|
||||
case SNES::Input::DeviceIDMultitap2A: index = 6; break;
|
||||
case SNES::Input::DeviceIDMultitap2B: index = 7; break;
|
||||
case SNES::Input::DeviceIDMultitap2C: index = 8; break;
|
||||
case SNES::Input::DeviceIDMultitap2D: index = 9; break;
|
||||
}
|
||||
|
||||
case SNES::Input::DeviceIDJoypad2: {
|
||||
switch(button) {
|
||||
case SNES::Input::JoypadUp: return joypad2.up.state;
|
||||
case SNES::Input::JoypadDown: return joypad2.down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad2.left.state;
|
||||
case SNES::Input::JoypadRight: return joypad2.right.state;
|
||||
case SNES::Input::JoypadA: return joypad2.a.state;
|
||||
case SNES::Input::JoypadB: return joypad2.b.state;
|
||||
case SNES::Input::JoypadX: return joypad2.x.state;
|
||||
case SNES::Input::JoypadY: return joypad2.y.state;
|
||||
case SNES::Input::JoypadL: return joypad2.l.state;
|
||||
case SNES::Input::JoypadR: return joypad2.r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad2.select.state;
|
||||
case SNES::Input::JoypadStart: return joypad2.start.state;
|
||||
}
|
||||
} break;
|
||||
if(index >= 0) {
|
||||
switch(id) {
|
||||
case SNES::Input::JoypadUp: return joypad[index].up.state;
|
||||
case SNES::Input::JoypadDown: return joypad[index].down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad[index].left.state;
|
||||
case SNES::Input::JoypadRight: return joypad[index].right.state;
|
||||
case SNES::Input::JoypadA: return joypad[index].a.state;
|
||||
case SNES::Input::JoypadB: return joypad[index].b.state;
|
||||
case SNES::Input::JoypadX: return joypad[index].x.state;
|
||||
case SNES::Input::JoypadY: return joypad[index].y.state;
|
||||
case SNES::Input::JoypadL: return joypad[index].l.state;
|
||||
case SNES::Input::JoypadR: return joypad[index].r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad[index].select.state;
|
||||
case SNES::Input::JoypadStart: return joypad[index].start.state;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -49,8 +49,8 @@ void SNESInterface::input_poll() {
|
||||
input_manager.poll();
|
||||
}
|
||||
|
||||
bool SNESInterface::input_poll(uint deviceid, uint button) {
|
||||
return input_manager.get_status(deviceid, button);
|
||||
bool SNESInterface::input_poll(unsigned deviceid, unsigned id) {
|
||||
return input_manager.get_status(deviceid, id);
|
||||
}
|
||||
|
||||
//core
|
||||
|
||||
+4
-14
@@ -32,9 +32,11 @@ using namespace libhiro;
|
||||
*****/
|
||||
|
||||
#include "ui.h"
|
||||
#include "status.h"
|
||||
#include "event.h"
|
||||
|
||||
#include "ui.cpp"
|
||||
#include "status.cpp"
|
||||
#include "event.cpp"
|
||||
|
||||
void alert(const char *s, ...) {
|
||||
@@ -44,7 +46,7 @@ void alert(const char *s, ...) {
|
||||
vsprintf(str, s, args);
|
||||
va_end(args);
|
||||
|
||||
window_message.show(str);
|
||||
status.enqueue(str);
|
||||
}
|
||||
|
||||
void dprintf(const char *s, ...) {
|
||||
@@ -56,15 +58,6 @@ void dprintf(const char *s, ...) {
|
||||
fprintf(stdout, "%s\r\n", str);
|
||||
}
|
||||
|
||||
void dprintf(uint source, const char *s, ...) {
|
||||
char str[4096];
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
vsprintf(str, s, args);
|
||||
va_end(args);
|
||||
fprintf(stdout, "[%d]: %s\r\n", source, str);
|
||||
}
|
||||
|
||||
void get_paths(const char *image) {
|
||||
char temp[PATH_MAX] = "";
|
||||
realpath(image, temp);
|
||||
@@ -103,7 +96,6 @@ void set_config_filenames() {
|
||||
strcat(filename, "/bsnes.cfg");
|
||||
}
|
||||
strcpy(config::bsnes_cfg, filename);
|
||||
fprintf(stdout, "Config file: %s\n", config::bsnes_cfg);
|
||||
|
||||
//locate locale.cfg
|
||||
strcpy(filename, config::path.base);
|
||||
@@ -115,11 +107,11 @@ void set_config_filenames() {
|
||||
strcat(filename, "/locale.cfg");
|
||||
}
|
||||
strcpy(config::locale_cfg, filename);
|
||||
fprintf(stdout, "Locale file: %s\n", config::locale_cfg);
|
||||
}
|
||||
|
||||
void run() {
|
||||
while(hiro().pending()) hiro().run();
|
||||
status.update();
|
||||
input_manager.refresh();
|
||||
|
||||
if(config::input.capture_mode == 2) {
|
||||
@@ -127,7 +119,6 @@ void run() {
|
||||
if(app.autopause == false && inactive == true) {
|
||||
app.autopause = true;
|
||||
audio.clear();
|
||||
if(cartridge.loaded()) event::update_status();
|
||||
} else if(app.autopause == true && inactive == false) {
|
||||
app.autopause = false;
|
||||
}
|
||||
@@ -140,7 +131,6 @@ void run() {
|
||||
usleep(20 * 1000);
|
||||
} else {
|
||||
snes.runtoframe();
|
||||
event::update_status();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,8 @@ void AdvancedWindow::load() {
|
||||
string name = config::config().list[i]->name;
|
||||
|
||||
//blacklist (omit/hide options that can be configured through the standard UI)
|
||||
if(strbegin(name, "file.")) continue;
|
||||
if(strbegin(name, "path.")) continue;
|
||||
if(strbegin(name, "snes.controller_port_")) continue;
|
||||
if(strbegin(name, "snes.controller_port")) continue;
|
||||
if(strpos(name, "colorfilter.") >= 0) continue;
|
||||
if(name == "misc.status_enable") continue;
|
||||
if(name == "system.emulation_speed") continue;
|
||||
@@ -66,6 +65,7 @@ void AdvancedWindow::load() {
|
||||
if(name == "audio.mute") continue;
|
||||
if(name == "input.capture_mode") continue;
|
||||
if(strbegin(name, "input.joypad")) continue;
|
||||
if(strbegin(name, "input.multitap")) continue;
|
||||
if(strbegin(name, "input.gui")) continue;
|
||||
|
||||
string value_, default_;
|
||||
|
||||
+150
-59
@@ -12,43 +12,114 @@ void InputConfigWindow::setup() {
|
||||
capture_focus.create (group, 0, 155, 18, translate["Ignore input"]);
|
||||
capture_pause.create (group, 0, 155, 18, translate["Pause emulation"]);
|
||||
|
||||
list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 284, string() << translate["Name"] << "\t" << translate["Value"]);
|
||||
config_type.create(0, 235, 25);
|
||||
config_type.add_item(translate["Controller Port 1"]);
|
||||
config_type.add_item(translate["Controller Port 2"]);
|
||||
config_type.add_item(translate["User Interface"]);
|
||||
config_type.set_selection(0);
|
||||
|
||||
config_subtype.create(0, 235, 25);
|
||||
refresh_subtype();
|
||||
|
||||
list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 254, string() << translate["Name"] << "\t" << translate["Value"]);
|
||||
setkey.create(0, 235, 25, translate["Assign Key"]);
|
||||
setkey.disable();
|
||||
clrkey.create(0, 235, 25, translate["Unassign Key"]);
|
||||
clrkey.disable();
|
||||
|
||||
unsigned y = 0;
|
||||
attach(capture_mode, 0, y); y += 18;
|
||||
attach(capture_always, 0, y);
|
||||
attach(capture_focus, 160, y);
|
||||
attach(capture_pause, 320, y); y += 18 + 5;
|
||||
attach(list, 0, y); y += 284 + 5;
|
||||
attach(setkey, 0, y);
|
||||
attach(clrkey, 240, y); y += 25 + 5;
|
||||
attach(capture_mode, 0, y); y += 18;
|
||||
attach(capture_always, 0, y);
|
||||
attach(capture_focus, 160, y);
|
||||
attach(capture_pause, 320, y); y += 18 + 5;
|
||||
attach(config_type, 0, y);
|
||||
attach(config_subtype, 240, y); y += 25 + 5;
|
||||
attach(list, 0, y); y += 254 + 5;
|
||||
attach(setkey, 0, y);
|
||||
attach(clrkey, 240, y); y += 25 + 5;
|
||||
|
||||
capture_always.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_focus.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_pause.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
list.on_change = bind(&InputConfigWindow::list_change, this);
|
||||
list.on_activate = bind(&InputConfigWindow::set_tick, this);
|
||||
setkey.on_tick = bind(&InputConfigWindow::set_tick, this);
|
||||
clrkey.on_tick = bind(&InputConfigWindow::clr_tick, this);
|
||||
capture_always.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_focus.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_pause.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
config_type.on_change = bind(&InputConfigWindow::type_change, this);
|
||||
config_subtype.on_change = bind(&InputConfigWindow::subtype_change, this);
|
||||
list.on_change = bind(&InputConfigWindow::list_change, this);
|
||||
list.on_activate = bind(&InputConfigWindow::set_tick, this);
|
||||
setkey.on_tick = bind(&InputConfigWindow::set_tick, this);
|
||||
clrkey.on_tick = bind(&InputConfigWindow::clr_tick, this);
|
||||
|
||||
if(config::input.capture_mode == 1) capture_focus.check();
|
||||
else if(config::input.capture_mode == 2) capture_pause.check();
|
||||
else config::input.capture_mode = 0; //capture_always
|
||||
|
||||
for(unsigned i = 0; i < inputcount; i++) list.add_item("???\t???");
|
||||
refresh_list();
|
||||
window_input_capture.setup();
|
||||
}
|
||||
|
||||
InputConfigWindow::InputType InputConfigWindow::get_input_type(unsigned &length) {
|
||||
unsigned type = config_type.get_selection();
|
||||
unsigned subtype = config_subtype.get_selection();
|
||||
|
||||
switch(type) {
|
||||
case 0: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return Port1_Joypad;
|
||||
case 1: length = 12; return Port1_Multitap1;
|
||||
case 2: length = 12; return Port1_Multitap2;
|
||||
case 3: length = 12; return Port1_Multitap3;
|
||||
case 4: length = 12; return Port1_Multitap4;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return Port2_Joypad;
|
||||
case 1: length = 12; return Port2_Multitap1;
|
||||
case 2: length = 12; return Port2_Multitap2;
|
||||
case 3: length = 12; return Port2_Multitap3;
|
||||
case 4: length = 12; return Port2_Multitap4;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return UI_General;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return TypeUnknown;
|
||||
}
|
||||
|
||||
void InputConfigWindow::refresh_subtype() {
|
||||
config_subtype.reset();
|
||||
|
||||
switch(config_type.get_selection()) {
|
||||
case 0:
|
||||
case 1: {
|
||||
config_subtype.add_item(translate["Joypad"]);
|
||||
config_subtype.add_item(translate["Multitap Port 1"]);
|
||||
config_subtype.add_item(translate["Multitap Port 2"]);
|
||||
config_subtype.add_item(translate["Multitap Port 3"]);
|
||||
config_subtype.add_item(translate["Multitap Port 4"]);
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
config_subtype.add_item(translate["General"]);
|
||||
} break;
|
||||
}
|
||||
|
||||
config_subtype.set_selection(0);
|
||||
}
|
||||
|
||||
void InputConfigWindow::refresh_list() {
|
||||
for(unsigned i = 0; i < inputcount; i++) {
|
||||
list.reset();
|
||||
unsigned length;
|
||||
get_input_type(length);
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
const char *name;
|
||||
acquire(i, name);
|
||||
list.set_item(i, string() << name << "\t" << input_find(get_value(i)));
|
||||
list.add_item(string() << name << "\t" << input_find(get_value(i)));
|
||||
}
|
||||
list.autosize_columns();
|
||||
}
|
||||
@@ -60,6 +131,17 @@ uintptr_t InputConfigWindow::capture_change(Event e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::type_change(Event) {
|
||||
refresh_subtype();
|
||||
refresh_list();
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::subtype_change(Event) {
|
||||
refresh_list();
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::list_change(Event) {
|
||||
int pos = list.get_selection();
|
||||
setkey.enable(pos >= 0);
|
||||
@@ -69,14 +151,14 @@ uintptr_t InputConfigWindow::list_change(Event) {
|
||||
|
||||
uintptr_t InputConfigWindow::set_tick(Event) {
|
||||
int pos = list.get_selection();
|
||||
if(pos < 0 || pos >= inputcount) return true;
|
||||
if(pos < 0) return true;
|
||||
window_input_capture.index = pos;
|
||||
string message = translate["Press a key to assign to $ ..."];
|
||||
const char *name;
|
||||
acquire(pos, name);
|
||||
replace(message, "$", name);
|
||||
window_input_capture.label.set_text(message);
|
||||
window_input_capture.canvas.show(pos < 24); //only show joypad graphic if setting joypad 1/2 button
|
||||
window_input_capture.canvas.show(config_type.get_selection() < 2); //only show joypad graphic if setting joypad button
|
||||
window_input_capture.show();
|
||||
return true;
|
||||
}
|
||||
@@ -130,54 +212,63 @@ InputCaptureWindow::InputCaptureWindow() {
|
||||
/* Misc */
|
||||
|
||||
string_setting& InputConfigWindow::acquire(unsigned index, const char *&name) {
|
||||
switch(index) {
|
||||
case 0: name = translate["Joypad 1 Up"]; return config::input.joypad1.up;
|
||||
case 1: name = translate["Joypad 1 Down"]; return config::input.joypad1.down;
|
||||
case 2: name = translate["Joypad 1 Left"]; return config::input.joypad1.left;
|
||||
case 3: name = translate["Joypad 1 Right"]; return config::input.joypad1.right;
|
||||
case 4: name = translate["Joypad 1 A"]; return config::input.joypad1.a;
|
||||
case 5: name = translate["Joypad 1 B"]; return config::input.joypad1.b;
|
||||
case 6: name = translate["Joypad 1 X"]; return config::input.joypad1.x;
|
||||
case 7: name = translate["Joypad 1 Y"]; return config::input.joypad1.y;
|
||||
case 8: name = translate["Joypad 1 L"]; return config::input.joypad1.l;
|
||||
case 9: name = translate["Joypad 1 R"]; return config::input.joypad1.r;
|
||||
case 10: name = translate["Joypad 1 Select"]; return config::input.joypad1.select;
|
||||
case 11: name = translate["Joypad 1 Start"]; return config::input.joypad1.start;
|
||||
#define map(n, lname) \
|
||||
case n: { \
|
||||
switch(index) { \
|
||||
case 0: name = translate["Up"]; return config::input.lname.up; \
|
||||
case 1: name = translate["Down"]; return config::input.lname.down; \
|
||||
case 2: name = translate["Left"]; return config::input.lname.left; \
|
||||
case 3: name = translate["Right"]; return config::input.lname.right; \
|
||||
case 4: name = translate["A"]; return config::input.lname.a; \
|
||||
case 5: name = translate["B"]; return config::input.lname.b; \
|
||||
case 6: name = translate["X"]; return config::input.lname.x; \
|
||||
case 7: name = translate["Y"]; return config::input.lname.y; \
|
||||
case 8: name = translate["L"]; return config::input.lname.l; \
|
||||
case 9: name = translate["R"]; return config::input.lname.r; \
|
||||
case 10: name = translate["Select"]; return config::input.lname.select; \
|
||||
case 11: name = translate["Start"]; return config::input.lname.start; \
|
||||
} \
|
||||
} break;
|
||||
|
||||
case 12: name = translate["Joypad 2 Up"]; return config::input.joypad2.up;
|
||||
case 13: name = translate["Joypad 2 Down"]; return config::input.joypad2.down;
|
||||
case 14: name = translate["Joypad 2 Left"]; return config::input.joypad2.left;
|
||||
case 15: name = translate["Joypad 2 Right"]; return config::input.joypad2.right;
|
||||
case 16: name = translate["Joypad 2 A"]; return config::input.joypad2.a;
|
||||
case 17: name = translate["Joypad 2 B"]; return config::input.joypad2.b;
|
||||
case 18: name = translate["Joypad 2 X"]; return config::input.joypad2.x;
|
||||
case 19: name = translate["Joypad 2 Y"]; return config::input.joypad2.y;
|
||||
case 20: name = translate["Joypad 2 L"]; return config::input.joypad2.l;
|
||||
case 21: name = translate["Joypad 2 R"]; return config::input.joypad2.r;
|
||||
case 22: name = translate["Joypad 2 Select"]; return config::input.joypad2.select;
|
||||
case 23: name = translate["Joypad 2 Start"]; return config::input.joypad2.start;
|
||||
unsigned length;
|
||||
switch(get_input_type(length)) { default:
|
||||
map(Port1_Joypad, joypad1)
|
||||
map(Port1_Multitap1, multitap1a)
|
||||
map(Port1_Multitap2, multitap1b)
|
||||
map(Port1_Multitap3, multitap1c)
|
||||
map(Port1_Multitap4, multitap1d)
|
||||
|
||||
case 24: name = translate["Load Cartridge"]; return config::input.gui.load;
|
||||
case 25: name = translate["Pause Emulation"]; return config::input.gui.pause;
|
||||
case 26: name = translate["Reset System"]; return config::input.gui.reset;
|
||||
case 27: name = translate["Power Cycle System"]; return config::input.gui.power;
|
||||
case 28: name = translate["Exit Emulator"]; return config::input.gui.quit;
|
||||
case 29: name = translate["Emulation Speed Decrease"]; return config::input.gui.speed_decrease;
|
||||
case 30: name = translate["Emulation Speed Increase"]; return config::input.gui.speed_increase;
|
||||
case 31: name = translate["Frameskip Decrease"]; return config::input.gui.frameskip_decrease;
|
||||
case 32: name = translate["Frameskip Increase"]; return config::input.gui.frameskip_increase;
|
||||
case 33: name = translate["Toggle Fullscreen"]; return config::input.gui.toggle_fullscreen;
|
||||
case 34: name = translate["Toggle Menubar"]; return config::input.gui.toggle_menubar;
|
||||
case 35: name = translate["Toggle Statusbar"]; return config::input.gui.toggle_statusbar;
|
||||
map(Port2_Joypad, joypad2)
|
||||
map(Port2_Multitap1, multitap2a)
|
||||
map(Port2_Multitap2, multitap2b)
|
||||
map(Port2_Multitap3, multitap2c)
|
||||
map(Port2_Multitap4, multitap2d)
|
||||
|
||||
case UI_General: {
|
||||
switch(index) {
|
||||
case 0: name = translate["Load Cartridge"]; return config::input.gui.load;
|
||||
case 1: name = translate["Pause Emulation"]; return config::input.gui.pause;
|
||||
case 2: name = translate["Reset System"]; return config::input.gui.reset;
|
||||
case 3: name = translate["Power Cycle System"]; return config::input.gui.power;
|
||||
case 4: name = translate["Exit Emulator"]; return config::input.gui.quit;
|
||||
case 5: name = translate["Emulation Speed Decrease"]; return config::input.gui.speed_decrease;
|
||||
case 6: name = translate["Emulation Speed Increase"]; return config::input.gui.speed_increase;
|
||||
case 7: name = translate["Frameskip Decrease"]; return config::input.gui.frameskip_decrease;
|
||||
case 8: name = translate["Frameskip Increase"]; return config::input.gui.frameskip_increase;
|
||||
case 9: name = translate["Toggle Fullscreen"]; return config::input.gui.toggle_fullscreen;
|
||||
case 10: name = translate["Toggle Menubar"]; return config::input.gui.toggle_menubar;
|
||||
case 11: name = translate["Toggle Statusbar"]; return config::input.gui.toggle_statusbar;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
#undef map
|
||||
|
||||
name = "";
|
||||
static string_setting notfound("", "", "");
|
||||
return notfound;
|
||||
}
|
||||
|
||||
const int InputConfigWindow::inputcount = 36;
|
||||
|
||||
uint InputConfigWindow::get_value(uint index) {
|
||||
const char *name;
|
||||
return input_find(acquire(index, name));
|
||||
|
||||
@@ -4,20 +4,44 @@ public:
|
||||
Radiobox capture_always;
|
||||
Radiobox capture_focus;
|
||||
Radiobox capture_pause;
|
||||
Combobox config_type;
|
||||
Combobox config_subtype;
|
||||
Listbox list;
|
||||
Button setkey;
|
||||
Button clrkey;
|
||||
|
||||
void setup();
|
||||
void refresh_subtype();
|
||||
void refresh_list();
|
||||
|
||||
enum InputType {
|
||||
TypeUnknown,
|
||||
|
||||
Port1_Joypad,
|
||||
Port1_Multitap1,
|
||||
Port1_Multitap2,
|
||||
Port1_Multitap3,
|
||||
Port1_Multitap4,
|
||||
|
||||
Port2_Joypad,
|
||||
Port2_Multitap1,
|
||||
Port2_Multitap2,
|
||||
Port2_Multitap3,
|
||||
Port2_Multitap4,
|
||||
|
||||
UI_General,
|
||||
};
|
||||
|
||||
InputType get_input_type(unsigned &length);
|
||||
|
||||
uintptr_t capture_change(Event);
|
||||
uintptr_t type_change(Event);
|
||||
uintptr_t subtype_change(Event);
|
||||
uintptr_t list_change(Event);
|
||||
uintptr_t set_tick(Event);
|
||||
uintptr_t clr_tick(Event);
|
||||
|
||||
string_setting& acquire(unsigned index, const char *&name);
|
||||
static const int inputcount;
|
||||
string_setting& acquire(unsigned index, const char *&name);
|
||||
uint get_value(uint index);
|
||||
void set_value(uint index, uint16 value);
|
||||
} window_input_config;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//========
|
||||
//ROM path
|
||||
//========
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_rom(Event) {
|
||||
char t[PATH_MAX];
|
||||
if(hiro().folder_select(&window_settings, t) == true) {
|
||||
@@ -13,10 +17,9 @@ uintptr_t PathSettingsWindow::defaultpath_rom(Event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t PathSettingsWindow::autodetect_tick(Event) {
|
||||
config::file.autodetect_type = autodetect.checked();
|
||||
return true;
|
||||
}
|
||||
//==============
|
||||
//UPS patch path
|
||||
//==============
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_patch(Event) {
|
||||
char t[PATH_MAX];
|
||||
@@ -33,10 +36,9 @@ uintptr_t PathSettingsWindow::defaultpath_patch(Event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t PathSettingsWindow::bypass_crc32_tick(Event) {
|
||||
config::file.bypass_patch_crc32 = bypass_crc32.checked();
|
||||
return true;
|
||||
}
|
||||
//=============
|
||||
//save RAM path
|
||||
//=============
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_save(Event) {
|
||||
char t[PATH_MAX];
|
||||
@@ -53,6 +55,10 @@ uintptr_t PathSettingsWindow::defaultpath_save(Event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========
|
||||
//cheat path
|
||||
//==========
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_cheat(Event) {
|
||||
char t[PATH_MAX];
|
||||
if(hiro().folder_select(&window_settings, t) == true) {
|
||||
@@ -75,13 +81,11 @@ void PathSettingsWindow::setup() {
|
||||
rompath.create(Editbox::Readonly, 265, 25);
|
||||
romselect.create(0, 100, 25, translate["Select"]);
|
||||
romdefault.create(0, 100, 25, translate["Default"]);
|
||||
autodetect.create(0, 475, 18, translate["Auto-detect file compression type (ignore file extension)"]);
|
||||
|
||||
lpatchpath.create(0, 475, 18, translate["Default UPS patch path:"]);
|
||||
patchpath.create(Editbox::Readonly, 265, 25);
|
||||
patchselect.create(0, 100, 25, translate["Select"]);
|
||||
patchdefault.create(0, 100, 25, translate["Default"]);
|
||||
bypass_crc32.create(0, 475, 18, translate["Bypass CRC32 patch validation (not recommended)"]);
|
||||
|
||||
lsavepath.create(0, 475, 18, translate["Default save RAM path:"]);
|
||||
savepath.create(Editbox::Readonly, 265, 25);
|
||||
@@ -97,14 +101,12 @@ void PathSettingsWindow::setup() {
|
||||
attach(lrompath, 0, y); y += 18;
|
||||
attach(rompath, 0, y);
|
||||
attach(romselect, 270, y);
|
||||
attach(romdefault, 375, y); y += 25;
|
||||
attach(autodetect, 0, y); y += 18 + 5;
|
||||
attach(romdefault, 375, y); y += 25 + 5;
|
||||
|
||||
attach(lpatchpath, 0, y); y += 18;
|
||||
attach(patchpath, 0, y);
|
||||
attach(patchselect, 270, y);
|
||||
attach(patchdefault, 375, y); y += 25;
|
||||
attach(bypass_crc32, 0, y); y += 18 + 5;
|
||||
attach(patchdefault, 375, y); y += 25 + 5;
|
||||
|
||||
attach(lsavepath, 0, y); y += 18;
|
||||
attach(savepath, 0, y);
|
||||
@@ -118,11 +120,9 @@ void PathSettingsWindow::setup() {
|
||||
|
||||
romselect.on_tick = bind(&PathSettingsWindow::selectpath_rom, this);
|
||||
romdefault.on_tick = bind(&PathSettingsWindow::defaultpath_rom, this);
|
||||
autodetect.on_tick = bind(&PathSettingsWindow::autodetect_tick, this);
|
||||
|
||||
patchselect.on_tick = bind(&PathSettingsWindow::selectpath_patch, this);
|
||||
patchdefault.on_tick = bind(&PathSettingsWindow::defaultpath_patch, this);
|
||||
bypass_crc32.on_tick = bind(&PathSettingsWindow::bypass_crc32_tick, this);
|
||||
|
||||
saveselect.on_tick = bind(&PathSettingsWindow::selectpath_save, this);
|
||||
savedefault.on_tick = bind(&PathSettingsWindow::defaultpath_save, this);
|
||||
@@ -131,9 +131,7 @@ void PathSettingsWindow::setup() {
|
||||
cheatdefault.on_tick = bind(&PathSettingsWindow::defaultpath_cheat, this);
|
||||
|
||||
rompath.set_text(config::path.rom);
|
||||
autodetect.check(config::file.autodetect_type);
|
||||
patchpath.set_text(config::path.patch);
|
||||
bypass_crc32.check(config::file.bypass_patch_crc32);
|
||||
savepath.set_text(config::path.save);
|
||||
cheatpath.set_text(config::path.cheat);
|
||||
}
|
||||
@@ -4,13 +4,11 @@ public:
|
||||
Editbox rompath;
|
||||
Button romselect;
|
||||
Button romdefault;
|
||||
Checkbox autodetect;
|
||||
|
||||
Label lpatchpath;
|
||||
Editbox patchpath;
|
||||
Button patchselect;
|
||||
Button patchdefault;
|
||||
Checkbox bypass_crc32;
|
||||
|
||||
Label lsavepath;
|
||||
Editbox savepath;
|
||||
@@ -24,11 +22,9 @@ public:
|
||||
|
||||
uintptr_t selectpath_rom(Event);
|
||||
uintptr_t defaultpath_rom(Event);
|
||||
uintptr_t autodetect_tick(Event);
|
||||
|
||||
uintptr_t selectpath_patch(Event);
|
||||
uintptr_t defaultpath_patch(Event);
|
||||
uintptr_t bypass_crc32_tick(Event);
|
||||
|
||||
uintptr_t selectpath_save(Event);
|
||||
uintptr_t defaultpath_save(Event);
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
void Status::update() {
|
||||
string output;
|
||||
|
||||
if(queue[0].valid == true) {
|
||||
output = queue[0].text;
|
||||
time_t currenttime = time(0);
|
||||
if((int)(currenttime - displaytime) >= 3) {
|
||||
displaytime = currenttime;
|
||||
|
||||
//message displayed for 3 seconds or more, remove from queue
|
||||
for(unsigned i = 0; i < 15; i++) {
|
||||
queue[i].valid = queue[i + 1].valid;
|
||||
queue[i].text = queue[i + 1].text;
|
||||
}
|
||||
queue[15].valid = false;
|
||||
queue[15].text = "";
|
||||
}
|
||||
} else if(!cartridge.loaded()) {
|
||||
output = "";
|
||||
} else if(app.pause || app.autopause) {
|
||||
output = "Paused";
|
||||
} else if(ppu.status.frames_updated) {
|
||||
ppu.status.frames_updated = false;
|
||||
|
||||
unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50;
|
||||
switch(config::system.emulation_speed) {
|
||||
case 0: max_framerate = unsigned(0.50 * max_framerate); break;
|
||||
case 1: max_framerate = unsigned(0.75 * max_framerate); break;
|
||||
case 2: max_framerate = unsigned(1.00 * max_framerate); break;
|
||||
case 3: max_framerate = unsigned(1.50 * max_framerate); break;
|
||||
case 4: max_framerate = unsigned(2.00 * max_framerate); break;
|
||||
case 5: max_framerate = 0; break;
|
||||
}
|
||||
|
||||
output = string() << cartridge.info.filename << " : " << (int)ppu.status.frames_executed;
|
||||
if(max_framerate != 0) {
|
||||
output << " / ";
|
||||
output << (int)max_framerate;
|
||||
}
|
||||
} else {
|
||||
//no operation
|
||||
return;
|
||||
}
|
||||
|
||||
if(text != output) {
|
||||
//refresh status text if it has changed since last redraw
|
||||
text = output;
|
||||
window.status.set_text(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Status::flush() {
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
queue[i].valid = false;
|
||||
queue[i].text = "";
|
||||
}
|
||||
}
|
||||
|
||||
bool Status::enqueue(const char *message) {
|
||||
unsigned index = 0;
|
||||
while(index < 16) {
|
||||
if(queue[index].valid == false) break;
|
||||
index++;
|
||||
}
|
||||
//queue full?
|
||||
if(index >= 16) return false;
|
||||
|
||||
queue[index].valid = true;
|
||||
queue[index].text = message;
|
||||
displaytime = time(0);
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status::Status() : window(window_main) {
|
||||
flush();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
class Status {
|
||||
public:
|
||||
void update();
|
||||
void flush();
|
||||
bool enqueue(const char *message);
|
||||
|
||||
Status();
|
||||
|
||||
private:
|
||||
Window &window;
|
||||
string text;
|
||||
|
||||
struct Queue {
|
||||
bool valid;
|
||||
string text;
|
||||
} queue[16];
|
||||
time_t displaytime;
|
||||
} status;
|
||||
+1
-2
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "base/main.cpp"
|
||||
#include "base/about.cpp"
|
||||
#include "base/message.cpp"
|
||||
|
||||
#include "loader/bsxloader.cpp"
|
||||
#include "loader/stloader.cpp"
|
||||
@@ -20,7 +19,6 @@ void ui_init() {
|
||||
|
||||
window_main.setup();
|
||||
window_about.setup();
|
||||
window_message.setup();
|
||||
|
||||
window_bsxloader.setup();
|
||||
window_stloader.setup();
|
||||
@@ -45,6 +43,7 @@ void ui_init() {
|
||||
video.set(Video::Synchronize, false);
|
||||
audio.set(Audio::Handle, window_main.handle());
|
||||
input.set(Input::Handle, window_main.handle());
|
||||
input.set(Input::AnalogAxisResistance, config::input.analog_axis_resistance);
|
||||
//sets Audio::Synchronize and Audio::Frequency
|
||||
event::update_emulation_speed(config::system.emulation_speed);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ nall::dictionary translate;
|
||||
|
||||
#include "base/main.h"
|
||||
#include "base/about.h"
|
||||
#include "base/message.h"
|
||||
|
||||
#include "loader/bsxloader.h"
|
||||
#include "loader/stloader.h"
|
||||
|
||||
Reference in new issue
Block a user