mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-11 09:21:46 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9bff19b5b | ||
|
|
20be19f876 | ||
|
|
f748a34e49 | ||
|
|
0af5703c47 | ||
|
|
f73d0908c4 | ||
|
|
18389cb8f7 | ||
|
|
448a8336b1 | ||
|
|
233e645772 | ||
|
|
f0627239bb | ||
|
|
ae67f268a8 | ||
|
|
b2331ddb85 | ||
|
|
2a2f50a8bc | ||
|
|
30b19613d5 | ||
|
|
98fc865130 | ||
|
|
f6a04682f5 | ||
|
|
87b91f0ace | ||
|
|
0114e10ede | ||
|
|
8c591ce44a | ||
|
|
e2cc164f70 | ||
|
|
d09e54149b | ||
|
|
8e4f1be189 | ||
|
|
f529a84fd1 | ||
|
|
567d415290 | ||
|
|
435a194ccd | ||
|
|
df9de289b9 | ||
|
|
dd83559786 | ||
|
|
100ef3a271 | ||
|
|
bccc5b5a12 | ||
|
|
acee547da9 | ||
|
|
b1b146fd7d | ||
|
|
53e913e225 | ||
|
|
0cf16ce784 | ||
|
|
ce38d577ef | ||
|
|
0a87b99370 | ||
|
|
82d5761705 | ||
|
|
9133129209 | ||
|
|
7d83cde40a | ||
|
|
bbc77a6cf2 |
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
|
||||
|
||||
+32
-24
@@ -1,10 +1,11 @@
|
||||
bsnes
|
||||
Version: 0.032
|
||||
Version: 0.037
|
||||
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:
|
||||
------------------
|
||||
====================
|
||||
Known Limitation(s):
|
||||
====================
|
||||
|
||||
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,20 @@ Hardware Bugs
|
||||
- S-CPU.r1 HDMA crashing bug not emulated
|
||||
- S-CPU<>S-SMP communication bus conflicts not emulated
|
||||
|
||||
---------------------
|
||||
===============
|
||||
Known Issue(s):
|
||||
===============
|
||||
|
||||
On Windows, attempting to load a ZIP, GZ or JMA compressed archive with
|
||||
non-ANSI characters in the filename will fail. This is because Windows
|
||||
requires UTF-16 encoding, but these libraries only work with UTF-8.
|
||||
Note that loading uncompressed images (SMC, SFC, etc) with non-ANSI characters
|
||||
works properly on all platforms.
|
||||
|
||||
=====================
|
||||
Unsupported Hardware:
|
||||
---------------------
|
||||
=====================
|
||||
|
||||
SA-1
|
||||
Coprocessor used in many popular games, including:
|
||||
- Dragon Ball Z Hyper Dimension
|
||||
@@ -75,13 +90,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 +99,10 @@ 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,
|
||||
Matthew Callis, mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister,
|
||||
tetsuo55, TRAC, zones
|
||||
+13
-12
@@ -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
|
||||
@@ -133,7 +133,7 @@ obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj)
|
||||
### libraries ###
|
||||
#################
|
||||
|
||||
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/*
|
||||
obj/ruby.$(obj): lib/ruby/ruby.cpp lib/ruby/* lib/ruby/video/* lib/ruby/audio/* lib/ruby/input/*
|
||||
$(call compile,$(rubydef) $(rubyflags))
|
||||
obj/hiro.$(obj): lib/hiro/hiro.cpp lib/hiro/* lib/hiro/gtk/* lib/hiro/win/*
|
||||
$(call compile,$(if $(call streq,$(platform),x),`pkg-config --cflags gtk+-2.0`))
|
||||
@@ -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 ###
|
||||
|
||||
+4
-14
@@ -1,4 +1,4 @@
|
||||
#define BSNES_VERSION "0.032"
|
||||
#define BSNES_VERSION "0.037"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
@@ -24,12 +24,15 @@
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/config.hpp>
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/modulo.hpp>
|
||||
#include <nall/new.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
using namespace nall;
|
||||
|
||||
@@ -39,18 +42,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"
|
||||
+74
-44
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <nall/crc32.hpp>
|
||||
#include <nall/ups.hpp>
|
||||
|
||||
|
||||
#include "cart_load.cpp"
|
||||
#include "cart_normal.cpp"
|
||||
#include "cart_bsx.cpp"
|
||||
#include "cart_bsc.cpp"
|
||||
@@ -13,7 +14,7 @@
|
||||
#include "cart_header.cpp"
|
||||
|
||||
namespace memory {
|
||||
MappedRAM cartrom, cartram;
|
||||
MappedRAM cartrom, cartram, cartrtc;
|
||||
MappedRAM bscram;
|
||||
MappedRAM stArom, stAram;
|
||||
MappedRAM stBrom, stBram;
|
||||
@@ -21,58 +22,34 @@ namespace memory {
|
||||
|
||||
Cartridge cartridge;
|
||||
|
||||
const char* Cartridge::name() { return info.filename; }
|
||||
Cartridge::CartridgeMode Cartridge::mode() { return info.mode; }
|
||||
Cartridge::MemoryMapper Cartridge::mapper() { return info.mapper; }
|
||||
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;
|
||||
void Cartridge::load_begin(CartridgeMode mode) {
|
||||
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.mode = mode;
|
||||
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.dsp1_mapper = DSP1Unmapped;
|
||||
|
||||
info.header_index = 0xffc0;
|
||||
info.mapper = LoROM;
|
||||
info.name[0] = 0;
|
||||
info.region = NTSC;
|
||||
|
||||
info.rom_size = 0;
|
||||
info.ram_size = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -87,7 +64,7 @@ void Cartridge::load_end() {
|
||||
memory::stBrom.write_protect(true);
|
||||
memory::stBram.write_protect(false);
|
||||
|
||||
if(fexists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
if(file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
cheat.clear();
|
||||
cheat.load(cheatfn);
|
||||
}
|
||||
@@ -101,25 +78,23 @@ bool Cartridge::unload() {
|
||||
|
||||
bus.unload_cart();
|
||||
|
||||
switch(info.type) {
|
||||
case CartridgeNormal: unload_cart_normal(); break;
|
||||
case CartridgeBSX: unload_cart_bsx(); break;
|
||||
case CartridgeBSC: unload_cart_bsc(); break;
|
||||
case CartridgeSufamiTurbo: unload_cart_st(); break;
|
||||
switch(info.mode) {
|
||||
case ModeNormal: unload_cart_normal(); break;
|
||||
case ModeBSX: unload_cart_bsx(); break;
|
||||
case ModeBSC: unload_cart_bsc(); break;
|
||||
case ModeSufamiTurbo: unload_cart_st(); break;
|
||||
}
|
||||
|
||||
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; }
|
||||
if(stB.rom) { delete[] stB.rom; stB.rom = 0; }
|
||||
if(stB.ram) { delete[] stB.ram; stB.ram = 0; }
|
||||
|
||||
char fn[PATH_MAX];
|
||||
strcpy(fn, cart.fn);
|
||||
modify_extension(fn, "cht");
|
||||
if(cheat.count() > 0 || fexists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
if(cheat.count() > 0 || file::exists(get_cheat_filename(cart.fn, "cht"))) {
|
||||
cheat.save(cheatfn);
|
||||
cheat.clear();
|
||||
}
|
||||
@@ -135,3 +110,58 @@ Cartridge::Cartridge() {
|
||||
Cartridge::~Cartridge() {
|
||||
if(cart.loaded == true) unload();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void Cartridge::cartinfo_t::reset() {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
|
||||
rom_size = 0;
|
||||
ram_size = 0;
|
||||
|
||||
bsxslot = false;
|
||||
superfx = false;
|
||||
sa1 = false;
|
||||
srtc = false;
|
||||
sdd1 = false;
|
||||
spc7110 = false;
|
||||
spc7110rtc = false;
|
||||
cx4 = false;
|
||||
dsp1 = false;
|
||||
dsp2 = false;
|
||||
dsp3 = false;
|
||||
dsp4 = false;
|
||||
obc1 = false;
|
||||
st010 = false;
|
||||
st011 = false;
|
||||
st018 = false;
|
||||
}
|
||||
|
||||
//apply cart-specific settings to current cartridge mode settings
|
||||
Cartridge::info_t& Cartridge::info_t::operator=(const Cartridge::cartinfo_t &source) {
|
||||
mapper = source.mapper;
|
||||
dsp1_mapper = source.dsp1_mapper;
|
||||
region = source.region;
|
||||
|
||||
bsxslot = source.bsxslot;
|
||||
superfx = source.superfx;
|
||||
sa1 = source.sa1;
|
||||
srtc = source.srtc;
|
||||
sdd1 = source.sdd1;
|
||||
spc7110 = source.spc7110;
|
||||
spc7110rtc = source.spc7110rtc;
|
||||
cx4 = source.cx4;
|
||||
dsp1 = source.dsp1;
|
||||
dsp2 = source.dsp2;
|
||||
dsp3 = source.dsp3;
|
||||
dsp4 = source.dsp4;
|
||||
obc1 = source.obc1;
|
||||
st010 = source.st010;
|
||||
st011 = source.st011;
|
||||
st018 = source.st018;
|
||||
|
||||
return *this;
|
||||
}
|
||||
+81
-34
@@ -1,10 +1,20 @@
|
||||
class Cartridge {
|
||||
public:
|
||||
enum CartridgeMode {
|
||||
ModeNormal,
|
||||
ModeBSC,
|
||||
ModeBSX,
|
||||
ModeSufamiTurbo,
|
||||
};
|
||||
|
||||
enum CartridgeType {
|
||||
CartridgeNormal,
|
||||
CartridgeBSX,
|
||||
CartridgeBSC,
|
||||
CartridgeSufamiTurbo,
|
||||
TypeNormal,
|
||||
TypeBSC,
|
||||
TypeBSXBIOS,
|
||||
TypeBSX,
|
||||
TypeSufamiTurboBIOS,
|
||||
TypeSufamiTurbo,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum HeaderField {
|
||||
@@ -18,8 +28,7 @@ public:
|
||||
VERSION = 0x1b,
|
||||
ICKSUM = 0x1c,
|
||||
CKSUM = 0x1e,
|
||||
RESL = 0x3c,
|
||||
RESH = 0x3d,
|
||||
RESETV = 0x3c,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
@@ -32,9 +41,10 @@ public:
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
BSXROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
@@ -45,11 +55,16 @@ public:
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
const char* name();
|
||||
CartridgeMode mode();
|
||||
MemoryMapper mapper();
|
||||
Region region();
|
||||
|
||||
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 {
|
||||
@@ -64,27 +79,22 @@ public:
|
||||
uint rom_size, ram_size;
|
||||
} stA, stB;
|
||||
|
||||
struct {
|
||||
struct cartinfo_t {
|
||||
CartridgeType type;
|
||||
|
||||
uint32 crc32;
|
||||
char filename[PATH_MAX * 4];
|
||||
char name[128];
|
||||
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
uint rom_size;
|
||||
uint ram_size;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
Region region;
|
||||
|
||||
bool bsxbase;
|
||||
bool bsxcart;
|
||||
bool bsxflash;
|
||||
bool st;
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
|
||||
bool bsxslot;
|
||||
bool superfx;
|
||||
bool sa1;
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
@@ -95,17 +105,53 @@ public:
|
||||
bool st011;
|
||||
bool st018;
|
||||
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
void reset();
|
||||
};
|
||||
|
||||
uint header_index;
|
||||
struct info_t {
|
||||
char filename[PATH_MAX * 4];
|
||||
bool patched;
|
||||
|
||||
CartridgeMode mode;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
Region region;
|
||||
|
||||
bool bsxcart; //is BS-X cart inserted?
|
||||
bool bsxflash; //is BS-X flash cart inserted into BS-X cart?
|
||||
|
||||
bool bsxslot;
|
||||
bool superfx;
|
||||
bool sa1;
|
||||
bool srtc;
|
||||
bool sdd1;
|
||||
bool spc7110;
|
||||
bool spc7110rtc;
|
||||
bool cx4;
|
||||
bool dsp1;
|
||||
bool dsp2;
|
||||
bool dsp3;
|
||||
bool dsp4;
|
||||
bool obc1;
|
||||
bool st010;
|
||||
bool st011;
|
||||
bool st018;
|
||||
|
||||
info_t& operator=(const cartinfo_t&);
|
||||
} info;
|
||||
|
||||
MemoryMapper mapper();
|
||||
Region region();
|
||||
struct {
|
||||
char fn[PATH_MAX];
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
} image;
|
||||
bool load_image(const char*);
|
||||
bool inspect_image(cartinfo_t &cartinfo, const char *filename);
|
||||
bool load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init);
|
||||
|
||||
void load_cart_normal(const char*);
|
||||
void load_cart_bsx(const char*, const char*);
|
||||
void load_cart_bsc(const char*, const char*);
|
||||
void load_cart_bsx(const char*, const char*);
|
||||
void load_cart_st(const char*, const char*, const char*);
|
||||
|
||||
void unload_cart_normal();
|
||||
@@ -114,13 +160,13 @@ public:
|
||||
void unload_cart_st();
|
||||
|
||||
bool loaded();
|
||||
void load_begin(CartridgeType);
|
||||
void load_begin(CartridgeMode);
|
||||
void load_end();
|
||||
bool unload();
|
||||
|
||||
void find_header();
|
||||
void read_header();
|
||||
void read_extended_header();
|
||||
void read_header(cartinfo_t &info, const uint8_t *data, unsigned size);
|
||||
unsigned find_header(const uint8_t *data, unsigned size);
|
||||
unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
|
||||
enum CompressionMode {
|
||||
CompressionNone, //always load without compression
|
||||
@@ -142,12 +188,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;
|
||||
|
||||
+19
-40
@@ -1,57 +1,36 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsc(const char *base, const char *slot) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot ? slot : "");
|
||||
load_begin(CartridgeBSC);
|
||||
|
||||
uint8_t *data = 0;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
load_file(cart.fn, data, size, CompressionAuto);
|
||||
cart.rom = data, cart.rom_size = size;
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot);
|
||||
|
||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
load_begin(ModeBSC);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(load_image(slot) == true) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = image.data;
|
||||
bs.ram_size = image.size;
|
||||
}
|
||||
|
||||
if(*bs.fn) {
|
||||
if(load_file(bs.fn, data, size, CompressionAuto) == true) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = data, bs.ram_size = size;
|
||||
if(load_file(get_patch_filename(bs.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, bs.ram, bs.ram_size);
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find_header();
|
||||
read_header();
|
||||
|
||||
info.mapper = cartridge.info.header_index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
||||
info.region = NTSC;
|
||||
|
||||
if(info.ram_size > 0) {
|
||||
cart.ram = new uint8_t[cart.ram_size = info.ram_size];
|
||||
memset(cart.ram, 0xff, cart.ram_size);
|
||||
|
||||
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
|
||||
memcpy(cart.ram, data, min(size, cart.ram_size));
|
||||
delete[] data;
|
||||
}
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, cart.fn);
|
||||
strcpy(info.filename, base);
|
||||
get_base_filename(info.filename);
|
||||
if(*bs.fn) {
|
||||
if(*slot) {
|
||||
char filenameBS[PATH_MAX];
|
||||
strcpy(filenameBS, bs.fn);
|
||||
strcpy(filenameBS, slot);
|
||||
get_base_filename(filenameBS);
|
||||
strcat(info.filename, " + ");
|
||||
strcat(info.filename, filenameBS);
|
||||
|
||||
+17
-29
@@ -1,27 +1,20 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot ? slot : "");
|
||||
|
||||
load_begin(CartridgeBSX);
|
||||
info.bsxbase = true;
|
||||
info.bsxcart = true;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC;
|
||||
|
||||
uint8_t *data = 0;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
load_file(cart.fn, data, size, CompressionAuto);
|
||||
cart.rom = data, cart.rom_size = size;
|
||||
cart.ram = 0, cart.ram_size = 0;
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(bs.fn, slot);
|
||||
|
||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
}
|
||||
load_begin(ModeBSX);
|
||||
if(load_image(base) == false) return;
|
||||
info.bsxcart = true;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
cart.ram = 0;
|
||||
cart.ram_size = 0;
|
||||
|
||||
memset(bsxcart.sram.handle (), 0x00, bsxcart.sram.size ());
|
||||
memset(bsxcart.psram.handle(), 0x00, bsxcart.psram.size());
|
||||
@@ -36,20 +29,15 @@ void Cartridge::load_cart_bsx(const char *base, const char *slot) {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
if(*bs.fn) {
|
||||
if(load_file(bs.fn, data, size, CompressionAuto) == true) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = data, bs.ram_size = size;
|
||||
if(load_file(get_patch_filename(bs.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, bs.ram, bs.ram_size);
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
if(load_image(slot)) {
|
||||
info.bsxflash = true;
|
||||
bs.ram = image.data;
|
||||
bs.ram_size = image.size;
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
strcpy(info.filename, !*bs.fn ? cart.fn : bs.fn);
|
||||
strcpy(info.filename, !*slot ? base : slot);
|
||||
get_base_filename(info.filename);
|
||||
}
|
||||
|
||||
|
||||
+110
-106
@@ -1,27 +1,27 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
#include "../reader/filereader.h"
|
||||
|
||||
#if defined(GZIP_SUPPORT)
|
||||
#include "../reader/gzreader.h"
|
||||
#include "../reader/zipreader.h"
|
||||
#endif
|
||||
|
||||
#if defined(JMA_SUPPORT)
|
||||
#include "../reader/jmareader.h"
|
||||
#endif
|
||||
|
||||
char* Cartridge::modify_extension(char *filename, const char *extension) {
|
||||
int i;
|
||||
for(i = strlen(filename); i >= 0; i--) {
|
||||
if(filename[i] == '.') break;
|
||||
if(filename[i] == '/') break;
|
||||
if(filename[i] == '\\') break;
|
||||
}
|
||||
if(i > 0 && filename[i] == '.') filename[i] = 0;
|
||||
strcat(filename, ".");
|
||||
strcat(filename, extension);
|
||||
return filename;
|
||||
#include "../reader/filereader.h"
|
||||
|
||||
#if defined(GZIP_SUPPORT)
|
||||
#include "../reader/gzreader.h"
|
||||
#include "../reader/zipreader.h"
|
||||
#endif
|
||||
|
||||
#if defined(JMA_SUPPORT)
|
||||
#include "../reader/jmareader.h"
|
||||
#endif
|
||||
|
||||
char* Cartridge::modify_extension(char *filename, const char *extension) {
|
||||
int i;
|
||||
for(i = strlen(filename); i >= 0; i--) {
|
||||
if(filename[i] == '.') break;
|
||||
if(filename[i] == '/') break;
|
||||
if(filename[i] == '\\') break;
|
||||
}
|
||||
if(i > 0 && filename[i] == '.') filename[i] = 0;
|
||||
strcat(filename, ".");
|
||||
strcat(filename, extension);
|
||||
return filename;
|
||||
}
|
||||
|
||||
//remove directory information and file extension ("/foo/bar.ext" -> "bar")
|
||||
@@ -47,36 +47,38 @@ char* Cartridge::get_base_filename(char *filename) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) {
|
||||
strcpy(filename, source);
|
||||
for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; }
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) {
|
||||
strcpy(filename, source);
|
||||
for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; }
|
||||
modify_extension(filename, extension);
|
||||
|
||||
//override path with user-specified folder, if one was defined
|
||||
|
||||
//override path with user-specified folder, if one was defined
|
||||
if(*path) {
|
||||
lstring part;
|
||||
split(part, "/", filename);
|
||||
string fn = path;
|
||||
if(strend(fn, "/") == false) strcat(fn, "/");
|
||||
strcat(fn, part[count(part) - 1]);
|
||||
strcpy(filename, fn);
|
||||
|
||||
//resolve relative path, if found
|
||||
if(strbegin(fn, "./") == true) {
|
||||
ltrim(fn, "./");
|
||||
strcpy(filename, config::path.base);
|
||||
strcat(filename, fn);
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
lstring part;
|
||||
split(part, "/", filename);
|
||||
string fn = path;
|
||||
if(strend(fn, "/") == false) strcat(fn, "/");
|
||||
strcat(fn, part[count(part) - 1]);
|
||||
strcpy(filename, fn);
|
||||
|
||||
//resolve relative path, if found
|
||||
if(strbegin(fn, "./") == true) {
|
||||
ltrim(fn, "./");
|
||||
strcpy(filename, config::path.base);
|
||||
strcat(filename, fn);
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
char* Cartridge::get_patch_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(patchfn, config::path.patch, source, extension);
|
||||
}
|
||||
}
|
||||
|
||||
char* Cartridge::get_save_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(savefn, config::path.save, source, extension);
|
||||
@@ -85,63 +87,65 @@ char* Cartridge::get_save_filename(const char *source, const char *extension) {
|
||||
char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
|
||||
return get_path_filename(cheatfn, config::path.cheat, source, extension);
|
||||
}
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) {
|
||||
dprintf("* Loading \"%s\" ...", fn);
|
||||
|
||||
if(fexists(fn) == false) return false;
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) {
|
||||
if(file::exists(fn) == false) return false;
|
||||
|
||||
Reader::Type filetype = Reader::Normal;
|
||||
if(compression == CompressionInspect) filetype = Reader::detect(fn, true);
|
||||
if(compression == CompressionAuto) filetype = Reader::detect(fn, config::file.autodetect_type);
|
||||
|
||||
switch(filetype) {
|
||||
|
||||
switch(filetype) {
|
||||
default:
|
||||
dprintf("* Warning: filetype detected as unsupported compression type.");
|
||||
dprintf("* Will attempt to load as uncompressed file -- may fail.");
|
||||
case Reader::Normal: {
|
||||
FileReader ff(fn);
|
||||
if(!ff.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
size = ff.size();
|
||||
data = ff.read();
|
||||
} break;
|
||||
|
||||
#ifdef GZIP_SUPPORT
|
||||
case Reader::GZIP: {
|
||||
GZReader gf(fn);
|
||||
if(!gf.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
size = gf.size();
|
||||
data = gf.read();
|
||||
} break;
|
||||
|
||||
case Reader::ZIP: {
|
||||
ZipReader zf(fn);
|
||||
size = zf.size();
|
||||
data = zf.read();
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef JMA_SUPPORT
|
||||
case Reader::JMA: {
|
||||
try {
|
||||
JMAReader jf(fn);
|
||||
size = jf.size();
|
||||
data = jf.read();
|
||||
} catch(JMA::jma_errors jma_error) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
dprintf("* Will attempt to load as uncompressed file -- may fail.");
|
||||
case Reader::Normal: {
|
||||
FileReader ff(fn);
|
||||
if(!ff.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
size = ff.size();
|
||||
data = ff.read();
|
||||
} break;
|
||||
|
||||
#ifdef GZIP_SUPPORT
|
||||
case Reader::GZIP: {
|
||||
GZReader gf(fn);
|
||||
if(!gf.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
size = gf.size();
|
||||
data = gf.read();
|
||||
} break;
|
||||
|
||||
case Reader::ZIP: {
|
||||
ZipReader zf(fn);
|
||||
if(!zf.ready()) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
size = zf.size();
|
||||
data = zf.read();
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#ifdef JMA_SUPPORT
|
||||
case Reader::JMA: {
|
||||
try {
|
||||
JMAReader jf(fn);
|
||||
size = jf.size();
|
||||
data = jf.read();
|
||||
} catch(JMA::jma_errors jma_error) {
|
||||
alert("Error loading image file (%s)!", fn);
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t *&data, unsigned &size) {
|
||||
@@ -157,23 +161,23 @@ bool Cartridge::apply_patch(const uint8_t *pdata, const unsigned psize, uint8_t
|
||||
if(result == ups::output_crc32_invalid) apply = true;
|
||||
}
|
||||
|
||||
//if patch application was successful, replace old data, size with new data, size
|
||||
if(apply == true) {
|
||||
delete[] data;
|
||||
data = new uint8_t[size = outsize];
|
||||
memcpy(data, outdata, outsize);
|
||||
} else {
|
||||
dprintf("* Warning: patch application failed!");
|
||||
}
|
||||
|
||||
if(outdata) delete[] outdata;
|
||||
}
|
||||
|
||||
return apply;
|
||||
}
|
||||
|
||||
bool Cartridge::save_file(const char *fn, uint8 *data, uint size) {
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
if(!fp) return false;
|
||||
fwrite(data, 1, size, fp);
|
||||
fclose(fp);
|
||||
file fp;
|
||||
if(!fp.open(fn, file::mode_write)) return false;
|
||||
fp.write(data, size);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
+158
-91
@@ -1,29 +1,70 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::read_header() {
|
||||
uint8 *rom = cart.rom;
|
||||
uint index = info.header_index;
|
||||
uint8 mapper = rom[index + MAPPER];
|
||||
uint8 rom_type = rom[index + ROM_TYPE];
|
||||
uint8 company = rom[index + COMPANY];
|
||||
uint8 region = rom[index + REGION] & 0x7f;
|
||||
void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size) {
|
||||
info.reset();
|
||||
unsigned index = find_header(data, size);
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
bool has_bsxflash = false;
|
||||
if(rom[index - 14] == 'Z') {
|
||||
if(rom[index - 11] == 'J') {
|
||||
uint8 n13 = rom[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (rom[index - 10] == 0x00 && rom[index - 4] == 0x00)) {
|
||||
has_bsxflash = true;
|
||||
//detect BS-X flash carts
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
info.type = TypeBSX;
|
||||
info.mapper = BSXROM;
|
||||
info.region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsxflash == true) {
|
||||
info.mapper = index == 0x7fc0 ? BSCLoROM : BSCHiROM;
|
||||
} else if(index == 0x7fc0 && cart.rom_size >= 0x401000) {
|
||||
//detect Sufami Turbo carts
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
info.type = TypeSufamiTurboBIOS;
|
||||
} else {
|
||||
info.type = TypeSufamiTurbo;
|
||||
}
|
||||
info.mapper = STROM;
|
||||
info.region = NTSC; //Sufami Turbo only released in Japan
|
||||
return;
|
||||
}
|
||||
|
||||
//standard cart
|
||||
uint8 mapper = data[index + MAPPER];
|
||||
uint8 rom_type = data[index + ROM_TYPE];
|
||||
uint8 rom_size = data[index + ROM_SIZE];
|
||||
uint8 company = data[index + COMPANY];
|
||||
uint8 region = data[index + REGION] & 0x7f;
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8 n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
info.bsxslot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(info.bsxslot == true) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
info.type = TypeBSXBIOS;
|
||||
info.mapper = BSXROM;
|
||||
} else {
|
||||
info.type = TypeBSC;
|
||||
info.mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
info.type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
info.mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapper == 0x32) {
|
||||
info.mapper = ExLoROM;
|
||||
@@ -52,8 +93,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) {
|
||||
@@ -73,7 +115,7 @@ void Cartridge::read_header() {
|
||||
}
|
||||
|
||||
if(info.dsp1 == true) {
|
||||
if((mapper & 0x2f) == 0x20 && cart.rom_size <= 0x100000) {
|
||||
if((mapper & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
info.dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapper & 0x2f) == 0x20) {
|
||||
info.dsp1_mapper = DSP1LoROM2MB;
|
||||
@@ -98,97 +140,122 @@ 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;
|
||||
}
|
||||
|
||||
if(rom[info.header_index + RAM_SIZE] & 7) {
|
||||
info.ram_size = 1024 << (rom[info.header_index + RAM_SIZE] & 7);
|
||||
if(data[index + RAM_SIZE] & 7) {
|
||||
info.ram_size = 1024 << (data[index + RAM_SIZE] & 7);
|
||||
} else {
|
||||
info.ram_size = 0;
|
||||
}
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
info.region = (region <= 1 || region >= 13) ? NTSC : PAL;
|
||||
|
||||
memcpy(&info.name, &rom[info.header_index + CART_NAME], 21);
|
||||
info.name[21] = 0;
|
||||
trim(info.name);
|
||||
|
||||
//convert undisplayable characters (half-width katakana, etc) to '?' characters
|
||||
for(int i = 0; i < 21; i++) {
|
||||
if(info.name[i] & 0x80) info.name[i] = '?';
|
||||
}
|
||||
|
||||
//always display something
|
||||
if(!info.name[0]) strcpy(info.name, "(untitled)");
|
||||
}
|
||||
|
||||
void Cartridge::find_header() {
|
||||
int32 score_lo = 0, score_hi = 0, score_ex = 0;
|
||||
uint8_t *rom = cart.rom;
|
||||
|
||||
if(cart.rom_size < 0x010000) {
|
||||
//cart too small to be anything but lorom
|
||||
info.header_index = 0x007fc0;
|
||||
return;
|
||||
}
|
||||
|
||||
if((rom[0x7fc0 + MAPPER] & ~0x10) == 0x20) score_lo++;
|
||||
if((rom[0xffc0 + MAPPER] & ~0x10) == 0x21) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_TYPE] < 0x08) score_lo++;
|
||||
if(rom[0xffc0 + ROM_TYPE] < 0x08) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + ROM_SIZE] < 0x10) score_lo++;
|
||||
if(rom[0xffc0 + ROM_SIZE] < 0x10) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RAM_SIZE] < 0x08) score_lo++;
|
||||
if(rom[0xffc0 + RAM_SIZE] < 0x08) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + REGION] < 14) score_lo++;
|
||||
if(rom[0xffc0 + REGION] < 14) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + COMPANY] < 3) score_lo++;
|
||||
if(rom[0xffc0 + COMPANY] < 3) score_hi++;
|
||||
|
||||
if(rom[0x7fc0 + RESH] & 0x80) score_lo += 2;
|
||||
if(rom[0xffc0 + RESH] & 0x80) score_hi += 2;
|
||||
|
||||
uint16 cksum, icksum;
|
||||
cksum = rom[0x7fc0 + CKSUM] | (rom[0x7fc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0x7fc0 + ICKSUM] | (rom[0x7fc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_lo += 8;
|
||||
}
|
||||
|
||||
cksum = rom[0xffc0 + CKSUM] | (rom[0xffc0 + CKSUM + 1] << 8);
|
||||
icksum = rom[0xffc0 + ICKSUM] | (rom[0xffc0 + ICKSUM + 1] << 8);
|
||||
if((cksum + icksum) == 0xffff && (cksum != 0) && (icksum != 0)) {
|
||||
score_hi += 8;
|
||||
}
|
||||
|
||||
if(cart.rom_size < 0x401000) {
|
||||
score_ex = 0;
|
||||
} else {
|
||||
if(rom[0x7fc0 + MAPPER] == 0x32) score_lo++;
|
||||
else score_ex += 16;
|
||||
}
|
||||
unsigned Cartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
info.header_index = 0x007fc0;
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
info.header_index = 0x00ffc0;
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
info.header_index = 0x40ffc0;
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16 resetvector = data[addr + RESETV] | (data[addr + RESETV + 1] << 8);
|
||||
uint16 checksum = data[addr + CKSUM] | (data[addr + CKSUM + 1] << 8);
|
||||
uint16 ichecksum = data[addr + ICKSUM] | (data[addr + ICKSUM + 1] << 8);
|
||||
|
||||
uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8 mapper = data[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + ichecksum) == 0xffff && (checksum != 0) && (ichecksum != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + ROM_TYPE] < 0x08) score++;
|
||||
if(data[addr + ROM_SIZE] < 0x10) score++;
|
||||
if(data[addr + RAM_SIZE] < 0x08) score++;
|
||||
if(data[addr + REGION] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
bool Cartridge::load_image(const char *filename) {
|
||||
if(!filename || !*filename) return false;
|
||||
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(!load_file(filename, data, size, CompressionAuto)) return false;
|
||||
|
||||
if((size & 0x7fff) != 512) {
|
||||
image.data = data;
|
||||
image.size = size;
|
||||
} else {
|
||||
//remove 512-byte header
|
||||
image.data = new uint8_t[image.size = size - 512];
|
||||
memcpy(image.data, data + 512, image.size);
|
||||
}
|
||||
|
||||
if(load_file(get_patch_filename(filename, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, image.data, image.size);
|
||||
delete[] data;
|
||||
info.patched = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::inspect_image(cartinfo_t &cartinfo, const char *filename) {
|
||||
cartinfo.reset();
|
||||
if(!load_image(filename)) return false;
|
||||
|
||||
read_header(cartinfo, image.data, image.size);
|
||||
delete[] image.data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cartridge::load_ram(const char *filename, uint8_t *&data, unsigned size, uint8_t init) {
|
||||
data = new uint8_t[size];
|
||||
memset(data, init, size);
|
||||
|
||||
uint8_t *savedata;
|
||||
unsigned savesize;
|
||||
if(load_file(filename, savedata, savesize, CompressionNone) == false) return false;
|
||||
|
||||
memcpy(data, savedata, min(size, savesize));
|
||||
delete[] savedata;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
+14
-33
@@ -1,54 +1,35 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_normal(const char *filename) {
|
||||
if(!filename || !*filename) return;
|
||||
|
||||
uint8_t *data = 0;
|
||||
void Cartridge::load_cart_normal(const char *base) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(load_file(filename, data, size, CompressionAuto) == false) return;
|
||||
strcpy(cart.fn, filename);
|
||||
strcpy(cart.fn, base);
|
||||
|
||||
load_begin(CartridgeNormal);
|
||||
load_begin(ModeNormal);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
//load ROM data, ignore 512-byte header if detected
|
||||
if((size & 0x7fff) != 512) {
|
||||
cart.rom = new uint8_t[cart.rom_size = size];
|
||||
memcpy(cart.rom, data, size);
|
||||
} else {
|
||||
cart.rom = new uint8_t[cart.rom_size = size - 512];
|
||||
memcpy(cart.rom, data + 512, size - 512);
|
||||
}
|
||||
delete[] data;
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
if(cartinfo.ram_size > 0) {
|
||||
load_ram(get_save_filename(base, "srm"), cart.ram, cart.ram_size = cartinfo.ram_size, 0xff);
|
||||
}
|
||||
|
||||
info.crc32 = crc32_calculate(cart.rom, cart.rom_size);
|
||||
|
||||
find_header();
|
||||
read_header();
|
||||
|
||||
if(info.ram_size > 0) {
|
||||
cart.ram = new uint8_t[cart.ram_size = info.ram_size];
|
||||
memset(cart.ram, 0xff, cart.ram_size);
|
||||
|
||||
if(load_file(get_save_filename(cart.fn, "srm"), data, size, CompressionNone) == true) {
|
||||
memcpy(cart.ram, data, min(size, cart.ram_size));
|
||||
delete[] data;
|
||||
}
|
||||
if(cartinfo.srtc || cartinfo.spc7110rtc) {
|
||||
load_ram(get_save_filename(base, "rtc"), cart.rtc, cart.rtc_size = 20, 0x00);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
strcpy(info.filename, cart.fn);
|
||||
strcpy(info.filename, base);
|
||||
get_base_filename(info.filename);
|
||||
}
|
||||
|
||||
void Cartridge::unload_cart_normal() {
|
||||
if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size);
|
||||
if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size);
|
||||
}
|
||||
|
||||
#endif //ifdef CART_CPP
|
||||
+30
-64
@@ -1,86 +1,52 @@
|
||||
#ifdef CART_CPP
|
||||
|
||||
void Cartridge::load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
if(!base || !*base) return;
|
||||
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(stA.fn, slotA ? slotA : "");
|
||||
strcpy(stB.fn, slotB ? slotB : "");
|
||||
|
||||
load_begin(CartridgeSufamiTurbo);
|
||||
info.st = true;
|
||||
info.mapper = STROM;
|
||||
info.region = NTSC;
|
||||
|
||||
uint8_t *data = 0;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(load_file(cart.fn, data, size, CompressionAuto) == true) {
|
||||
cart.rom = new(zeromemory) uint8_t[cart.rom_size = 0x040000];
|
||||
memcpy(cart.rom, data, min(size, cart.rom_size));
|
||||
delete[] data;
|
||||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
}
|
||||
strcpy(cart.fn, base);
|
||||
strcpy(stA.fn, slotA);
|
||||
strcpy(stB.fn, slotB);
|
||||
|
||||
load_begin(ModeSufamiTurbo);
|
||||
if(load_image(base) == false) return;
|
||||
|
||||
cartinfo_t cartinfo;
|
||||
read_header(cartinfo, cart.rom = image.data, cart.rom_size = image.size);
|
||||
info = cartinfo;
|
||||
|
||||
if(load_image(slotA)) {
|
||||
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
||||
memcpy(stA.rom, image.data, min(image.size, stA.rom_size));
|
||||
delete[] image.data;
|
||||
|
||||
load_ram(get_save_filename(slotA, "srm"), stA.ram, stA.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
if(*stA.fn) {
|
||||
if(load_file(stA.fn, data, size, CompressionAuto) == true) {
|
||||
stA.rom = new(zeromemory) uint8_t[stA.rom_size = 0x100000];
|
||||
memcpy(stA.rom, data, min(size, stA.rom_size));
|
||||
delete[] data;
|
||||
if(load_file(get_patch_filename(stA.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, stA.rom, stA.rom_size);
|
||||
delete[] data;
|
||||
}
|
||||
if(load_image(slotB)) {
|
||||
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
||||
memcpy(stB.rom, image.data, min(image.size, stB.rom_size));
|
||||
delete[] image.data;
|
||||
|
||||
stA.ram = new uint8_t[stA.ram_size = 0x020000];
|
||||
memset(stA.ram, 0xff, stA.ram_size);
|
||||
|
||||
if(load_file(get_save_filename(stA.fn, "srm"), data, size, CompressionNone) == true) {
|
||||
memcpy(stA.ram, data, min(size, 0x020000U));
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(*stB.fn) {
|
||||
if(load_file(stB.fn, data, size, CompressionAuto) == true) {
|
||||
stB.rom = new(zeromemory) uint8_t[stB.rom_size = 0x100000];
|
||||
memcpy(stB.rom, data, min(size, stB.rom_size));
|
||||
delete[] data;
|
||||
if(load_file(get_patch_filename(stB.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, stB.rom, stB.rom_size);
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
stB.ram = new uint8_t[stB.ram_size = 0x020000];
|
||||
memset(stB.ram, 0xff, stB.ram_size);
|
||||
|
||||
if(load_file(get_save_filename(stB.fn, "srm"), data, size, CompressionNone) == true) {
|
||||
memcpy(stB.ram, data, min(size, 0x020000U));
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
load_ram(get_save_filename(slotB, "srm"), stB.ram, stB.ram_size = 0x020000, 0xff);
|
||||
}
|
||||
|
||||
load_end();
|
||||
|
||||
//set base filename
|
||||
if(!*stA.fn && !*stB.fn) {
|
||||
if(!*slotA && !*slotB) {
|
||||
strcpy(info.filename, cart.fn);
|
||||
get_base_filename(info.filename);
|
||||
} else if(*stA.fn && !*stB.fn) {
|
||||
strcpy(info.filename, stA.fn);
|
||||
} else if(*slotA && !*slotB) {
|
||||
strcpy(info.filename, slotA);
|
||||
get_base_filename(info.filename);
|
||||
} else if(!*stA.fn && *stB.fn) {
|
||||
strcpy(info.filename, stB.fn);
|
||||
} else if(!*slotA && *slotB) {
|
||||
strcpy(info.filename, slotB);
|
||||
get_base_filename(info.filename);
|
||||
} else {
|
||||
char filenameA[PATH_MAX], filenameB[PATH_MAX];
|
||||
strcpy(filenameA, stA.fn);
|
||||
strcpy(filenameA, slotA);
|
||||
get_base_filename(filenameA);
|
||||
strcpy(filenameB, stB.fn);
|
||||
strcpy(filenameB, slotB);
|
||||
get_base_filename(filenameB);
|
||||
strcpy(info.filename, filenameA);
|
||||
strcat(info.filename, " + ");
|
||||
|
||||
+132
-125
@@ -1,30 +1,53 @@
|
||||
#include "../base.h"
|
||||
#include "../reader/filereader.h"
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) {
|
||||
enabled = source.enabled;
|
||||
addr = source.addr;
|
||||
data = source.data;
|
||||
code = source.code;
|
||||
desc = source.desc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//used to sort cheat code list by description
|
||||
bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) {
|
||||
return strcmp(desc, source.desc) < 0;
|
||||
}
|
||||
|
||||
/*****
|
||||
* string <> binary code translation routines
|
||||
* decode() "7e1234:56" -> 0x7e123456
|
||||
* encode() 0x7e123456 -> "7e1234:56"
|
||||
*****/
|
||||
|
||||
bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
|
||||
string t, part;
|
||||
strcpy(t, str);
|
||||
strlower(t());
|
||||
if(strlen(t) == 8 || (strlen(t) == 9 && t()[6] == ':')) {
|
||||
bool Cheat::decode(const char *str, unsigned &addr, uint8 &data, type_t &type) {
|
||||
string t = str;
|
||||
strlower(t);
|
||||
|
||||
#define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f'))
|
||||
|
||||
if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) {
|
||||
//strip ':'
|
||||
if(strlen(t) == 9 && t[6] == ':') t = string() << substr(t, 0, 6) << substr(t, 7);
|
||||
//validate input
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||
|
||||
type = ProActionReplay;
|
||||
replace(t, ":", "");
|
||||
uint32 r = strhex((const char*)t);
|
||||
unsigned r = strhex((const char*)t);
|
||||
addr = r >> 8;
|
||||
data = r & 0xff;
|
||||
return true;
|
||||
} else if(strlen(t) == 9 && t()[4] == '-') {
|
||||
} else if(strlen(t) == 9 && t[4] == '-') {
|
||||
//strip '-'
|
||||
t = string() << substr(t, 0, 4) << substr(t, 5);
|
||||
//validate input
|
||||
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
||||
|
||||
type = GameGenie;
|
||||
replace(t, "-", "");
|
||||
strtr(t, "df4709156bc8a23e", "0123456789abcdef");
|
||||
uint32 r = strhex((const char*)t);
|
||||
unsigned r = strhex((const char*)t);
|
||||
//8421 8421 8421 8421 8421 8421
|
||||
//abcd efgh ijkl mnop qrst uvwx
|
||||
//ijkl qrst opab cduv wxef ghmn
|
||||
@@ -42,16 +65,20 @@ bool Cheat::decode(char *str, uint32 &addr, uint8 &data, uint8 &type) {
|
||||
(!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0);
|
||||
data = r >> 24;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||
bool Cheat::encode(string &str, unsigned addr, uint8 data, type_t type) {
|
||||
char t[16];
|
||||
|
||||
if(type == ProActionReplay) {
|
||||
sprintf(str, "%0.6x:%0.2x", addr, data);
|
||||
sprintf(t, "%0.6x:%0.2x", addr, data);
|
||||
str = t;
|
||||
return true;
|
||||
} else if(type == GameGenie) {
|
||||
uint32 r = addr;
|
||||
unsigned r = addr;
|
||||
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) |
|
||||
(!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) |
|
||||
(!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) |
|
||||
@@ -64,11 +91,13 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||
(!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) |
|
||||
(!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) |
|
||||
(!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
|
||||
sprintf(str, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
|
||||
strtr(str, "0123456789abcdef", "df4709156bc8a23e");
|
||||
sprintf(t, "%0.2x%0.2x-%0.4x", data, addr >> 16, addr & 0xffff);
|
||||
strtr(t, "0123456789abcdef", "df4709156bc8a23e");
|
||||
str = t;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****
|
||||
@@ -78,21 +107,21 @@ bool Cheat::encode(char *str, uint32 addr, uint8 data, uint8 type) {
|
||||
* clear() disable specified address, mirror accordingly
|
||||
*****/
|
||||
|
||||
uint Cheat::mirror_address(uint addr) {
|
||||
unsigned Cheat::mirror_address(unsigned addr) const {
|
||||
if((addr & 0x40e000) != 0x0000) return addr;
|
||||
//8k WRAM mirror
|
||||
//$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
|
||||
return (0x7e0000 + (addr & 0x1fff));
|
||||
}
|
||||
|
||||
void Cheat::set(uint32 addr) {
|
||||
void Cheat::set(unsigned addr) {
|
||||
addr = mirror_address(addr);
|
||||
|
||||
mask[addr >> 3] |= 1 << (addr & 7);
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
for(int x = 0; x <= 0x3f; x++) {
|
||||
unsigned mirror;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] |= 1 << (mirror & 7);
|
||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
@@ -101,20 +130,20 @@ void Cheat::set(uint32 addr) {
|
||||
}
|
||||
}
|
||||
|
||||
void Cheat::clear(uint32 addr) {
|
||||
void Cheat::clear(unsigned addr) {
|
||||
addr = mirror_address(addr);
|
||||
|
||||
//is there more than one cheat code using the same address
|
||||
//(and likely a different override value) that is enabled?
|
||||
//if so, do not clear code lookup table entry for this address.
|
||||
//if there is more than one cheat code using the same address,
|
||||
//(eg with a different override value) then do not clear code
|
||||
//lookup table entry.
|
||||
uint8 r;
|
||||
if(read(addr, r) == true)return;
|
||||
if(read(addr, r) == true) return;
|
||||
|
||||
mask[addr >> 3] &= ~(1 << (addr & 7));
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
|
||||
uint mirror;
|
||||
for(int x = 0; x <= 0x3f; x++) {
|
||||
unsigned mirror;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
mask[mirror >> 3] &= ~(1 << (mirror & 7));
|
||||
mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
@@ -130,12 +159,12 @@ void Cheat::clear(uint32 addr) {
|
||||
* when true, cheat code substitution value is stored in data.
|
||||
*****/
|
||||
|
||||
bool Cheat::read(uint32 addr, uint8 &data) {
|
||||
bool Cheat::read(unsigned addr, uint8 &data) const {
|
||||
addr = mirror_address(addr);
|
||||
for(int i = 0; i < cheat_count; i++) {
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
if(enabled(i) == false) continue;
|
||||
if(addr == mirror_address(index[i].addr)) {
|
||||
data = index[i].data;
|
||||
if(addr == mirror_address(code[i].addr)) {
|
||||
data = code[i].data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -147,99 +176,77 @@ bool Cheat::read(uint32 addr, uint8 &data) {
|
||||
* update_cheat_status() will scan to see if any codes are
|
||||
* enabled. if any are, make sure the cheat system is on.
|
||||
* otherwise, turn cheat system off to speed up emulation.
|
||||
*****/
|
||||
*****/
|
||||
|
||||
void Cheat::update_cheat_status() {
|
||||
for(unsigned i = 0; i < cheat_count; i++) {
|
||||
if(index[i].enabled) {
|
||||
cheat_enabled = true;
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
if(code[i].enabled) {
|
||||
cheat_system_enabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
cheat_enabled = false;
|
||||
cheat_system_enabled = false;
|
||||
}
|
||||
|
||||
/*****
|
||||
* cheat list manipulation routines
|
||||
*****/
|
||||
|
||||
bool Cheat::add(bool enable, char *code, char *desc) {
|
||||
if(cheat_count >= CheatLimit) return false;
|
||||
bool Cheat::add(bool enable, const char *code_, const char *desc_) {
|
||||
unsigned addr;
|
||||
uint8 data;
|
||||
type_t type;
|
||||
if(decode(code_, addr, data, type) == false) return false;
|
||||
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false) return false;
|
||||
|
||||
index[cheat_count].enabled = enable;
|
||||
index[cheat_count].addr = addr;
|
||||
index[cheat_count].data = data;
|
||||
len = strlen(code);
|
||||
len = len > 16 ? 16 : len;
|
||||
memcpy(index[cheat_count].code, code, len);
|
||||
index[cheat_count].code[len] = 0;
|
||||
len = strlen(desc);
|
||||
len = len > 128 ? 128 : len;
|
||||
memcpy(index[cheat_count].desc, desc, len);
|
||||
index[cheat_count].desc[len] = 0;
|
||||
cheat_count++;
|
||||
unsigned n = code.size();
|
||||
code[n].enabled = enable;
|
||||
code[n].addr = addr;
|
||||
code[n].data = data;
|
||||
code[n].code = code_;
|
||||
code[n].desc = desc_;
|
||||
(enable) ? set(addr) : clear(addr);
|
||||
|
||||
update_cheat_status();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cheat::edit(uint32 n, bool enable, char *code, char *desc) {
|
||||
if(n >= cheat_count) return false;
|
||||
|
||||
uint32 addr, len;
|
||||
uint8 data, type;
|
||||
if(decode(code, addr, data, type) == false) return false;
|
||||
bool Cheat::edit(unsigned n, bool enable, const char *code_, const char *desc_) {
|
||||
unsigned addr;
|
||||
uint8 data;
|
||||
type_t type;
|
||||
if(decode(code_, addr, data, type) == false) return false;
|
||||
|
||||
//disable current code and clear from code lookup table
|
||||
index[n].enabled = false;
|
||||
clear(index[n].addr);
|
||||
code[n].enabled = false;
|
||||
clear(code[n].addr);
|
||||
|
||||
//update code and enable in code lookup table
|
||||
index[n].enabled = enable;
|
||||
index[n].addr = addr;
|
||||
index[n].data = data;
|
||||
len = strlen(code);
|
||||
len = len > 16 ? 16 : len;
|
||||
memcpy(index[n].code, code, len);
|
||||
index[n].code[len] = 0;
|
||||
len = strlen(desc);
|
||||
len = len > 128 ? 128 : len;
|
||||
memcpy(index[n].desc, desc, len);
|
||||
index[n].desc[len] = 0;
|
||||
code[n].enabled = enable;
|
||||
code[n].addr = addr;
|
||||
code[n].data = data;
|
||||
code[n].code = code_;
|
||||
code[n].desc = desc_;
|
||||
set(addr);
|
||||
|
||||
update_cheat_status();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cheat::remove(uint32 n) {
|
||||
if(n >= cheat_count) return false;
|
||||
bool Cheat::remove(unsigned n) {
|
||||
unsigned size = code.size();
|
||||
if(n >= size) return false; //also verifies size cannot be < 1
|
||||
|
||||
for(unsigned i = n; i < cheat_count; i++) {
|
||||
index[i].enabled = index[i + 1].enabled;
|
||||
index[i].addr = index[i + 1].addr;
|
||||
index[i].data = index[i + 1].data;
|
||||
strcpy(index[i].desc, index[i + 1].desc);
|
||||
}
|
||||
|
||||
cheat_count--;
|
||||
for(unsigned i = n; i < size - 1; i++) code[i] = code[i + 1];
|
||||
code.resize(size - 1);
|
||||
|
||||
update_cheat_status();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc) {
|
||||
if(n >= cheat_count) return false;
|
||||
enable = index[n].enabled;
|
||||
addr = index[n].addr;
|
||||
data = index[n].data;
|
||||
strcpy(code, index[n].code);
|
||||
strcpy(desc, index[n].desc);
|
||||
bool Cheat::get(unsigned n, cheat_t &cheat) const {
|
||||
if(n >= code.size()) return false;
|
||||
|
||||
cheat = code[n];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -247,22 +254,23 @@ bool Cheat::get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, c
|
||||
* code status modifier routines
|
||||
*****/
|
||||
|
||||
bool Cheat::enabled(uint32 n) {
|
||||
if(n >= cheat_count) return false;
|
||||
return index[n].enabled;
|
||||
bool Cheat::enabled(unsigned n) const {
|
||||
return (n < code.size()) ? code[n].enabled : false;
|
||||
}
|
||||
|
||||
void Cheat::enable(uint32 n) {
|
||||
if(n >= cheat_count) return;
|
||||
index[n].enabled = true;
|
||||
set(index[n].addr);
|
||||
void Cheat::enable(unsigned n) {
|
||||
if(n >= code.size()) return;
|
||||
|
||||
code[n].enabled = true;
|
||||
set(code[n].addr);
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
void Cheat::disable(uint32 n) {
|
||||
if(n >= cheat_count) return;
|
||||
index[n].enabled = false;
|
||||
clear(index[n].addr);
|
||||
void Cheat::disable(unsigned n) {
|
||||
if(n >= code.size()) return;
|
||||
|
||||
code[n].enabled = false;
|
||||
clear(code[n].addr);
|
||||
update_cheat_status();
|
||||
}
|
||||
|
||||
@@ -288,40 +296,39 @@ bool Cheat::load(const char *fn) {
|
||||
split(part, ",", line[i]);
|
||||
if(::count(part) != 3) continue;
|
||||
trim(part[2], "\"");
|
||||
add(part[1] == "enabled", part[0](), part[2]());
|
||||
add(part[1] == "enabled", part[0], part[2]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cheat::save(const char *fn) {
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
if(!fp) return false;
|
||||
for(unsigned i = 0; i < cheat_count; i++) {
|
||||
fprintf(fp, "%9s = %8s, \"%s\"\r\n",
|
||||
index[i].code,
|
||||
index[i].enabled ? "enabled" : "disabled",
|
||||
index[i].desc);
|
||||
bool Cheat::save(const char *fn) const {
|
||||
file fp;
|
||||
if(!fp.open(fn, file::mode_write)) return false;
|
||||
for(unsigned i = 0; i < code.size(); i++) {
|
||||
fp.print(string()
|
||||
<< code[i].code << " = "
|
||||
<< (code[i].enabled ? "enabled" : "disabled") << ", "
|
||||
<< "\"" << code[i].desc << "\""
|
||||
<< "\r\n");
|
||||
}
|
||||
fclose(fp);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****
|
||||
* initialization routines
|
||||
*****/
|
||||
void Cheat::sort() {
|
||||
if(code.size() <= 1) return; //nothing to sort?
|
||||
cheat_t *buffer = new cheat_t[code.size()];
|
||||
for(unsigned i = 0; i < code.size(); i++) buffer[i] = code[i];
|
||||
nall::sort(buffer, code.size());
|
||||
for(unsigned i = 0; i < code.size(); i++) code[i] = buffer[i];
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void Cheat::clear() {
|
||||
cheat_enabled = false;
|
||||
cheat_count = 0;
|
||||
cheat_system_enabled = false;
|
||||
memset(mask, 0, 0x200000);
|
||||
for(unsigned i = 0; i <= CheatLimit; i++) {
|
||||
index[i].enabled = false;
|
||||
index[i].addr = 0x000000;
|
||||
index[i].data = 0x00;
|
||||
strcpy(index[i].code, "");
|
||||
strcpy(index[i].desc, "");
|
||||
}
|
||||
code.reset();
|
||||
}
|
||||
|
||||
Cheat::Cheat() {
|
||||
|
||||
+42
-38
@@ -1,51 +1,55 @@
|
||||
class Cheat {
|
||||
public:
|
||||
enum { CheatLimit = 1024 };
|
||||
|
||||
enum Type {
|
||||
ProActionReplay,
|
||||
GameGenie,
|
||||
public:
|
||||
enum type_t {
|
||||
ProActionReplay,
|
||||
GameGenie,
|
||||
};
|
||||
|
||||
struct CheatIndex {
|
||||
bool enabled;
|
||||
uint32 addr;
|
||||
uint8 data;
|
||||
char code[ 16 + 1];
|
||||
char desc[128 + 1];
|
||||
} index[CheatLimit + 1];
|
||||
|
||||
bool cheat_enabled;
|
||||
uint32 cheat_count;
|
||||
uint8 mask[0x200000];
|
||||
struct cheat_t {
|
||||
bool enabled;
|
||||
unsigned addr;
|
||||
uint8 data;
|
||||
string code;
|
||||
string desc;
|
||||
|
||||
inline bool enabled() { return cheat_enabled; }
|
||||
inline uint count() { return cheat_count; }
|
||||
inline bool exists(uint32 addr) { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
|
||||
cheat_t& operator=(const cheat_t&);
|
||||
bool operator<(const cheat_t&);
|
||||
};
|
||||
|
||||
bool decode(char *str, uint32 &addr, uint8 &data, uint8 &type);
|
||||
bool encode(char *str, uint32 addr, uint8 data, uint8 type);
|
||||
static bool decode(const char *str, unsigned &addr, uint8 &data, type_t &type);
|
||||
static bool encode(string &str, unsigned addr, uint8 data, type_t type);
|
||||
|
||||
bool read(uint32 addr, uint8 &data);
|
||||
inline bool enabled() const { return cheat_system_enabled; }
|
||||
inline unsigned count() const { return code.size(); }
|
||||
inline bool exists(unsigned addr) const { return bool(mask[addr >> 3] & 1 << (addr & 7)); }
|
||||
|
||||
bool read(unsigned addr, uint8 &data) const;
|
||||
|
||||
bool add(bool enable, const char *code, const char *desc);
|
||||
bool edit(unsigned n, bool enable, const char *code, const char *desc);
|
||||
bool get(unsigned n, cheat_t &cheat) const;
|
||||
bool remove(unsigned n);
|
||||
bool enabled(unsigned n) const;
|
||||
void enable(unsigned n);
|
||||
void disable(unsigned n);
|
||||
|
||||
void update_cheat_status();
|
||||
bool add(bool enable, char *code, char *desc);
|
||||
bool edit(uint32 n, bool enable, char *code, char *desc);
|
||||
bool get(uint32 n, bool &enable, uint32 &addr, uint8 &data, char *code, char *desc);
|
||||
bool remove(uint32 n);
|
||||
bool enabled(uint32 n);
|
||||
void enable(uint32 n);
|
||||
void disable(uint32 n);
|
||||
bool load(const char *fn);
|
||||
bool save(const char *fn);
|
||||
bool save(const char *fn) const;
|
||||
|
||||
void sort();
|
||||
void clear();
|
||||
|
||||
Cheat();
|
||||
|
||||
private:
|
||||
uint mirror_address(uint addr);
|
||||
void set(uint32 addr);
|
||||
void clear(uint32 addr);
|
||||
Cheat();
|
||||
|
||||
private:
|
||||
bool cheat_system_enabled;
|
||||
uint8 mask[0x200000];
|
||||
vector<cheat_t> code;
|
||||
|
||||
void update_cheat_status();
|
||||
unsigned mirror_address(unsigned addr) const;
|
||||
void set(unsigned addr);
|
||||
void clear(unsigned addr);
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
||||
@@ -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"
|
||||
|
||||
+33
-52
@@ -8,80 +8,61 @@ void OBC1::power() {
|
||||
}
|
||||
|
||||
void OBC1::reset() {
|
||||
for(uint i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff);
|
||||
for(unsigned i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff);
|
||||
|
||||
status.baseptr = (ram_read(0x1ff5) & 1) ? 0x1800 : 0x1c00;
|
||||
status.address = (ram_read(0x1ff6) & 0x7f);
|
||||
status.shift = (ram_read(0x1ff6) & 3) << 1;
|
||||
}
|
||||
|
||||
uint8 OBC1::read(uint addr) {
|
||||
uint8 OBC1::read(unsigned addr) {
|
||||
addr &= 0x1fff;
|
||||
if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0x1ff0:
|
||||
return ram_read(status.baseptr + (status.address << 2) + 0);
|
||||
case 0x1ff1:
|
||||
return ram_read(status.baseptr + (status.address << 2) + 1);
|
||||
case 0x1ff2:
|
||||
return ram_read(status.baseptr + (status.address << 2) + 2);
|
||||
case 0x1ff3:
|
||||
return ram_read(status.baseptr + (status.address << 2) + 3);
|
||||
case 0x1ff4:
|
||||
return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
case 0x1ff5:
|
||||
case 0x1ff6:
|
||||
case 0x1ff7:
|
||||
return ram_read(addr);
|
||||
switch(addr) { default: //never used, avoids compiler warning
|
||||
case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0);
|
||||
case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1);
|
||||
case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2);
|
||||
case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3);
|
||||
case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
case 0x1ff5: case 0x1ff6: case 0x1ff7: return ram_read(addr);
|
||||
}
|
||||
|
||||
return 0x00; //never used, avoids compiler warning
|
||||
}
|
||||
|
||||
void OBC1::write(uint addr, uint8 data) {
|
||||
void OBC1::write(unsigned addr, uint8 data) {
|
||||
addr &= 0x1fff;
|
||||
if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data);
|
||||
|
||||
switch(addr) {
|
||||
case 0x1ff0:
|
||||
ram_write(status.baseptr + (status.address << 2) + 0, data);
|
||||
break;
|
||||
case 0x1ff1:
|
||||
ram_write(status.baseptr + (status.address << 2) + 1, data);
|
||||
break;
|
||||
case 0x1ff2:
|
||||
ram_write(status.baseptr + (status.address << 2) + 2, data);
|
||||
break;
|
||||
case 0x1ff3:
|
||||
ram_write(status.baseptr + (status.address << 2) + 3, data);
|
||||
break;
|
||||
case 0x1ff4: {
|
||||
uint8 temp;
|
||||
temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
||||
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
||||
} break;
|
||||
case 0x1ff5:
|
||||
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
||||
ram_write(addr, data);
|
||||
break;
|
||||
case 0x1ff6:
|
||||
status.address = (data & 0x7f);
|
||||
status.shift = (data & 3) << 1;
|
||||
ram_write(addr, data);
|
||||
break;
|
||||
case 0x1ff7:
|
||||
ram_write(addr, data);
|
||||
break;
|
||||
case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break;
|
||||
case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break;
|
||||
case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break;
|
||||
case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break;
|
||||
case 0x1ff4: {
|
||||
uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
||||
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
||||
} break;
|
||||
case 0x1ff5: {
|
||||
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
case 0x1ff6: {
|
||||
status.address = (data & 0x7f);
|
||||
status.shift = (data & 3) << 1;
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
case 0x1ff7: {
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 OBC1::ram_read(uint addr) {
|
||||
uint8 OBC1::ram_read(unsigned addr) {
|
||||
return memory::cartram.read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
void OBC1::ram_write(uint addr, uint8 data) {
|
||||
void OBC1::ram_write(unsigned addr, uint8 data) {
|
||||
memory::cartram.write(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,15 @@ public:
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 read(uint addr);
|
||||
void write(uint addr, uint8 data);
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
OBC1();
|
||||
~OBC1();
|
||||
|
||||
private:
|
||||
uint8 ram_read(uint addr);
|
||||
void ram_write(uint addr, uint8 data);
|
||||
uint8 ram_read(unsigned addr);
|
||||
void ram_write(unsigned addr, uint8 data);
|
||||
|
||||
struct {
|
||||
uint16 address;
|
||||
|
||||
@@ -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(unsigned 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(unsigned 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 (unsigned addr);
|
||||
void mmio_write(unsigned 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;
|
||||
+41
-33
@@ -3,22 +3,22 @@ namespace config {
|
||||
configuration& config() {
|
||||
static configuration config;
|
||||
return config;
|
||||
}
|
||||
|
||||
integral_setting File::autodetect_type(config(), "file.autodetect_type",
|
||||
"Auto-detect file type by inspecting file header, rather than by file extension.\n"
|
||||
"In other words, if a .zip file is renamed to .smc, it will still be correctly\n"
|
||||
"identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n"
|
||||
"chance of a false detection when loading an uncompressed image file, if this\n"
|
||||
"option is enabled.",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
|
||||
"UPS patches contain CRC32s to validate that a patch was applied successfully.\n"
|
||||
"By default, if this validation fails, said patch will not be applied.\n"
|
||||
"Setting this option to true will bypass the validation,\n"
|
||||
"which may or may not result in a working image.\n"
|
||||
"Enabling this option is strongly discouraged.",
|
||||
}
|
||||
|
||||
integral_setting File::autodetect_type(config(), "file.autodetect_type",
|
||||
"Auto-detect file type by inspecting file header, rather than by file extension.\n"
|
||||
"In other words, if a .zip file is renamed to .smc, it will still be correctly\n"
|
||||
"identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n"
|
||||
"chance of a false detection when loading an uncompressed image file, if this\n"
|
||||
"option is enabled.",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32",
|
||||
"UPS patches contain CRC32s to validate that a patch was applied successfully.\n"
|
||||
"By default, if this validation fails, said patch will not be applied.\n"
|
||||
"Setting this option to true will bypass the validation,\n"
|
||||
"which may or may not result in a working image.\n"
|
||||
"Enabling this option is strongly discouraged.",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
string file_updatepath(const char *req_file, const char *req_path) {
|
||||
@@ -48,25 +48,36 @@ string_setting Path::base("path.base", "Path that bsnes resides in", "");
|
||||
string_setting Path::user("path.user", "Path to user folder", "");
|
||||
|
||||
string_setting Path::rom(config(), "path.rom",
|
||||
"Default path to look for ROM files in (\"\" = use default directory)", "");
|
||||
string_setting Path::patch(config(), "path.patch",
|
||||
"Default path to look for ROM files in (\"\" = use default directory)", "");
|
||||
string_setting Path::patch(config(), "path.patch",
|
||||
"Default path for all UPS patch files (\"\" = use current directory)", "");
|
||||
string_setting Path::save(config(), "path.save",
|
||||
"Default path for all save RAM files (\"\" = use current directory)", "");
|
||||
string_setting Path::cheat(config(), "path.cheat",
|
||||
"Default path for all cheat files (\"\" = use current directory)", "");
|
||||
string_setting Path::save(config(), "path.save",
|
||||
"Default path for all save RAM files (\"\" = use current directory)", "");
|
||||
string_setting Path::cheat(config(), "path.cheat",
|
||||
"Default path for all cheat files (\"\" = use current directory)", "");
|
||||
string_setting Path::bsx(config(), "path.bsx", "", "");
|
||||
string_setting Path::st(config(), "path.st", "", "");
|
||||
|
||||
integral_setting SNES::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 SNES::expansion_port(config(), "snes.expansion_port",
|
||||
"Device attached to SNES expansion port\n"
|
||||
"0 = None\n"
|
||||
"1 = Satellaview BS-X\n"
|
||||
"", integral_setting::decimal, ::SNES::ExpansionBSX);
|
||||
integral_setting SNES::region(config(), "snes.region",
|
||||
"SNES regional model\n"
|
||||
"0 = Auto-detect based on cartridge\n"
|
||||
"1 = NTSC\n"
|
||||
"2 = PAL\n"
|
||||
"", integral_setting::decimal, ::SNES::Autodetect);
|
||||
|
||||
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 +92,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, 32041 * 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, 32041 * 768);
|
||||
|
||||
integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position",
|
||||
"Approximate HCLOCK position to render at for scanline-based renderers",
|
||||
@@ -127,4 +135,4 @@ integral_setting PPU::oam_pri1_enable("ppu.oam_pri1_enable", "Enable OAM Priorit
|
||||
integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true);
|
||||
integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true);
|
||||
|
||||
} //namespace config
|
||||
} //namespace config
|
||||
+3
-2
@@ -15,14 +15,15 @@ extern struct Path {
|
||||
} path;
|
||||
|
||||
extern struct SNES {
|
||||
static integral_setting controller_port0;
|
||||
static integral_setting controller_port1;
|
||||
static integral_setting controller_port2;
|
||||
static integral_setting expansion_port;
|
||||
static integral_setting region;
|
||||
} 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
|
||||
+237
-236
@@ -1,255 +1,256 @@
|
||||
#ifdef SCPU_CPP
|
||||
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
|
||||
unsigned sCPU::dma_counter() {
|
||||
return (status.dma_counter + status.hcounter) & 7;
|
||||
}
|
||||
|
||||
/*****
|
||||
* One PPU dot = 4 CPU clocks
|
||||
*
|
||||
* PPU dots 323 and 327 are 6 CPU clocks long.
|
||||
* This does not apply to NTSC non-interlace scanline 240 on odd fields. This is
|
||||
* because the PPU skips one dot to alter the color burst phase of the video signal.
|
||||
*
|
||||
* 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 \
|
||||
)
|
||||
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
|
||||
uint16 sCPU::vcounter() { return status.vcounter; }
|
||||
uint16 sCPU::hcounter() { return status.hcounter; }
|
||||
uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
|
||||
|
||||
/*****
|
||||
* One PPU dot = 4 CPU clocks
|
||||
*
|
||||
* PPU dots 323 and 327 are 6 CPU clocks long.
|
||||
* This does not apply to NTSC non-interlace scanline 240 on odd fields. This is
|
||||
* because the PPU skips one dot to alter the color burst phase of the video signal.
|
||||
*
|
||||
* Dot 323 range = { 1292, 1294, 1296 }
|
||||
* Dot 327 range = { 1310, 1312, 1314 }
|
||||
*****/
|
||||
uint16 sCPU::hdot() {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||
}
|
||||
|
||||
void sCPU::add_clocks(uint clocks) {
|
||||
if(status.dram_refreshed == false) {
|
||||
if(status.hcounter + clocks >= status.dram_refresh_position) {
|
||||
status.dram_refreshed = true;
|
||||
clocks += 40;
|
||||
}
|
||||
}
|
||||
|
||||
counter.sub(counter.irq_delay, clocks);
|
||||
scheduler.addclocks_cpu(clocks);
|
||||
|
||||
clocks >>= 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;
|
||||
}
|
||||
|
||||
void sCPU::add_clocks(unsigned clocks) {
|
||||
if(status.dram_refreshed == false) {
|
||||
if(status.hcounter + clocks >= status.dram_refresh_position) {
|
||||
status.dram_refreshed = true;
|
||||
clocks += 40;
|
||||
}
|
||||
}
|
||||
|
||||
counter.sub(counter.irq_delay, clocks);
|
||||
scheduler.addclocks_cpu(clocks);
|
||||
|
||||
clocks >>= 1;
|
||||
while(clocks--) {
|
||||
history.enqueue(status.vcounter, status.hcounter);
|
||||
status.hcounter += 2;
|
||||
if(status.hcounter >= status.line_clocks) scanline();
|
||||
poll_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::scanline() {
|
||||
status.hcounter = 0;
|
||||
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
|
||||
if(++status.vcounter >= status.field_lines) frame();
|
||||
status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360;
|
||||
|
||||
//dram refresh occurs once every scanline
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//hdma triggers once every visible scanline
|
||||
status.line_rendered = false;
|
||||
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
|
||||
|
||||
ppu.scanline();
|
||||
snes.scanline();
|
||||
|
||||
update_interrupts();
|
||||
|
||||
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
|
||||
snes.input.poll();
|
||||
run_auto_joypad_poll();
|
||||
}
|
||||
}
|
||||
|
||||
history.enqueue(status.vcounter, status.hcounter);
|
||||
status.hcounter += 2;
|
||||
if(status.hcounter >= status.line_clocks) scanline();
|
||||
poll_interrupts();
|
||||
snes.input.tick();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::scanline() {
|
||||
status.hcounter = 0;
|
||||
status.dma_counter = (status.dma_counter + status.line_clocks) & 7;
|
||||
if(++status.vcounter >= status.field_lines) frame();
|
||||
status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360;
|
||||
|
||||
//dram refresh occurs once every scanline
|
||||
status.dram_refreshed = false;
|
||||
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
|
||||
|
||||
//hdma triggers once every visible scanline
|
||||
status.line_rendered = false;
|
||||
status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true;
|
||||
|
||||
ppu.scanline();
|
||||
snes.scanline();
|
||||
|
||||
update_interrupts();
|
||||
|
||||
if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) {
|
||||
snes.input.poll();
|
||||
run_auto_joypad_poll();
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::frame() {
|
||||
ppu.frame();
|
||||
snes.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)
|
||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
if(cpu_version == 1) {
|
||||
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
|
||||
} else {
|
||||
status.hdmainit_trigger_position = 12 + dma_counter();
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* precycle_edge()
|
||||
*
|
||||
* Used for DMA/HDMA 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);
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* cycle_edge()
|
||||
*
|
||||
* Used to test for HDMA, which can trigger on the edge of every opcode cycle.
|
||||
*****/
|
||||
void sCPU::cycle_edge() {
|
||||
if(status.line_rendered == false) {
|
||||
if(status.hcounter >= status.line_render_position) {
|
||||
status.line_rendered = true;
|
||||
ppu.render_scanline();
|
||||
}
|
||||
}
|
||||
|
||||
if(status.hdmainit_triggered == false) {
|
||||
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status.hdma_triggered == false) {
|
||||
if(status.hcounter >= 1106) {
|
||||
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.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)
|
||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
if(cpu_version == 1) {
|
||||
status.hdmainit_trigger_position = 12 + 8 - dma_counter();
|
||||
} else {
|
||||
status.hdmainit_trigger_position = 12 + dma_counter();
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* precycle_edge()
|
||||
*
|
||||
* Used for H/DMA bus synchronization
|
||||
*****/
|
||||
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 H/DMA, which can trigger on the edge of every opcode cycle.
|
||||
*****/
|
||||
void sCPU::cycle_edge() {
|
||||
if(status.line_rendered == false) {
|
||||
if(status.hcounter >= status.line_render_position) {
|
||||
status.line_rendered = true;
|
||||
ppu.render_scanline();
|
||||
}
|
||||
}
|
||||
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_INACTIVE: break;
|
||||
if(status.hdmainit_triggered == false) {
|
||||
if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) {
|
||||
status.hdmainit_triggered = true;
|
||||
hdma_init_reset();
|
||||
if(hdma_enabled_channels()) {
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case DMASTATE_DMASYNC: {
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
} break;
|
||||
if(status.hdma_triggered == false) {
|
||||
if(status.hcounter >= 1104) {
|
||||
status.hdma_triggered = true;
|
||||
if(hdma_active_channels()) {
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case DMASTATE_RUN: {
|
||||
status.dma_state = DMASTATE_CPUSYNC;
|
||||
status.dma_clocks = 8 - dma_counter() + 8;
|
||||
add_clocks(status.dma_clocks);
|
||||
//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.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; }
|
||||
if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; }
|
||||
if(status.dma_pending) { dma_run(); status.dma_pending = false; }
|
||||
if(status.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();
|
||||
if(!dma_enabled_channels()) status.dma_state = DMA_CPUsync;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* last_cycle()
|
||||
*
|
||||
* Used to test for NMI/IRQ, which can trigger on the edge of every opcode.
|
||||
* Test one cycle early to simulate two-stage pipeline of x816 CPU.
|
||||
*
|
||||
* status.irq_delay is used to simulate hardware delay before interrupts can
|
||||
* trigger during certain events (immediately after DMA, writes to $4200, etc)
|
||||
*****/
|
||||
void sCPU::last_cycle() {
|
||||
if(counter.irq_delay) return;
|
||||
|
||||
status.nmi_pending |= nmi_test();
|
||||
status.irq_pending |= irq_test();
|
||||
|
||||
event.irq = (status.nmi_pending || status.irq_pending);
|
||||
}
|
||||
|
||||
void sCPU::timing_power() {
|
||||
}
|
||||
|
||||
void sCPU::timing_reset() {
|
||||
counter.nmi_hold = 0;
|
||||
counter.irq_hold = 0;
|
||||
|
||||
counter.nmi_fire = 0;
|
||||
counter.irq_fire = 0;
|
||||
counter.irq_delay = 0;
|
||||
counter.hw_math = 0;
|
||||
|
||||
status.clock_count = 0;
|
||||
|
||||
status.vcounter = 0;
|
||||
status.hcounter = 0;
|
||||
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
status.line_clocks = 1364;
|
||||
|
||||
status.line_rendered = false;
|
||||
status.line_render_position = min(1112U, (uint)config::ppu.hack.render_scanline_position);
|
||||
|
||||
status.dram_refreshed = false;
|
||||
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
status.hdmainit_trigger_position = 0;
|
||||
|
||||
status.hdma_triggered = false;
|
||||
|
||||
status.irq_delay = 0;
|
||||
|
||||
status.nmi_valid = false;
|
||||
status.nmi_line = false;
|
||||
status.nmi_transition = false;
|
||||
status.nmi_pending = false;
|
||||
|
||||
status.irq_valid = false;
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
status.irq_pending = false;
|
||||
|
||||
update_interrupts();
|
||||
|
||||
status.dma_counter = 0;
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdmainit_pending = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
history.reset();
|
||||
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
add_clocks(186);
|
||||
}
|
||||
|
||||
#undef ntsc_color_burst_phase_shift_scanline
|
||||
if(status.dma_state == DMA_Inactive) {
|
||||
if(status.dma_pending || status.hdma_pending) {
|
||||
status.dma_clocks = 0;
|
||||
status.dma_state = DMA_Run;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* last_cycle()
|
||||
*
|
||||
* Used to test for NMI/IRQ, which can trigger on the edge of every opcode.
|
||||
* Test one cycle early to simulate two-stage pipeline of x816 CPU.
|
||||
*
|
||||
* status.irq_delay is used to simulate hardware delay before interrupts can
|
||||
* trigger during certain events (immediately after DMA, writes to $4200, etc)
|
||||
*****/
|
||||
void sCPU::last_cycle() {
|
||||
if(counter.irq_delay) return;
|
||||
|
||||
status.nmi_pending |= nmi_test();
|
||||
status.irq_pending |= irq_test();
|
||||
|
||||
event.irq = (status.nmi_pending || status.irq_pending);
|
||||
}
|
||||
|
||||
void sCPU::timing_power() {
|
||||
}
|
||||
|
||||
void sCPU::timing_reset() {
|
||||
counter.nmi_hold = 0;
|
||||
counter.irq_hold = 0;
|
||||
|
||||
counter.nmi_fire = 0;
|
||||
counter.irq_fire = 0;
|
||||
counter.irq_delay = 0;
|
||||
counter.hw_math = 0;
|
||||
|
||||
status.clock_count = 0;
|
||||
|
||||
status.vcounter = 0;
|
||||
status.hcounter = 0;
|
||||
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
status.line_clocks = 1364;
|
||||
|
||||
status.line_rendered = false;
|
||||
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;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
status.hdmainit_trigger_position = 0;
|
||||
|
||||
status.hdma_triggered = false;
|
||||
|
||||
status.irq_delay = 0;
|
||||
|
||||
status.nmi_valid = false;
|
||||
status.nmi_line = false;
|
||||
status.nmi_transition = false;
|
||||
status.nmi_pending = false;
|
||||
|
||||
status.irq_valid = false;
|
||||
status.irq_line = false;
|
||||
status.irq_transition = false;
|
||||
status.irq_pending = false;
|
||||
|
||||
update_interrupts();
|
||||
|
||||
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();
|
||||
|
||||
//initial latch values for $213c/$213d
|
||||
//[x]0035 : [y]0000 (53.0 -> 212) [lda $2137]
|
||||
//[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137]
|
||||
add_clocks(186);
|
||||
}
|
||||
|
||||
#undef ntsc_color_burst_phase_shift_scanline
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
@@ -1,9 +1,9 @@
|
||||
uint16 vcounter();
|
||||
uint16 hcounter();
|
||||
alwaysinline uint16 vcounter() { return status.vcounter; }
|
||||
alwaysinline uint16 hcounter() { return status.hcounter; }
|
||||
uint16 hdot();
|
||||
uint dma_counter();
|
||||
unsigned dma_counter();
|
||||
|
||||
void add_clocks(uint clocks);
|
||||
void add_clocks(unsigned clocks);
|
||||
void scanline();
|
||||
void frame();
|
||||
|
||||
@@ -14,30 +14,30 @@
|
||||
|
||||
void timing_power();
|
||||
void timing_reset();
|
||||
|
||||
//timeshifting -- needed by NMI and IRQ timing
|
||||
struct History {
|
||||
struct Time {
|
||||
uint16 vcounter;
|
||||
uint16 hcounter;
|
||||
} time[32];
|
||||
unsigned index;
|
||||
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
|
||||
Time &t = time[index++];
|
||||
index &= 31;
|
||||
t.vcounter = vcounter;
|
||||
t.hcounter = hcounter;
|
||||
}
|
||||
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
|
||||
Time &t = time[(index - (offset >> 1)) & 31];
|
||||
vcounter = t.vcounter;
|
||||
hcounter = t.hcounter;
|
||||
}
|
||||
void reset() {
|
||||
index = 0;
|
||||
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
|
||||
}
|
||||
History() { reset(); }
|
||||
|
||||
//timeshifting -- needed by NMI and IRQ timing
|
||||
struct History {
|
||||
struct Time {
|
||||
uint16 vcounter;
|
||||
uint16 hcounter;
|
||||
} time[32];
|
||||
unsigned index;
|
||||
alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) {
|
||||
Time &t = time[index++];
|
||||
index &= 31;
|
||||
t.vcounter = vcounter;
|
||||
t.hcounter = hcounter;
|
||||
}
|
||||
alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) {
|
||||
Time &t = time[(index - (offset >> 1)) & 31];
|
||||
vcounter = t.vcounter;
|
||||
hcounter = t.hcounter;
|
||||
}
|
||||
void reset() {
|
||||
index = 0;
|
||||
for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0;
|
||||
}
|
||||
History() { reset(); }
|
||||
} history;
|
||||
|
||||
//irq.cpp
|
||||
|
||||
@@ -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[(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[(uint16)(addr + 0)];
|
||||
uint8 hi = memory::apuram[(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[(uint16)(addr + 0)] = s;
|
||||
memory::apuram[(uint16)(addr + 1)] = 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[(uint16)(addr + 0)];
|
||||
uint8 hi = memory::apuram[(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[(uint16)(v.brr_addr + v.brr_offset)];
|
||||
state.t_brr_header = memory::apuram[(uint16)(v.brr_addr)];
|
||||
}
|
||||
|
||||
void sDSP::voice_3c(voice_t &v) {
|
||||
|
||||
+18
-144
@@ -1,24 +1,24 @@
|
||||
/*
|
||||
bbase : version 0.14 ~byuu (2008-04-16)
|
||||
bbase : version 0.17 ~byuu (2008-10-19)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#ifndef BBASE_H
|
||||
#define BBASE_H
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#include <algorithm>
|
||||
using std::min;
|
||||
#define BBASE_H
|
||||
|
||||
#include <nall/stdint.hpp>
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
typedef unsigned uint;
|
||||
|
||||
#include <algorithm>
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
#include <assert.h>
|
||||
@@ -41,7 +41,7 @@ using std::max;
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
//disable libc deprecation warnings in MSVC 2k5+
|
||||
//disable libc deprecation warnings in MSVC 2k5+
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#define NOMINMAX
|
||||
@@ -52,17 +52,10 @@ using std::max;
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define getcwd _getcwd
|
||||
#define ftruncate _chsize
|
||||
#define mkdir _mkdir
|
||||
#define putenv _putenv
|
||||
#define rmdir _rmdir
|
||||
#define vsnprintf _vsnprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define usleep(n) Sleep(n / 1000)
|
||||
|
||||
static char *realpath(const char *file_name, char *resolved_name) {
|
||||
return _fullpath(resolved_name, file_name, PATH_MAX);
|
||||
}
|
||||
#else
|
||||
#define mkdir(path) (mkdir)(path, 0755);
|
||||
#endif
|
||||
|
||||
/*****
|
||||
@@ -83,123 +76,4 @@ using std::max;
|
||||
#define alwaysinline inline
|
||||
#endif
|
||||
|
||||
/*****
|
||||
* OS localization
|
||||
*****/
|
||||
|
||||
//userpath(output) retrieves path to user's home folder
|
||||
//output must be at least as large as PATH_MAX
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
static char *userpath(char *output) {
|
||||
strcpy(output, "."); //failsafe
|
||||
SHGetFolderPath(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, output);
|
||||
return output;
|
||||
}
|
||||
#else
|
||||
static char *userpath(char *output) {
|
||||
strcpy(output, "."); //failsafe
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) { strcpy(output, userinfo->pw_dir); }
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<int min, int max, typename T> inline T minmax(const T x) {
|
||||
return (x < (T)min) ? (T)min : (x > (T)max) ? (T)max : x;
|
||||
}
|
||||
|
||||
/*****
|
||||
* endian wrappers
|
||||
*****/
|
||||
|
||||
#ifndef ARCH_MSB
|
||||
//little-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||
#define order_lsb2(a,b) a,b
|
||||
#define order_lsb3(a,b,c) a,b,c
|
||||
#define order_lsb4(a,b,c,d) a,b,c,d
|
||||
#define order_lsb5(a,b,c,d,e) a,b,c,d,e
|
||||
#define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#define order_msb2(a,b) b,a
|
||||
#define order_msb3(a,b,c) c,b,a
|
||||
#define order_msb4(a,b,c,d) d,c,b,a
|
||||
#define order_msb5(a,b,c,d,e) e,d,c,b,a
|
||||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#else
|
||||
//big-endian: uint8[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||
#define order_lsb2(a,b) b,a
|
||||
#define order_lsb3(a,b,c) c,b,a
|
||||
#define order_lsb4(a,b,c,d) d,c,b,a
|
||||
#define order_lsb5(a,b,c,d,e) e,d,c,b,a
|
||||
#define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#define order_msb2(a,b) a,b
|
||||
#define order_msb3(a,b,c) a,b,c
|
||||
#define order_msb4(a,b,c,d) a,b,c,d
|
||||
#define order_msb5(a,b,c,d,e) a,b,c,d,e
|
||||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#endif
|
||||
|
||||
/*****
|
||||
* libc extensions
|
||||
*****/
|
||||
|
||||
//pseudo-random number generator
|
||||
static unsigned prng() {
|
||||
static unsigned n = 0;
|
||||
return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
static uint64 fget(FILE *fp, unsigned length = 1) {
|
||||
uint64 data = 0;
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
data |= fgetc(fp) << (i << 3);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void fput(FILE *fp, uint64 data, unsigned length = 1) {
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
fputc(data >> (i << 3), fp);
|
||||
}
|
||||
}
|
||||
|
||||
static bool fexists(const char *fn) {
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
if(!fp) return false;
|
||||
fclose(fp);
|
||||
fp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned fsize(FILE *fp) {
|
||||
if(!fp) return 0;
|
||||
unsigned pos = ftell(fp);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned size = ftell(fp);
|
||||
fseek(fp, pos, SEEK_SET);
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned fsize(const char *fn) {
|
||||
FILE *fp = fopen(fn, "rb");
|
||||
if(!fp) return 0;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned size = ftell(fp);
|
||||
fclose(fp);
|
||||
fp = 0;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int fresize(FILE *fp, long size) {
|
||||
return ftruncate(fileno(fp), size);
|
||||
}
|
||||
|
||||
#endif //ifndef BBASE_H
|
||||
@@ -1,8 +1,8 @@
|
||||
void hiro_pbutton_tick(pButton *p) {
|
||||
if(p->self.on_tick) p->self.on_tick(Event(Event::Tick, 0, &p->self));
|
||||
if(p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, 0, &p->self));
|
||||
}
|
||||
|
||||
void pButton::create(uint style, uint width, uint height, const char *text) {
|
||||
void pButton::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
button = gtk_button_new_with_label(text ? text : "");
|
||||
set_default_font(button);
|
||||
gtk_widget_set_size_request(button, width, height);
|
||||
@@ -13,6 +13,7 @@ void pButton::create(uint style, uint width, uint height, const char *text) {
|
||||
void pButton::set_text(const char *text) {
|
||||
if(!button) return;
|
||||
gtk_button_set_label(GTK_BUTTON(button), text ? text : "");
|
||||
set_default_font(button);
|
||||
}
|
||||
|
||||
pButton::pButton(Button &self_) : pFormControl(self_), self(self_) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pButton : public pFormControl {
|
||||
public:
|
||||
Button &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pButton(Button&);
|
||||
@@ -1,8 +1,8 @@
|
||||
void hiro_pcanvas_expose(pCanvas *p) {
|
||||
uint32_t *f = p->fbuffer;
|
||||
uint32_t *r = p->rbuffer;
|
||||
for(uint y = p->canvas->allocation.height; y; y--) {
|
||||
for(uint x = p->canvas->allocation.width; x; x--) {
|
||||
for(unsigned y = p->canvas->allocation.height; y; y--) {
|
||||
for(unsigned x = p->canvas->allocation.width; x; x--) {
|
||||
uint32_t p = *f++;
|
||||
*r++ = ((p << 16) & 0xff0000) + (p & 0x00ff00) + ((p >> 16) & 0x0000ff);
|
||||
}
|
||||
@@ -14,15 +14,32 @@ void hiro_pcanvas_expose(pCanvas *p) {
|
||||
GDK_RGB_DITHER_NONE, (guchar*)p->rbuffer, p->bpitch);
|
||||
}
|
||||
|
||||
void pCanvas::create(uint style, uint width, uint height) {
|
||||
gboolean hiro_pcanvas_button_press(GtkWidget *widget, GdkEventButton *event, pCanvas *p) {
|
||||
if(p->self.on_input && event->button < mouse::buttons) {
|
||||
p->self.on_input(event_t(event_t::Input, (mouse::button + event->button) + (1 << 16), &p->self));
|
||||
}
|
||||
return false; //do not propogate the event to other handlers
|
||||
}
|
||||
|
||||
gboolean hiro_pcanvas_button_release(GtkWidget *widget, GdkEventButton *event, pCanvas *p) {
|
||||
if(p->self.on_input && event->button < mouse::buttons) {
|
||||
p->self.on_input(event_t(event_t::Input, (mouse::button + event->button) + (0 << 16), &p->self));
|
||||
}
|
||||
return false; //do not propogate the event to other handlers
|
||||
}
|
||||
|
||||
void pCanvas::create(unsigned style, unsigned width, unsigned height) {
|
||||
canvas = gtk_drawing_area_new();
|
||||
resize(width, height);
|
||||
GdkColor color;
|
||||
color.pixel = color.red = color.green = color.blue = 0;
|
||||
gtk_widget_modify_bg(canvas, GTK_STATE_NORMAL, &color);
|
||||
gtk_widget_set_double_buffered(canvas, false);
|
||||
gtk_widget_add_events(canvas, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
|
||||
gtk_widget_show(canvas);
|
||||
g_signal_connect_swapped(G_OBJECT(canvas), "expose_event", G_CALLBACK(hiro_pcanvas_expose), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(canvas), "button_press_event", G_CALLBACK(hiro_pcanvas_button_press), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(canvas), "button_release_event", G_CALLBACK(hiro_pcanvas_button_release), (gpointer)this);
|
||||
}
|
||||
|
||||
void pCanvas::redraw() {
|
||||
@@ -54,7 +71,7 @@ pCanvas::~pCanvas() {
|
||||
|
||||
/* internal */
|
||||
|
||||
void pCanvas::resize(uint width, uint height) {
|
||||
void pCanvas::resize(unsigned width, unsigned height) {
|
||||
if(fbuffer) free(fbuffer);
|
||||
if(rbuffer) free(rbuffer);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pCanvas : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height);
|
||||
void create(unsigned style, unsigned width, unsigned height);
|
||||
void redraw();
|
||||
uint32_t* buffer();
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
//GTK+ RGB drawing function draws in xBGR format, so two buffers are needed ...
|
||||
uint32_t *fbuffer; //one for the xRGB image
|
||||
uint32_t *rbuffer; //one for the xBGR image
|
||||
uint bpitch;
|
||||
void resize(uint width, uint height);
|
||||
unsigned bpitch;
|
||||
void resize(unsigned width, unsigned height);
|
||||
GtkWidget* gtk_handle();
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
void hiro_pcheckbox_tick(pCheckbox *p) {
|
||||
if(!p->locked && p->self.on_tick) p->self.on_tick(Event(Event::Tick, p->checked(), &p->self));
|
||||
if(!p->locked && p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, p->checked(), &p->self));
|
||||
}
|
||||
|
||||
void pCheckbox::create(uint style, uint width, uint height, const char *text) {
|
||||
void pCheckbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
checkbox = gtk_check_button_new_with_label(text ? text : "");
|
||||
set_default_font(checkbox);
|
||||
gtk_widget_set_size_request(checkbox, width, height);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pCheckbox : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void check(bool state = true);
|
||||
void uncheck();
|
||||
@@ -1,8 +1,8 @@
|
||||
void hiro_pcombobox_change(pCombobox *p) {
|
||||
if(p->self.on_change) p->self.on_change(Event(Event::Change, p->get_selection(), &p->self));
|
||||
if(p->self.on_change) p->self.on_change(event_t(event_t::Change, p->get_selection(), &p->self));
|
||||
}
|
||||
|
||||
void pCombobox::create(uint style, uint width, uint height, const char *text) {
|
||||
void pCombobox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
combobox = gtk_combo_box_new_text();
|
||||
set_default_font(combobox);
|
||||
gtk_widget_set_size_request(combobox, width, height);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pCombobox : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void add_item(const char *text);
|
||||
int get_selection();
|
||||
void set_selection(int index);
|
||||
@@ -11,6 +11,6 @@ public:
|
||||
|
||||
/* internal */
|
||||
GtkWidget *combobox;
|
||||
uint counter;
|
||||
unsigned counter;
|
||||
GtkWidget* gtk_handle();
|
||||
};
|
||||
@@ -1,4 +1,10 @@
|
||||
void pEditbox::create(uint style, uint width, uint height, const char *text) {
|
||||
void hiro_peditbox_change(pEditbox *p) {
|
||||
if(p->self.on_change) {
|
||||
p->self.on_change(event_t(event_t::Change, 0, &p->self));
|
||||
}
|
||||
}
|
||||
|
||||
void pEditbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
multiline = bool(style & Editbox::Multiline);
|
||||
|
||||
if(multiline == false) {
|
||||
@@ -7,6 +13,7 @@ void pEditbox::create(uint style, uint width, uint height, const char *text) {
|
||||
gtk_entry_set_text(GTK_ENTRY(editbox), text ? text : "");
|
||||
gtk_widget_set_size_request(editbox, width, height);
|
||||
gtk_widget_show(editbox);
|
||||
g_signal_connect_swapped(G_OBJECT(editbox), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
|
||||
} else {
|
||||
GtkPolicyType hscroll = (style & Editbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
|
||||
(style & Editbox::HorizontalScrollNever) ? GTK_POLICY_NEVER :
|
||||
@@ -25,6 +32,7 @@ void pEditbox::create(uint style, uint width, uint height, const char *text) {
|
||||
gtk_text_buffer_set_text(buffer, text ? text : "", -1);
|
||||
gtk_widget_show(editbox);
|
||||
gtk_widget_show(scrollbox);
|
||||
g_signal_connect_swapped(G_OBJECT(buffer), "changed", G_CALLBACK(hiro_peditbox_change), (gpointer)this);
|
||||
}
|
||||
|
||||
set_default_font(editbox);
|
||||
@@ -38,7 +46,7 @@ void pEditbox::set_text(const char *text) {
|
||||
}
|
||||
}
|
||||
|
||||
uint pEditbox::get_text(char *text, uint length) {
|
||||
unsigned pEditbox::get_text(char *text, unsigned length) {
|
||||
if(multiline == false) {
|
||||
const char *temp = gtk_entry_get_text(GTK_ENTRY(editbox));
|
||||
return strlcpy(text, temp ? temp : "", length);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class pEditbox : public pFormControl {
|
||||
public:
|
||||
Editbox &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
uint get_text(char *text, uint length = -1U);
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
unsigned get_text(char *text, unsigned length = -1U);
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pEditbox(Editbox&);
|
||||
@@ -1,4 +1,4 @@
|
||||
void pFormControl::resize(uint width, uint height) {
|
||||
void pFormControl::resize(unsigned width, unsigned height) {
|
||||
gtk_widget_set_size_request(gtk_handle(), width, height);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pFormControl : public pWidget {
|
||||
public:
|
||||
virtual void resize(uint width, uint height);
|
||||
virtual void resize(unsigned width, unsigned height);
|
||||
void focus();
|
||||
bool focused();
|
||||
void enable(bool = true);
|
||||
@@ -1,4 +1,4 @@
|
||||
void pFrame::create(uint style, uint width, uint height, const char *text) {
|
||||
void pFrame::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
frame = gtk_frame_new(text ? text : "");
|
||||
set_default_font(frame);
|
||||
gtk_widget_set_size_request(frame, width, height);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pFrame : public pFormControl {
|
||||
public:
|
||||
Frame &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pFrame(Frame&);
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "hiro.h"
|
||||
#include "hiro.hpp"
|
||||
#include "port.cpp"
|
||||
|
||||
#include <nall/algorithm.hpp>
|
||||
using nall::min;
|
||||
@@ -40,16 +41,6 @@ static void set_default_font(GtkWidget *widget) {
|
||||
#include "slider.cpp"
|
||||
|
||||
void pHiro::init() {
|
||||
//simulate passing argc, argv to gtk_init()
|
||||
int argc = 1;
|
||||
char **argv;
|
||||
argv = (char**)malloc(1 * sizeof(char*));
|
||||
argv[0] = (char*)malloc(64 * sizeof(char));
|
||||
strcpy(argv[0], "./hiro");
|
||||
gtk_init(&argc, &argv);
|
||||
free(argv[0]);
|
||||
free(argv);
|
||||
|
||||
is_composited = false;
|
||||
*default_path = 0;
|
||||
screen = gdk_screen_get_default();
|
||||
@@ -158,11 +149,11 @@ bool pHiro::file_save(Window *focus, char *filename, const char *path, const cha
|
||||
return strcmp(filename, ""); //return true if filename exists
|
||||
}
|
||||
|
||||
uint pHiro::screen_width() {
|
||||
unsigned pHiro::screen_width() {
|
||||
return gdk_screen_width();
|
||||
}
|
||||
|
||||
uint pHiro::screen_height() {
|
||||
unsigned pHiro::screen_height() {
|
||||
return gdk_screen_height();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifndef HIRO_GTK_H
|
||||
#define HIRO_GTK_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <cairo.h>
|
||||
@@ -8,28 +12,30 @@
|
||||
#include <X11/extensions/dpms.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
extern int hiromain(int argc, const char *const argv[]);
|
||||
|
||||
namespace libhiro {
|
||||
|
||||
#include "widget.h"
|
||||
#include "window.h"
|
||||
#include "menucontrol.h"
|
||||
#include "menugroup.h"
|
||||
#include "menuitem.h"
|
||||
#include "menucheckitem.h"
|
||||
#include "menuradioitem.h"
|
||||
#include "menuseparator.h"
|
||||
#include "formcontrol.h"
|
||||
#include "frame.h"
|
||||
#include "canvas.h"
|
||||
#include "label.h"
|
||||
#include "button.h"
|
||||
#include "checkbox.h"
|
||||
#include "radiobox.h"
|
||||
#include "editbox.h"
|
||||
#include "listbox.h"
|
||||
#include "combobox.h"
|
||||
#include "progressbar.h"
|
||||
#include "slider.h"
|
||||
#include "widget.hpp"
|
||||
#include "window.hpp"
|
||||
#include "menucontrol.hpp"
|
||||
#include "menugroup.hpp"
|
||||
#include "menuitem.hpp"
|
||||
#include "menucheckitem.hpp"
|
||||
#include "menuradioitem.hpp"
|
||||
#include "menuseparator.hpp"
|
||||
#include "formcontrol.hpp"
|
||||
#include "frame.hpp"
|
||||
#include "canvas.hpp"
|
||||
#include "label.hpp"
|
||||
#include "button.hpp"
|
||||
#include "checkbox.hpp"
|
||||
#include "radiobox.hpp"
|
||||
#include "editbox.hpp"
|
||||
#include "listbox.hpp"
|
||||
#include "combobox.hpp"
|
||||
#include "progressbar.hpp"
|
||||
#include "slider.hpp"
|
||||
|
||||
class pHiro {
|
||||
public:
|
||||
@@ -43,8 +49,8 @@ public:
|
||||
bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
|
||||
uint screen_width();
|
||||
uint screen_height();
|
||||
unsigned screen_width();
|
||||
unsigned screen_height();
|
||||
|
||||
void enable_screensaver();
|
||||
void disable_screensaver();
|
||||
@@ -62,7 +68,7 @@ public:
|
||||
void set_default_path(const char*);
|
||||
bool is_screensaver_enabled;
|
||||
void screensaver_tick();
|
||||
uint16_t translate_key(uint key);
|
||||
uint16_t translate_key(unsigned key);
|
||||
};
|
||||
|
||||
pHiro& phiro();
|
||||
@@ -1,4 +1,4 @@
|
||||
uint16_t pHiro::translate_key(uint key) {
|
||||
uint16_t pHiro::translate_key(unsigned key) {
|
||||
switch(key) {
|
||||
case GDK_Escape: return keyboard::escape;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
void pLabel::create(uint style, uint width, uint height, const char *text) {
|
||||
void pLabel::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
label = gtk_label_new(text ? text : "");
|
||||
set_default_font(label);
|
||||
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pLabel : public pFormControl {
|
||||
public:
|
||||
Label &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pLabel(Label&);
|
||||
@@ -1,13 +1,13 @@
|
||||
void hiro_plistbox_change(pListbox *p) {
|
||||
if(p->listbox_selection == p->get_selection()) return;
|
||||
if(p->self.on_change) p->self.on_change(Event(Event::Change, p->listbox_selection = p->get_selection(), &p->self));
|
||||
if(p->self.on_change) p->self.on_change(event_t(event_t::Change, p->listbox_selection = p->get_selection(), &p->self));
|
||||
}
|
||||
|
||||
void hiro_plistbox_activate(pListbox *p) {
|
||||
if(p->self.on_activate) p->self.on_activate(Event(Event::Activate, p->listbox_selection = p->get_selection(), &p->self));
|
||||
if(p->self.on_activate) p->self.on_activate(event_t(event_t::Activate, p->listbox_selection = p->get_selection(), &p->self));
|
||||
}
|
||||
|
||||
void pListbox::create(uint style, uint width, uint height, const char *columns, const char *text) {
|
||||
void pListbox::create(unsigned style, unsigned width, unsigned height, const char *columns, const char *text) {
|
||||
bool header = style & Listbox::Header;
|
||||
GtkPolicyType hscroll = (style & Listbox::HorizontalScrollAlways) ? GTK_POLICY_ALWAYS :
|
||||
(style & Listbox::HorizontalScrollNever) ? GTK_POLICY_NEVER :
|
||||
@@ -24,7 +24,7 @@ void pListbox::create(uint style, uint width, uint height, const char *columns,
|
||||
split(list, "\t", columns);
|
||||
|
||||
GType *v = (GType*)malloc(count(list) * sizeof(GType));
|
||||
for(uint i = 0; i < count(list); i++) v[i] = G_TYPE_STRING;
|
||||
for(unsigned i = 0; i < count(list); i++) v[i] = G_TYPE_STRING;
|
||||
store = gtk_list_store_newv(count(list), v);
|
||||
free(v);
|
||||
|
||||
@@ -37,7 +37,7 @@ void pListbox::create(uint style, uint width, uint height, const char *columns,
|
||||
|
||||
//alternate colors for each listbox entry if there are multiple columns ...
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(listbox), count(list) >= 2 ? true : false);
|
||||
for(uint i = 0; i < count(list); i++) {
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
renderer = gtk_cell_renderer_text_new();
|
||||
column = gtk_tree_view_column_new_with_attributes(list[i], renderer, "text", i, (void*)0);
|
||||
column_list[column_list.size()] = column;
|
||||
@@ -46,7 +46,7 @@ void pListbox::create(uint style, uint width, uint height, const char *columns,
|
||||
|
||||
if(text && *text) {
|
||||
split(list, "\n", text);
|
||||
for(uint i = 0; i < count(list); i++) add_item(list[i]);
|
||||
for(unsigned i = 0; i < count(list); i++) add_item(list[i]);
|
||||
}
|
||||
|
||||
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(listbox), header);
|
||||
@@ -62,7 +62,7 @@ void pListbox::autosize_columns() {
|
||||
gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listbox));
|
||||
}
|
||||
|
||||
void pListbox::set_column_width(uint column, uint width) {
|
||||
void pListbox::set_column_width(unsigned column, unsigned width) {
|
||||
gtk_tree_view_column_set_min_width(column_list[column], width);
|
||||
gtk_tree_view_column_set_max_width(column_list[column], width);
|
||||
}
|
||||
@@ -71,14 +71,14 @@ void pListbox::add_item(const char *text) {
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
gtk_list_store_append(store, &iter);
|
||||
for(uint i = 0; i < count(list); i++) {
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
gtk_list_store_set(store, &iter, i, list[i](), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void pListbox::set_item(uint index, const char *text) {
|
||||
void pListbox::set_item(unsigned index, const char *text) {
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
for(uint i = 0; i <= index; i++) {
|
||||
for(unsigned i = 0; i <= index; i++) {
|
||||
i == 0 ?
|
||||
gtk_tree_model_get_iter_first(model, &iter) :
|
||||
gtk_tree_model_iter_next(model, &iter);
|
||||
@@ -86,7 +86,7 @@ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
|
||||
lstring list;
|
||||
split(list, "\t", text);
|
||||
for(uint i = 0; i < count(list); i++) {
|
||||
for(unsigned i = 0; i < count(list); i++) {
|
||||
gtk_list_store_set(store, &iter, i, list[i](), -1);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(listbox)
|
||||
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
if(gtk_tree_model_get_iter_first(model, &iter) == false) { return -1; }
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) { return 0; }
|
||||
for(uint i = 1; i < 100000; i++) {
|
||||
for(unsigned i = 1; i < 100000; i++) {
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) { return -1; }
|
||||
if(gtk_tree_selection_iter_is_selected(selection, &iter) == true) { return i; }
|
||||
}
|
||||
@@ -111,12 +111,12 @@ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(listbox));
|
||||
if(index < 0) { goto end; }
|
||||
if(gtk_tree_model_get_iter_first(model, &iter) == false) { goto end; }
|
||||
if(index == 0) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
|
||||
for(uint i = 1; i < 100000; i++) {
|
||||
for(unsigned i = 1; i < 100000; i++) {
|
||||
if(gtk_tree_model_iter_next(model, &iter) == false) { goto end; }
|
||||
if(index == i) { gtk_tree_selection_select_iter(selection, &iter); goto end; }
|
||||
}
|
||||
end:
|
||||
if(current != index) ;//{ owner->message(Message::Changed, (uintptr_t)this); }
|
||||
if(current != index); //{ owner->message(Message::Changed, (uintptr_t)this); }
|
||||
}
|
||||
|
||||
void pListbox::reset() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
class pListbox : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *columns = "", const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *columns = "", const char *text = "");
|
||||
void autosize_columns();
|
||||
void set_column_width(uint column, uint width);
|
||||
void set_column_width(unsigned column, unsigned width);
|
||||
void add_item(const char *text);
|
||||
void set_item(uint index, const char *text);
|
||||
void set_item(unsigned index, const char *text);
|
||||
int get_selection();
|
||||
void set_selection(int index);
|
||||
void reset();
|
||||
@@ -1,5 +1,5 @@
|
||||
void hiro_pmenucheckitem_tick(pMenuCheckItem *p) {
|
||||
if(!p->locked && p->self.on_tick) p->self.on_tick(Event(Event::Tick, p->checked(), &p->self));
|
||||
if(!p->locked && p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, p->checked(), &p->self));
|
||||
}
|
||||
|
||||
void pMenuCheckItem::create(const char *text) {
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,5 +1,5 @@
|
||||
void hiro_pmenuitem_tick(pMenuItem *p) {
|
||||
if(p->self.on_tick) p->self.on_tick(Event(Event::Tick, 0, &p->self));
|
||||
if(p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, 0, &p->self));
|
||||
}
|
||||
|
||||
void pMenuItem::create(const char *text) {
|
||||
|
||||
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
void hiro_pmenuradioitem_tick(pMenuRadioItem *p) {
|
||||
//GTK+ sends two messages: one for the activated radio item,
|
||||
//and one for the deactivated radio item. ignore the latter.
|
||||
if(!p->locked && p->checked() && p->self.on_tick) p->self.on_tick(Event(Event::Tick, 0, &p->self));
|
||||
if(!p->locked && p->checked() && p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, 0, &p->self));
|
||||
}
|
||||
|
||||
void pMenuRadioItem::create(MenuRadioItemGroup &group, const char *text) {
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,17 @@
|
||||
char* userpath(char *output) {
|
||||
struct passwd *userinfo = getpwuid(getuid());
|
||||
if(userinfo) { strcpy(output, userinfo->pw_dir); }
|
||||
return output;
|
||||
}
|
||||
|
||||
int mkdir(const char *path) {
|
||||
return mkdir(path, 0755);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
gtk_init(&argc, &argv);
|
||||
libhiro::hiro().init();
|
||||
int result = hiromain(argc, argv);
|
||||
libhiro::hiro().term();
|
||||
return result;
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
void pProgressbar::create(uint style, uint width, uint height) {
|
||||
void pProgressbar::create(unsigned style, unsigned width, unsigned height) {
|
||||
progressbar = gtk_progress_bar_new();
|
||||
gtk_widget_set_size_request(progressbar, width, height);
|
||||
gtk_widget_show(progressbar);
|
||||
}
|
||||
|
||||
uint pProgressbar::get_progress() {
|
||||
uint progress = (uint)(gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(progressbar)) * 100.0);
|
||||
unsigned pProgressbar::get_progress() {
|
||||
unsigned progress = (unsigned)(gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(progressbar)) * 100.0);
|
||||
return max(0U, min(progress, 100U));
|
||||
}
|
||||
|
||||
void pProgressbar::set_progress(uint progress) {
|
||||
void pProgressbar::set_progress(unsigned progress) {
|
||||
progress = max(0U, min(progress, 100U));
|
||||
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), (double)progress / 100.0);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class pProgressbar : public pFormControl {
|
||||
public:
|
||||
Progressbar &self;
|
||||
void create(uint style, uint width, uint height);
|
||||
uint get_progress();
|
||||
void set_progress(uint progress);
|
||||
void create(unsigned style, unsigned width, unsigned height);
|
||||
unsigned get_progress();
|
||||
void set_progress(unsigned progress);
|
||||
|
||||
pProgressbar(Progressbar&);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
void hiro_pradiobox_tick(pRadiobox *p) {
|
||||
//GTK+ sends two messages: one for the activated radiobox,
|
||||
//and one for the deactivated radiobox. ignore the latter.
|
||||
if(!p->locked && p->checked() && p->self.on_tick) p->self.on_tick(Event(Event::Tick, 0, &p->self));
|
||||
if(!p->locked && p->checked() && p->self.on_tick) p->self.on_tick(event_t(event_t::Tick, 0, &p->self));
|
||||
}
|
||||
|
||||
void pRadiobox::create(RadioboxGroup &group, uint style, uint width, uint height, const char *text) {
|
||||
void pRadiobox::create(RadioboxGroup &group, unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
if(group.size() == 0 || group[0] == &self) {
|
||||
radiobox = gtk_radio_button_new_with_label(0, text ? text : "");
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pRadiobox : public pFormControl {
|
||||
public:
|
||||
void create(RadioboxGroup &group, uint style, uint width, uint height, const char *text = "");
|
||||
void create(RadioboxGroup &group, unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void check();
|
||||
bool checked();
|
||||
@@ -1,9 +1,9 @@
|
||||
void hiro_pslider_change(pSlider *p) {
|
||||
if(p->slider_position == p->get_position()) return;
|
||||
if(p->self.on_change) p->self.on_change(Event(Event::Change, p->slider_position = p->get_position(), &p->self));
|
||||
if(p->self.on_change) p->self.on_change(event_t(event_t::Change, p->slider_position = p->get_position(), &p->self));
|
||||
}
|
||||
|
||||
void pSlider::create(uint style, uint width, uint height, uint length) {
|
||||
void pSlider::create(unsigned style, unsigned width, unsigned height, unsigned length) {
|
||||
if(length < 1) length = 1;
|
||||
if(style & Slider::Vertical) {
|
||||
slider = gtk_vscale_new_with_range(0, length - 1, 1);
|
||||
@@ -16,11 +16,11 @@ void pSlider::create(uint style, uint width, uint height, uint length) {
|
||||
g_signal_connect_swapped(G_OBJECT(slider), "value-changed", G_CALLBACK(hiro_pslider_change), (gpointer)this);
|
||||
}
|
||||
|
||||
uint pSlider::get_position() {
|
||||
return (uint)gtk_range_get_value(GTK_RANGE(slider));
|
||||
unsigned pSlider::get_position() {
|
||||
return (unsigned)gtk_range_get_value(GTK_RANGE(slider));
|
||||
}
|
||||
|
||||
void pSlider::set_position(uint position) {
|
||||
void pSlider::set_position(unsigned position) {
|
||||
gtk_range_set_value(GTK_RANGE(slider), position);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
class pSlider : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, uint length);
|
||||
uint get_position();
|
||||
void set_position(uint position);
|
||||
|
||||
Slider &self;
|
||||
pSlider(Slider&);
|
||||
|
||||
/* internal */
|
||||
GtkWidget *slider;
|
||||
uint slider_position;
|
||||
GtkWidget* gtk_handle();
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
class pSlider : public pFormControl {
|
||||
public:
|
||||
void create(unsigned style, unsigned width, unsigned height, unsigned length);
|
||||
unsigned get_position();
|
||||
void set_position(unsigned position);
|
||||
|
||||
Slider &self;
|
||||
pSlider(Slider&);
|
||||
|
||||
/* internal */
|
||||
GtkWidget *slider;
|
||||
unsigned slider_position;
|
||||
GtkWidget* gtk_handle();
|
||||
};
|
||||
File renamed without changes.
+21
-13
@@ -1,5 +1,5 @@
|
||||
static gint hiro_pwindow_close(pWindow *p) {
|
||||
uintptr_t r = p->self.on_close ? p->self.on_close(Event(Event::Close, 0, &p->self)) : true;
|
||||
uintptr_t r = p->self.on_close ? p->self.on_close(event_t(event_t::Close, 0, &p->self)) : true;
|
||||
return !bool(r);
|
||||
}
|
||||
|
||||
@@ -25,16 +25,24 @@ static gboolean hiro_pwindow_expose(pWindow *p) {
|
||||
}
|
||||
|
||||
static gint hiro_pwindow_keydown(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
||||
if(p && p->self.on_keydown) p->self.on_keydown(Event(Event::KeyDown, phiro().translate_key(key->keyval), &p->self));
|
||||
if(p && p->self.on_input) {
|
||||
p->self.on_input(event_t(event_t::Input,
|
||||
phiro().translate_key(key->keyval) + (1 << 16),
|
||||
&p->self));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint hiro_pwindow_keyup(GtkWidget *w, GdkEventKey *key, pWindow *p) {
|
||||
if(p && p->self.on_keyup) p->self.on_keyup(Event(Event::KeyUp, phiro().translate_key(key->keyval), &p->self));
|
||||
if(p && p->self.on_input) {
|
||||
p->self.on_input(event_t(event_t::Input,
|
||||
phiro().translate_key(key->keyval) + (0 << 16),
|
||||
&p->self));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void pWindow::create(uint style, uint width, uint height, const char *text) {
|
||||
void pWindow::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_colormap(window, phiro().colormap);
|
||||
|
||||
@@ -81,12 +89,12 @@ void pWindow::close() {
|
||||
gtk_widget_destroy(window);
|
||||
}
|
||||
|
||||
void pWindow::move(uint x, uint y) {
|
||||
void pWindow::move(unsigned x, unsigned y) {
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE);
|
||||
gtk_window_move(GTK_WINDOW(window), x, y);
|
||||
}
|
||||
|
||||
void pWindow::resize(uint width, uint height) {
|
||||
void pWindow::resize(unsigned width, unsigned height) {
|
||||
gtk_widget_set_size_request(formcontainer, width, height);
|
||||
state.width = width;
|
||||
state.height = height;
|
||||
@@ -132,14 +140,14 @@ void pWindow::unfullscreen() {
|
||||
//is unreliable, as it will usually report the previous window size.
|
||||
//therefore, calculate it manually by using state information.
|
||||
|
||||
uint pWindow::get_width() {
|
||||
unsigned pWindow::get_width() {
|
||||
if(state.is_fullscreen == false) return state.width;
|
||||
return gdk_screen_width();
|
||||
}
|
||||
|
||||
uint pWindow::get_height() {
|
||||
unsigned pWindow::get_height() {
|
||||
if(state.is_fullscreen == false) return state.height;
|
||||
uint height = gdk_screen_height();
|
||||
unsigned height = gdk_screen_height();
|
||||
|
||||
//do not include menubar height in client area height
|
||||
if(menu.visible()) {
|
||||
@@ -198,7 +206,7 @@ void pWindow::set_text(const char *text) {
|
||||
gtk_window_set_title(GTK_WINDOW(window), text ? text : "");
|
||||
}
|
||||
|
||||
void pWindow::attach(Window &window, uint x, uint y) {
|
||||
void pWindow::attach(Window &window, unsigned x, unsigned y) {
|
||||
window.p.owner = this;
|
||||
|
||||
//GTK+ does not support attaching a window to another window,
|
||||
@@ -216,15 +224,15 @@ void pWindow::attach(MenuGroup &menugroup) {
|
||||
gtk_widget_show(menubar);
|
||||
}
|
||||
|
||||
void pWindow::attach(FormControl &formcontrol, uint x, uint y) {
|
||||
void pWindow::attach(FormControl &formcontrol, unsigned x, unsigned y) {
|
||||
gtk_fixed_put(GTK_FIXED(formcontainer), formcontrol.p.gtk_handle(), x, y);
|
||||
}
|
||||
|
||||
void pWindow::move(Window &window, uint x, uint y) {
|
||||
void pWindow::move(Window &window, unsigned x, unsigned y) {
|
||||
gtk_fixed_move(GTK_FIXED(formcontainer), window.p.gtk_handle(), x, y);
|
||||
}
|
||||
|
||||
void pWindow::move(FormControl &formcontrol, uint x, uint y) {
|
||||
void pWindow::move(FormControl &formcontrol, unsigned x, unsigned y) {
|
||||
gtk_fixed_move(GTK_FIXED(formcontainer), formcontrol.p.gtk_handle(), x, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
class pWindow : public pWidget {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void close();
|
||||
void move(uint x, uint y);
|
||||
void resize(uint width, uint height);
|
||||
void move(unsigned x, unsigned y);
|
||||
void resize(unsigned width, unsigned height);
|
||||
void focus();
|
||||
bool focused();
|
||||
void fullscreen();
|
||||
void unfullscreen();
|
||||
uint get_width();
|
||||
uint get_height();
|
||||
unsigned get_width();
|
||||
unsigned get_height();
|
||||
void set_opacity(uint8_t opacity);
|
||||
void set_background_color(uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_icon(unsigned width, unsigned height, const uint32_t *data);
|
||||
void set_text(const char *text = "");
|
||||
void attach(Window &window, uint x, uint y);
|
||||
void attach(Window &window, unsigned x, unsigned y);
|
||||
void attach(MenuGroup &menugroup);
|
||||
void attach(FormControl &formcontrol, uint x, uint y);
|
||||
void move(Window &window, uint x, uint y);
|
||||
void move(FormControl &formcontrol, uint x, uint y);
|
||||
void attach(FormControl &formcontrol, unsigned x, unsigned y);
|
||||
void move(Window &window, unsigned x, unsigned y);
|
||||
void move(FormControl &formcontrol, unsigned x, unsigned y);
|
||||
|
||||
class Statusbar {
|
||||
public:
|
||||
@@ -55,9 +55,9 @@ public:
|
||||
GtkWidget* gtk_handle();
|
||||
struct {
|
||||
bool is_fullscreen;
|
||||
uint width;
|
||||
uint height;
|
||||
uint alpha;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned alpha;
|
||||
} state;
|
||||
|
||||
void menu_show(bool = true);
|
||||
+31
-31
@@ -1,4 +1,4 @@
|
||||
#include "hiro.h"
|
||||
#include "hiro.hpp"
|
||||
using namespace nall;
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -18,8 +18,8 @@ bool Hiro::pending() { return p.pending(); }
|
||||
bool Hiro::folder_select(Window *focus, char *filename, const char *path) { return p.folder_select(focus, filename, path); }
|
||||
bool Hiro::file_open(Window *focus, char *filename, const char *path, const char *filter) { return p.file_open(focus, filename, path, filter); }
|
||||
bool Hiro::file_save(Window *focus, char *filename, const char *path, const char *filter) { return p.file_save(focus, filename, path, filter); }
|
||||
uint Hiro::screen_width() { return p.screen_width(); }
|
||||
uint Hiro::screen_height() { return p.screen_height(); }
|
||||
unsigned Hiro::screen_width() { return p.screen_width(); }
|
||||
unsigned Hiro::screen_height() { return p.screen_height(); }
|
||||
void Hiro::enable_screensaver() { p.enable_screensaver(); }
|
||||
void Hiro::disable_screensaver() { p.disable_screensaver(); }
|
||||
Hiro& Hiro::handle() { static Hiro hiro; return hiro; }
|
||||
@@ -39,25 +39,25 @@ Widget::~Widget() { delete &p; }
|
||||
|
||||
/* Widget -> Window */
|
||||
|
||||
void Window::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Window::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Window::close() { p.close(); }
|
||||
void Window::move(uint x, uint y) { p.move(x, y); }
|
||||
void Window::resize(uint width, uint height) { p.resize(width, height); }
|
||||
void Window::move(unsigned x, unsigned y) { p.move(x, y); }
|
||||
void Window::resize(unsigned width, unsigned height) { p.resize(width, height); }
|
||||
void Window::focus() { p.focus(); }
|
||||
bool Window::focused() { return p.focused(); }
|
||||
void Window::fullscreen() { p.fullscreen(); }
|
||||
void Window::unfullscreen() { p.unfullscreen(); }
|
||||
uint Window::get_width() { return p.get_width(); }
|
||||
uint Window::get_height() { return p.get_height(); }
|
||||
unsigned Window::get_width() { return p.get_width(); }
|
||||
unsigned Window::get_height() { return p.get_height(); }
|
||||
void Window::set_opacity(uint8_t opacity) { p.set_opacity(opacity); }
|
||||
void Window::set_background_color(uint8_t r, uint8_t g, uint8_t b) { p.set_background_color(r, g, b); }
|
||||
void Window::set_icon(unsigned width, unsigned height, const uint32_t *data) { p.set_icon(width, height, data); }
|
||||
void Window::set_text(const char *text) { p.set_text(text); }
|
||||
void Window::attach(Window &window, uint x, uint y) { p.attach(window, x, y); }
|
||||
void Window::attach(Window &window, unsigned x, unsigned y) { p.attach(window, x, y); }
|
||||
void Window::attach(MenuGroup &menugroup) { p.attach(menugroup); }
|
||||
void Window::attach(FormControl &formcontrol, uint x, uint y) { p.attach(formcontrol, x, y); }
|
||||
void Window::move(Window &window, uint x, uint y) { p.move(window, x, y); }
|
||||
void Window::move(FormControl &formcontrol, uint x, uint y) { p.move(formcontrol, x, y); }
|
||||
void Window::attach(FormControl &formcontrol, unsigned x, unsigned y) { p.attach(formcontrol, x, y); }
|
||||
void Window::move(Window &window, unsigned x, unsigned y) { p.move(window, x, y); }
|
||||
void Window::move(FormControl &formcontrol, unsigned x, unsigned y) { p.move(formcontrol, x, y); }
|
||||
|
||||
void Window::Menubar::show(bool state) { p.menu.show(state); }
|
||||
void Window::Menubar::hide() { p.menu.hide(); }
|
||||
@@ -139,7 +139,7 @@ MenuSeparator::MenuSeparator() :
|
||||
|
||||
/* Widget -> FormControl */
|
||||
|
||||
void FormControl::resize(uint width, uint height) { p.resize(width, height); }
|
||||
void FormControl::resize(unsigned width, unsigned height) { p.resize(width, height); }
|
||||
void FormControl::focus() { p.focus(); }
|
||||
bool FormControl::focused() { return p.focused(); }
|
||||
void FormControl::enable(bool state) { p.enable(state); }
|
||||
@@ -156,7 +156,7 @@ FormControl::FormControl(pFormControl &p_) :
|
||||
|
||||
/* Widget -> FormControl -> Frame */
|
||||
|
||||
void Frame::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Frame::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Frame::set_text(const char *text) { p.set_text(text); }
|
||||
Frame::Frame() :
|
||||
base_from_member<pFrame&>(*new pFrame(*this)),
|
||||
@@ -165,7 +165,7 @@ Frame::Frame() :
|
||||
|
||||
/* Widget -> FormControl -> Canvas */
|
||||
|
||||
void Canvas::create(uint style, uint width, uint height) { p.create(style, width, height); }
|
||||
void Canvas::create(unsigned style, unsigned width, unsigned height) { p.create(style, width, height); }
|
||||
void Canvas::redraw() { p.redraw(); }
|
||||
uint32_t* Canvas::buffer() { return p.buffer(); }
|
||||
Canvas::Canvas() :
|
||||
@@ -175,7 +175,7 @@ Canvas::Canvas() :
|
||||
|
||||
/* Widget -> FormControl -> Label */
|
||||
|
||||
void Label::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Label::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Label::set_text(const char *text) { p.set_text(text); }
|
||||
Label::Label() :
|
||||
base_from_member<pLabel&>(*new pLabel(*this)),
|
||||
@@ -184,7 +184,7 @@ Label::Label() :
|
||||
|
||||
/* Widget -> FormControl -> Button */
|
||||
|
||||
void Button::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Button::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Button::set_text(const char *text) { p.set_text(text); }
|
||||
Button::Button() :
|
||||
base_from_member<pButton&>(*new pButton(*this)),
|
||||
@@ -193,7 +193,7 @@ Button::Button() :
|
||||
|
||||
/* Widget -> FormControl -> Checkbox */
|
||||
|
||||
void Checkbox::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Checkbox::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Checkbox::set_text(const char *text) { p.set_text(text); }
|
||||
void Checkbox::check(bool state) { state ? p.check() : p.uncheck(); }
|
||||
void Checkbox::uncheck() { p.uncheck(); }
|
||||
@@ -205,7 +205,7 @@ Checkbox::Checkbox() :
|
||||
|
||||
/* Widget -> FormControl -> Radiobox */
|
||||
|
||||
void Radiobox::create(RadioboxGroup &group, uint style, uint width, uint height, const char *text) { p.create(group, style, width, height, text); }
|
||||
void Radiobox::create(RadioboxGroup &group, unsigned style, unsigned width, unsigned height, const char *text) { p.create(group, style, width, height, text); }
|
||||
void Radiobox::set_text(const char *text) { p.set_text(text); }
|
||||
void Radiobox::check() { p.check(); }
|
||||
bool Radiobox::checked() { return p.checked(); }
|
||||
@@ -216,8 +216,8 @@ Radiobox::Radiobox() :
|
||||
|
||||
/* Widget -> FormControl -> Editbox */
|
||||
|
||||
void Editbox::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
uint Editbox::get_text(char *text, uint length) { return p.get_text(text, length); }
|
||||
void Editbox::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
unsigned Editbox::get_text(char *text, unsigned length) { return p.get_text(text, length); }
|
||||
void Editbox::set_text(const char *text) { p.set_text(text); }
|
||||
Editbox::Editbox() :
|
||||
base_from_member<pEditbox&>(*new pEditbox(*this)),
|
||||
@@ -226,11 +226,11 @@ Editbox::Editbox() :
|
||||
|
||||
/* Widget -> FormControl -> Listbox */
|
||||
|
||||
void Listbox::create(uint style, uint width, uint height, const char *columns, const char *text) { p.create(style, width, height, columns, text); }
|
||||
void Listbox::create(unsigned style, unsigned width, unsigned height, const char *columns, const char *text) { p.create(style, width, height, columns, text); }
|
||||
void Listbox::autosize_columns() { p.autosize_columns(); }
|
||||
void Listbox::set_column_width(uint column, uint width) { p.set_column_width(column, width); }
|
||||
void Listbox::set_column_width(unsigned column, unsigned width) { p.set_column_width(column, width); }
|
||||
void Listbox::add_item(const char *text) { p.add_item(text); }
|
||||
void Listbox::set_item(uint index, const char *text) { p.set_item(index, text); }
|
||||
void Listbox::set_item(unsigned index, const char *text) { p.set_item(index, text); }
|
||||
int Listbox::get_selection() { return p.get_selection(); }
|
||||
void Listbox::set_selection(int index) { p.set_selection(index); }
|
||||
void Listbox::reset() { p.reset(); }
|
||||
@@ -241,7 +241,7 @@ Listbox::Listbox() :
|
||||
|
||||
/* Widget -> FormControl -> Combobox */
|
||||
|
||||
void Combobox::create(uint style, uint width, uint height, const char *text) { p.create(style, width, height, text); }
|
||||
void Combobox::create(unsigned style, unsigned width, unsigned height, const char *text) { p.create(style, width, height, text); }
|
||||
void Combobox::add_item(const char *text) { p.add_item(text); }
|
||||
int Combobox::get_selection() { return p.get_selection(); }
|
||||
void Combobox::set_selection(int index) { p.set_selection(index); }
|
||||
@@ -253,9 +253,9 @@ Combobox::Combobox() :
|
||||
|
||||
/* Widget -> FormControl -> Progressbar */
|
||||
|
||||
void Progressbar::create(uint style, uint width, uint height) { p.create(style, width, height); }
|
||||
uint Progressbar::get_progress() { return p.get_progress(); }
|
||||
void Progressbar::set_progress(uint progress) { p.set_progress(progress); }
|
||||
void Progressbar::create(unsigned style, unsigned width, unsigned height) { p.create(style, width, height); }
|
||||
unsigned Progressbar::get_progress() { return p.get_progress(); }
|
||||
void Progressbar::set_progress(unsigned progress) { p.set_progress(progress); }
|
||||
Progressbar::Progressbar() :
|
||||
base_from_member<pProgressbar&>(*new pProgressbar(*this)),
|
||||
FormControl(base_from_member<pProgressbar&>::value),
|
||||
@@ -263,9 +263,9 @@ Progressbar::Progressbar() :
|
||||
|
||||
/* Widget -> FormControl -> Slider */
|
||||
|
||||
void Slider::create(uint style, uint width, uint height, uint length) { p.create(style, width, height, length); }
|
||||
uint Slider::get_position() { return p.get_position(); }
|
||||
void Slider::set_position(uint position) { p.set_position(position); }
|
||||
void Slider::create(unsigned style, unsigned width, unsigned height, unsigned length) { p.create(style, width, height, length); }
|
||||
unsigned Slider::get_position() { return p.get_position(); }
|
||||
void Slider::set_position(unsigned position) { p.set_position(position); }
|
||||
Slider::Slider() :
|
||||
base_from_member<pSlider&>(*new pSlider(*this)),
|
||||
FormControl(base_from_member<pSlider&>::value),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
hiro
|
||||
version: 0.004 (2008-05-14)
|
||||
version: 0.007b (2008-10-26)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
@@ -15,7 +15,10 @@
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
typedef unsigned int uint;
|
||||
|
||||
extern char* realpath(const char*, char*);
|
||||
extern char* userpath(char*);
|
||||
int mkdir(const char*);
|
||||
|
||||
namespace libhiro {
|
||||
|
||||
@@ -89,12 +92,11 @@ class Widget;
|
||||
typedef nall::array<MenuRadioItem*> MenuRadioItemGroup;
|
||||
typedef nall::array<Radiobox*> RadioboxGroup;
|
||||
|
||||
struct Event {
|
||||
enum Type {
|
||||
struct event_t {
|
||||
enum type_t {
|
||||
Close,
|
||||
Block,
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
Input,
|
||||
Change,
|
||||
Tick,
|
||||
Activate,
|
||||
@@ -102,7 +104,7 @@ struct Event {
|
||||
uintptr_t param;
|
||||
Widget *widget;
|
||||
|
||||
Event(Type type_, uintptr_t param_ = 0, Widget *widget_ = 0) :
|
||||
event_t(type_t type_, uintptr_t param_ = 0, Widget *widget_ = 0) :
|
||||
type(type_), param(param_), widget(widget_) {}
|
||||
};
|
||||
|
||||
@@ -117,8 +119,8 @@ public:
|
||||
bool file_open(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
bool file_save(Window *focus, char *filename, const char *path = "", const char *filter = "");
|
||||
|
||||
uint screen_width();
|
||||
uint screen_height();
|
||||
unsigned screen_width();
|
||||
unsigned screen_height();
|
||||
|
||||
void enable_screensaver();
|
||||
void disable_screensaver();
|
||||
@@ -182,26 +184,26 @@ public:
|
||||
AutoCenter = 1 << 1,
|
||||
};
|
||||
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void close();
|
||||
void move(uint x, uint y);
|
||||
void resize(uint width, uint height);
|
||||
void move(unsigned x, unsigned y);
|
||||
void resize(unsigned width, unsigned height);
|
||||
void focus();
|
||||
bool focused();
|
||||
void fullscreen();
|
||||
void unfullscreen();
|
||||
uint get_width();
|
||||
uint get_height();
|
||||
unsigned get_width();
|
||||
unsigned get_height();
|
||||
void set_opacity(uint8_t opacity);
|
||||
void set_background_color(uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_icon(unsigned width, unsigned height, const uint32_t *data);
|
||||
void set_status_text(const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void attach(Window &window, uint x, uint y);
|
||||
void attach(Window &window, unsigned x, unsigned y);
|
||||
void attach(MenuGroup &menugroup);
|
||||
void attach(FormControl &formcontrol, uint x, uint y);
|
||||
void move(Window &window, uint x, uint y);
|
||||
void move(FormControl &formcontrol, uint x, uint y);
|
||||
void attach(FormControl &formcontrol, unsigned x, unsigned y);
|
||||
void move(Window &window, unsigned x, unsigned y);
|
||||
void move(FormControl &formcontrol, unsigned x, unsigned y);
|
||||
|
||||
class Menubar {
|
||||
public:
|
||||
@@ -224,10 +226,9 @@ public:
|
||||
Statusbar(pWindow&);
|
||||
} status;
|
||||
|
||||
nall::function<uintptr_t (Event)> on_close;
|
||||
nall::function<uintptr_t (Event)> on_block;
|
||||
nall::function<uintptr_t (Event)> on_keydown;
|
||||
nall::function<uintptr_t (Event)> on_keyup;
|
||||
nall::function<uintptr_t (event_t)> on_close;
|
||||
nall::function<uintptr_t (event_t)> on_block;
|
||||
nall::function<uintptr_t (event_t)> on_input;
|
||||
|
||||
Window();
|
||||
|
||||
@@ -268,7 +269,7 @@ public:
|
||||
MenuItem& create(const char *text);
|
||||
MenuItem();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
private:
|
||||
pFriends;
|
||||
@@ -283,7 +284,7 @@ public:
|
||||
bool checked();
|
||||
MenuCheckItem();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
private:
|
||||
pFriends;
|
||||
@@ -297,7 +298,7 @@ public:
|
||||
bool checked();
|
||||
MenuRadioItem();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
private:
|
||||
pFriends;
|
||||
@@ -316,7 +317,7 @@ private:
|
||||
|
||||
class FormControl : private nall::base_from_member<pFormControl&>, public Widget {
|
||||
public:
|
||||
void resize(uint width, uint height);
|
||||
void resize(unsigned width, unsigned height);
|
||||
void focus();
|
||||
bool focused();
|
||||
void enable(bool = true);
|
||||
@@ -335,7 +336,7 @@ private:
|
||||
|
||||
class Frame : private nall::base_from_member<pFrame&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
Frame();
|
||||
@@ -347,12 +348,14 @@ private:
|
||||
|
||||
class Canvas : private nall::base_from_member<pCanvas&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height);
|
||||
void create(unsigned style, unsigned width, unsigned height);
|
||||
void redraw();
|
||||
uint32_t* buffer();
|
||||
|
||||
Canvas();
|
||||
|
||||
nall::function<uintptr_t (event_t)> on_input;
|
||||
|
||||
private:
|
||||
pFriends;
|
||||
pCanvas &p;
|
||||
@@ -360,7 +363,7 @@ private:
|
||||
|
||||
class Label : private nall::base_from_member<pLabel&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
Label();
|
||||
@@ -372,10 +375,10 @@ private:
|
||||
|
||||
class Button : private nall::base_from_member<pButton&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
Button();
|
||||
|
||||
@@ -386,13 +389,13 @@ private:
|
||||
|
||||
class Checkbox : private nall::base_from_member<pCheckbox&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void check(bool = true);
|
||||
void uncheck();
|
||||
bool checked();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
Checkbox();
|
||||
|
||||
@@ -403,12 +406,12 @@ private:
|
||||
|
||||
class Radiobox : private nall::base_from_member<pRadiobox&>, public FormControl {
|
||||
public:
|
||||
void create(RadioboxGroup &group, uint style, uint width, uint height, const char *text = "");
|
||||
void create(RadioboxGroup &group, unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void check();
|
||||
bool checked();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_tick;
|
||||
nall::function<uintptr_t (event_t)> on_tick;
|
||||
|
||||
Radiobox();
|
||||
|
||||
@@ -432,10 +435,12 @@ public:
|
||||
VerticalScrollNever = 1 << 6,
|
||||
};
|
||||
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
uint get_text(char *text, uint length = -1U);
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
unsigned get_text(char *text, unsigned length = -1U);
|
||||
void set_text(const char *text = "");
|
||||
|
||||
nall::function<uintptr_t (event_t)> on_change;
|
||||
|
||||
Editbox();
|
||||
|
||||
private:
|
||||
@@ -457,17 +462,17 @@ public:
|
||||
VerticalScrollNever = 1 << 5,
|
||||
};
|
||||
|
||||
void create(uint style, uint width, uint height, const char *columns = "", const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *columns = "", const char *text = "");
|
||||
void autosize_columns();
|
||||
void set_column_width(uint column, uint width);
|
||||
void set_column_width(unsigned column, unsigned width);
|
||||
void add_item(const char *text);
|
||||
void set_item(uint index, const char *text);
|
||||
void set_item(unsigned index, const char *text);
|
||||
int get_selection();
|
||||
void set_selection(int index);
|
||||
void reset();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_change;
|
||||
nall::function<uintptr_t (Event)> on_activate;
|
||||
nall::function<uintptr_t (event_t)> on_change;
|
||||
nall::function<uintptr_t (event_t)> on_activate;
|
||||
|
||||
Listbox();
|
||||
|
||||
@@ -478,13 +483,13 @@ private:
|
||||
|
||||
class Combobox : private nall::base_from_member<pCombobox&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void add_item(const char *text);
|
||||
int get_selection();
|
||||
void set_selection(int index);
|
||||
void reset();
|
||||
|
||||
nall::function<uintptr_t (Event)> on_change;
|
||||
nall::function<uintptr_t (event_t)> on_change;
|
||||
|
||||
Combobox();
|
||||
|
||||
@@ -495,9 +500,9 @@ private:
|
||||
|
||||
class Progressbar : private nall::base_from_member<pProgressbar&>, public FormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height);
|
||||
uint get_progress();
|
||||
void set_progress(uint progress);
|
||||
void create(unsigned style, unsigned width, unsigned height);
|
||||
unsigned get_progress();
|
||||
void set_progress(unsigned progress);
|
||||
|
||||
Progressbar();
|
||||
|
||||
@@ -513,11 +518,11 @@ public:
|
||||
Vertical = 1 << 1,
|
||||
};
|
||||
|
||||
void create(uint style, uint width, uint height, uint length);
|
||||
uint get_position();
|
||||
void set_position(uint position);
|
||||
void create(unsigned style, unsigned width, unsigned height, unsigned length);
|
||||
unsigned get_position();
|
||||
void set_position(unsigned position);
|
||||
|
||||
nall::function<uintptr_t (Event)> on_change;
|
||||
nall::function<uintptr_t (event_t)> on_change;
|
||||
|
||||
Slider();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
void pButton::create(uint style, uint width, uint height, const char *text) {
|
||||
void pButton::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
hwnd = CreateWindow(L"BUTTON", utf16(text), WS_CHILD | WS_TABSTOP | WS_VISIBLE,
|
||||
0, 0, width, height,
|
||||
phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pButton : public pFormControl {
|
||||
public:
|
||||
Button &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pButton(Button&);
|
||||
@@ -1,4 +1,4 @@
|
||||
void pCanvas::create(uint style, uint width, uint height) {
|
||||
void pCanvas::create(unsigned style, unsigned width, unsigned height) {
|
||||
hwnd = CreateWindow(L"hiro_window", L"", WS_CHILD,
|
||||
0, 0, width, height,
|
||||
phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 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,14 +35,7 @@ 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) {
|
||||
void pCanvas::resize(unsigned width, unsigned height) {
|
||||
if(ibuffer) free(ibuffer);
|
||||
|
||||
ipitch = width * sizeof(uint32_t);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pCanvas : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height);
|
||||
void create(unsigned style, unsigned width, unsigned height);
|
||||
void redraw();
|
||||
uint32_t* buffer();
|
||||
|
||||
@@ -11,7 +11,6 @@ public:
|
||||
/* internal */
|
||||
BITMAPINFO bmi;
|
||||
uint32_t *ibuffer;
|
||||
uint ipitch, iwidth, iheight;
|
||||
void blit();
|
||||
void resize(uint width, uint height);
|
||||
unsigned ipitch, iwidth, iheight;
|
||||
void resize(unsigned width, unsigned height);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
void pCheckbox::create(uint style, uint width, uint height, const char *text) {
|
||||
void pCheckbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
hwnd = CreateWindow(L"BUTTON", utf16(text), WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_CHECKBOX,
|
||||
0, 0, width, height,
|
||||
phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pCheckbox : public pFormControl {
|
||||
public:
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
void check(bool state = true);
|
||||
void uncheck();
|
||||
@@ -1,4 +1,4 @@
|
||||
void pCombobox::create(uint style, uint width, uint height, const char *text) {
|
||||
void pCombobox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"COMBOBOX", L"",
|
||||
WS_CHILD | WS_TABSTOP | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS,
|
||||
0, 0, width, 200,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pCombobox : public pFormControl {
|
||||
public:
|
||||
Combobox &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void add_item(const char *text);
|
||||
int get_selection();
|
||||
void set_selection(int index);
|
||||
@@ -1,12 +1,12 @@
|
||||
void pEditbox::create(uint style, uint width, uint height, const char *text) {
|
||||
void pEditbox::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
bool multiline = style & Editbox::Multiline;
|
||||
bool readonly = style & Editbox::Readonly;
|
||||
uint vscroll = (style & Editbox::VerticalScrollAlways) ? WS_VSCROLL :
|
||||
(style & Editbox::VerticalScrollNever) ? 0 :
|
||||
ES_AUTOVSCROLL;
|
||||
uint hscroll = (style & Editbox::HorizontalScrollAlways) ? WS_HSCROLL :
|
||||
(style & Editbox::HorizontalScrollNever) ? 0 :
|
||||
ES_AUTOHSCROLL;
|
||||
bool readonly = style & Editbox::Readonly;
|
||||
unsigned vscroll = (style & Editbox::VerticalScrollAlways) ? WS_VSCROLL :
|
||||
(style & Editbox::VerticalScrollNever) ? 0 :
|
||||
ES_AUTOVSCROLL;
|
||||
unsigned hscroll = (style & Editbox::HorizontalScrollAlways) ? WS_HSCROLL :
|
||||
(style & Editbox::HorizontalScrollNever) ? 0 :
|
||||
ES_AUTOHSCROLL;
|
||||
|
||||
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"",
|
||||
WS_CHILD | WS_VISIBLE | vscroll | hscroll |
|
||||
@@ -25,10 +25,11 @@ void pEditbox::set_text(const char *text) {
|
||||
SetWindowText(hwnd, utf16(temp));
|
||||
}
|
||||
|
||||
uint pEditbox::get_text(char *text, uint length) {
|
||||
wchar_t buffer[length * 2 + 1];
|
||||
GetWindowText(hwnd, buffer, length * 2);
|
||||
unsigned pEditbox::get_text(char *text, unsigned length) {
|
||||
wchar_t *buffer = new wchar_t[length + 1];
|
||||
GetWindowText(hwnd, buffer, length);
|
||||
string temp = (const char*)utf8(buffer);
|
||||
delete[] buffer;
|
||||
replace(temp, "\r", "");
|
||||
strlcpy(text, temp, length);
|
||||
return strlen(text);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class pEditbox : public pFormControl {
|
||||
public:
|
||||
Editbox &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
uint get_text(char *text, uint length = -1U);
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pEditbox(Editbox&);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
class pEditbox : public pFormControl {
|
||||
public:
|
||||
Editbox &self;
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
unsigned get_text(char *text, unsigned length = -1U);
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pEditbox(Editbox&);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
void pFormControl::resize(uint width, uint height) {
|
||||
void pFormControl::resize(unsigned width, unsigned height) {
|
||||
SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class pFormControl : public pWidget {
|
||||
public:
|
||||
virtual void resize(uint width, uint height);
|
||||
virtual void resize(unsigned width, unsigned height);
|
||||
void focus();
|
||||
bool focused();
|
||||
void enable(bool = true);
|
||||
@@ -1,4 +1,4 @@
|
||||
void pFrame::create(uint style, uint width, uint height, const char *text) {
|
||||
void pFrame::create(unsigned style, unsigned width, unsigned height, const char *text) {
|
||||
hwnd = CreateWindow(L"BUTTON", utf16(text), WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
|
||||
0, 0, width, height,
|
||||
phiro().default_hwnd, (HMENU)instance, GetModuleHandle(0), 0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class pFrame : public pFormControl {
|
||||
public:
|
||||
Frame &self;
|
||||
void create(uint style, uint width, uint height, const char *text = "");
|
||||
void create(unsigned style, unsigned width, unsigned height, const char *text = "");
|
||||
void set_text(const char *text = "");
|
||||
|
||||
pFrame(Frame&);
|
||||
Loaded 100 of 271 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user