Update to v095r07 release.

byuu says:

Changelog:
- entire GBA core ported to auto function() -> return; syntax
- fixed GBA BLDY bug that was causing flickering in a few games
- replaced nall/config usage with nall/string/markup/node
  - this merges all configuration files to a unified settings.bml file
- added "Ignore Manifests" option to the advanced setting tab
  - this lets you keep a manifest.bml for an older version of higan; if
    you want to do regression testing

Be sure to remap your controller/hotkey inputs, and for SNES, choose
"Gamepad" from "Controller Port 1" in the system menu. Otherwise you
won't get any input. No need to blow away your old config files, unless
you want to.
This commit is contained in:
Tim Allen 2015-11-16 19:38:05 +11:00
parent 40f4b91000
commit 41c478ac4a
96 changed files with 1055 additions and 1156 deletions

View File

@ -7,7 +7,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "095.06"; static const string Version = "095.07";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -14,7 +14,7 @@ namespace GameBoyAdvance {
#include "serialization.cpp" #include "serialization.cpp"
APU apu; APU apu;
void APU::Enter() { auto APU::Enter() -> void {
while(true) { while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) { if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
@ -24,23 +24,23 @@ void APU::Enter() {
} }
} }
void APU::main() { auto APU::main() -> void {
for(unsigned n = 0; n < 64; n++) { for(auto n : range(64)) {
runsequencer(); runsequencer();
} }
signed lsample = regs.bias.level - 0x0200; int lsample = regs.bias.level - 0x0200;
signed rsample = regs.bias.level - 0x0200; int rsample = regs.bias.level - 0x0200;
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output //(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
if(sequencer.masterenable) { if(sequencer.masterenable) {
signed lsequence = 0; int lsequence = 0;
if(sequencer.lenable[0]) lsequence += square1.output; if(sequencer.lenable[0]) lsequence += square1.output;
if(sequencer.lenable[1]) lsequence += square2.output; if(sequencer.lenable[1]) lsequence += square2.output;
if(sequencer.lenable[2]) lsequence += wave.output; if(sequencer.lenable[2]) lsequence += wave.output;
if(sequencer.lenable[3]) lsequence += noise.output; if(sequencer.lenable[3]) lsequence += noise.output;
signed rsequence = 0; int rsequence = 0;
if(sequencer.renable[0]) rsequence += square1.output; if(sequencer.renable[0]) rsequence += square1.output;
if(sequencer.renable[1]) rsequence += square2.output; if(sequencer.renable[1]) rsequence += square2.output;
if(sequencer.renable[2]) rsequence += wave.output; if(sequencer.renable[2]) rsequence += wave.output;
@ -53,8 +53,8 @@ void APU::main() {
} }
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output //(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
signed fifo0 = fifo[0].output + (1 << fifo[0].volume); int fifo0 = fifo[0].output + (1 << fifo[0].volume);
signed fifo1 = fifo[1].output + (1 << fifo[1].volume); int fifo1 = fifo[1].output + (1 << fifo[1].volume);
if(fifo[0].lenable) lsample += fifo0; if(fifo[0].lenable) lsample += fifo0;
if(fifo[1].lenable) lsample += fifo1; if(fifo[1].lenable) lsample += fifo1;
@ -74,12 +74,12 @@ void APU::main() {
step(512); step(512);
} }
void APU::step(unsigned clocks) { auto APU::step(uint clocks) -> void {
clock += clocks; clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} }
void APU::power() { auto APU::power() -> void {
create(APU::Enter, 16777216); create(APU::Enter, 16777216);
square1.power(); square1.power();
@ -92,7 +92,7 @@ void APU::power() {
regs.bias = 0x0200; regs.bias = 0x0200;
for(unsigned n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this; for(uint n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
} }
} }

View File

@ -1,17 +1,17 @@
struct APU : Thread, MMIO { struct APU : Thread, MMIO {
#include "registers.hpp" #include "registers.hpp"
static void Enter(); static auto Enter() -> void;
void main(); auto main() -> void;
void step(unsigned clocks); auto step(uint clocks) -> void;
uint8 read(uint32 addr); auto read(uint32 addr) -> uint8;
void write(uint32 addr, uint8 byte); auto write(uint32 addr, uint8 byte) -> void;
void power(); auto power() -> void;
void runsequencer(); auto runsequencer() -> void;
void serialize(serializer&); auto serialize(serializer&) -> void;
}; };
extern APU apu; extern APU apu;

View File

@ -1,16 +1,16 @@
void APU::FIFO::read() { auto APU::FIFO::read() -> void {
if(size == 0) return; if(size == 0) return;
size--; size--;
output = sample[rdoffset++]; output = sample[rdoffset++];
} }
void APU::FIFO::write(int8 byte) { auto APU::FIFO::write(int8 byte) -> void {
if(size == 32) rdoffset++; if(size == 32) rdoffset++;
else size++; else size++;
sample[wroffset++] = byte; sample[wroffset++] = byte;
} }
void APU::FIFO::reset() { auto APU::FIFO::reset() -> void {
for(auto& byte : sample) byte = 0; for(auto& byte : sample) byte = 0;
output = 0; output = 0;
@ -19,7 +19,7 @@ void APU::FIFO::reset() {
size = 0; size = 0;
} }
void APU::FIFO::power() { auto APU::FIFO::power() -> void {
reset(); reset();
lenable = 0; lenable = 0;

View File

@ -1,4 +1,4 @@
uint8 APU::read(uint32 addr) { auto APU::read(uint32 addr) -> uint8 {
switch(addr) { switch(addr) {
//NR10 //NR10
@ -97,7 +97,7 @@ uint8 APU::read(uint32 addr) {
return 0u; return 0u;
} }
void APU::write(uint32 addr, uint8 byte) { auto APU::write(uint32 addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
//NR10 //NR10

View File

@ -1,9 +1,9 @@
unsigned APU::Noise::divider() const { auto APU::Noise::divider() const -> uint {
if(divisor == 0) return 4; if(divisor == 0) return 4;
return divisor * 8; return divisor * 8;
} }
void APU::Noise::run() { auto APU::Noise::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = divider() << frequency; period = divider() << frequency;
if(frequency < 14) { if(frequency < 14) {
@ -16,13 +16,13 @@ void APU::Noise::run() {
if(enable == false || (lfsr & 1)) output = 0; if(enable == false || (lfsr & 1)) output = 0;
} }
void APU::Noise::clocklength() { auto APU::Noise::clocklength() -> void {
if(enable && counter) { if(enable && counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
void APU::Noise::clockenvelope() { auto APU::Noise::clockenvelope() -> void {
if(enable && envelope.frequency && --envelope.period == 0) { if(enable && envelope.frequency && --envelope.period == 0) {
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
if(envelope.direction == 0 && volume > 0) volume--; if(envelope.direction == 0 && volume > 0) volume--;
@ -30,7 +30,7 @@ void APU::Noise::clockenvelope() {
} }
} }
uint8 APU::Noise::read(unsigned addr) const { auto APU::Noise::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 1: return 0; case 1: return 0;
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4); case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
@ -39,7 +39,7 @@ uint8 APU::Noise::read(unsigned addr) const {
} }
} }
void APU::Noise::write(unsigned addr, uint8 byte) { auto APU::Noise::write(uint addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
case 1: //NR41 case 1: //NR41
length = byte >> 0; length = byte >> 0;
@ -49,7 +49,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
envelope.frequency = byte >> 0; envelope.frequency = byte >> 0;
envelope.direction = byte >> 3; envelope.direction = byte >> 3;
envelope.volume = byte >> 4; envelope.volume = byte >> 4;
if(envelope.dacenable() == false) enable = false; if(!envelope.dacEnable()) enable = false;
break; break;
case 3: //NR43 case 3: //NR43
@ -64,7 +64,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
initialize = byte >> 7; initialize = byte >> 7;
if(initialize) { if(initialize) {
enable = envelope.dacenable(); enable = envelope.dacEnable();
lfsr = ~0u; lfsr = ~0u;
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
volume = envelope.volume; volume = envelope.volume;
@ -74,7 +74,7 @@ void APU::Noise::write(unsigned addr, uint8 byte) {
} }
} }
void APU::Noise::power() { auto APU::Noise::power() -> void {
envelope.frequency = 0; envelope.frequency = 0;
envelope.direction = 0; envelope.direction = 0;
envelope.volume = 0; envelope.volume = 0;

View File

@ -5,7 +5,7 @@ APU::Registers::SoundBias::operator uint16() const {
); );
} }
uint16 APU::Registers::SoundBias::operator=(uint16 source) { auto APU::Registers::SoundBias::operator=(uint16 source) -> uint16 {
level = (source >> 0) & 1023; level = (source >> 0) & 1023;
amplitude = (source >> 14) & 3; amplitude = (source >> 14) & 3;
return operator uint16(); return operator uint16();

View File

@ -4,11 +4,11 @@ struct Registers {
uint2 amplitude; uint2 amplitude;
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); auto operator=(uint16 source) -> uint16;
SoundBias& operator=(const SoundBias&) = delete; auto operator=(const SoundBias&) -> SoundBias& = delete;
} bias; } bias;
unsigned clock; uint clock;
} regs; } regs;
struct Sweep { struct Sweep {
@ -28,7 +28,7 @@ struct Envelope {
uint3 period; uint3 period;
inline bool dacenable() const { return volume || direction; } auto dacEnable() const -> bool { return volume || direction; }
}; };
struct Square { struct Square {
@ -40,32 +40,32 @@ struct Square {
uint1 counter; uint1 counter;
uint1 initialize; uint1 initialize;
signed shadowfrequency; int shadowfrequency;
uint1 signal; uint1 signal;
uint4 output; uint4 output;
unsigned period; uint period;
uint3 phase; uint3 phase;
uint4 volume; uint4 volume;
void run(); auto run() -> void;
void clocklength(); auto clocklength() -> void;
void clockenvelope(); auto clockenvelope() -> void;
}; };
struct Square1 : Square { struct Square1 : Square {
Sweep sweep; Sweep sweep;
void runsweep(bool update); auto runsweep(bool update) -> void;
void clocksweep(); auto clocksweep() -> void;
uint8 read(unsigned addr) const; auto read(uint addr) const -> uint8;
void write(unsigned addr, uint8 byte); auto write(uint addr, uint8 byte) -> void;
void power(); auto power() -> void;
} square1; } square1;
struct Square2 : Square { struct Square2 : Square {
uint8 read(unsigned addr) const; auto read(uint addr) const -> uint8;
void write(unsigned addr, uint8 byte); auto write(uint addr, uint8 byte) -> void;
void power(); auto power() -> void;
} square2; } square2;
struct Wave { struct Wave {
@ -84,15 +84,15 @@ struct Wave {
uint4 patternaddr; uint4 patternaddr;
uint1 patternbank; uint1 patternbank;
uint4 patternsample; uint4 patternsample;
unsigned period; uint period;
void run(); auto run() -> void;
void clocklength(); auto clocklength() -> void;
uint8 read(unsigned addr) const; auto read(uint addr) const -> uint8;
void write(unsigned addr, uint8 byte); auto write(uint addr, uint8 byte) -> void;
uint8 readram(unsigned addr) const; auto readram(uint addr) const -> uint8;
void writeram(unsigned addr, uint8 byte); auto writeram(uint addr, uint8 byte) -> void;
void power(); auto power() -> void;
} wave; } wave;
struct Noise { struct Noise {
@ -107,16 +107,16 @@ struct Noise {
uint1 enable; uint1 enable;
uint15 lfsr; uint15 lfsr;
uint4 output; uint4 output;
unsigned period; uint period;
uint4 volume; uint4 volume;
unsigned divider() const; auto divider() const -> uint;
void run(); auto run() -> void;
void clocklength(); auto clocklength() -> void;
void clockenvelope(); auto clockenvelope() -> void;
uint8 read(unsigned addr) const; auto read(uint addr) const -> uint8;
void write(unsigned addr, uint8 byte); auto write(uint addr, uint8 byte) -> void;
void power(); auto power() -> void;
} noise; } noise;
struct Sequencer { struct Sequencer {
@ -133,9 +133,9 @@ struct Sequencer {
int16 lsample; int16 lsample;
int16 rsample; int16 rsample;
uint8 read(unsigned addr) const; auto read(uint addr) const -> uint8;
void write(unsigned addr, uint8 byte); auto write(uint addr, uint8 byte) -> void;
void power(); auto power() -> void;
} sequencer; } sequencer;
struct FIFO { struct FIFO {
@ -151,8 +151,8 @@ struct FIFO {
uint1 renable; uint1 renable;
uint1 timer; uint1 timer;
void read(); auto read() -> void;
void write(int8 byte); auto write(int8 byte) -> void;
void reset(); auto reset() -> void;
void power(); auto power() -> void;
} fifo[2]; } fifo[2];

View File

@ -1,4 +1,4 @@
void APU::runsequencer() { auto APU::runsequencer() -> void {
auto& r = sequencer; auto& r = sequencer;
if(r.base == 0) { //512hz if(r.base == 0) { //512hz
@ -26,7 +26,7 @@ void APU::runsequencer() {
if(r.enable[3]) noise.run(); if(r.enable[3]) noise.run();
} }
uint8 APU::Sequencer::read(unsigned addr) const { auto APU::Sequencer::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 0: return (rvolume << 0) | (lvolume << 4); case 0: return (rvolume << 0) | (lvolume << 4);
case 1: return ( case 1: return (
@ -43,7 +43,7 @@ uint8 APU::Sequencer::read(unsigned addr) const {
} }
} }
void APU::Sequencer::write(unsigned addr, uint8 byte) { auto APU::Sequencer::write(uint addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
case 0: //NR50 case 0: //NR50
rvolume = byte >> 0; rvolume = byte >> 0;
@ -71,7 +71,7 @@ void APU::Sequencer::write(unsigned addr, uint8 byte) {
} }
} }
void APU::Sequencer::power() { auto APU::Sequencer::power() -> void {
lvolume = 0; lvolume = 0;
rvolume = 0; rvolume = 0;
for(auto& n : lenable) n = 0; for(auto& n : lenable) n = 0;

View File

@ -1,4 +1,4 @@
void APU::serialize(serializer& s) { auto APU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.integer(regs.bias.level); s.integer(regs.bias.level);

View File

@ -1,4 +1,4 @@
void APU::Square::run() { auto APU::Square::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
@ -15,13 +15,13 @@ void APU::Square::run() {
output = sample; output = sample;
} }
void APU::Square::clocklength() { auto APU::Square::clocklength() -> void {
if(enable && counter) { if(enable && counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
void APU::Square::clockenvelope() { auto APU::Square::clockenvelope() -> void {
if(enable && envelope.frequency && --envelope.period == 0) { if(enable && envelope.frequency && --envelope.period == 0) {
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
if(envelope.direction == 0 && volume > 0) volume--; if(envelope.direction == 0 && volume > 0) volume--;

View File

@ -1,9 +1,9 @@
void APU::Square1::runsweep(bool update) { auto APU::Square1::runsweep(bool update) -> void {
if(sweep.enable == false) return; if(!sweep.enable) return;
sweep.negate = sweep.direction; sweep.negate = sweep.direction;
unsigned delta = shadowfrequency >> sweep.shift; uint delta = shadowfrequency >> sweep.shift;
signed updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta); int updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);
if(updatefrequency > 2047) { if(updatefrequency > 2047) {
enable = false; enable = false;
@ -14,7 +14,7 @@ void APU::Square1::runsweep(bool update) {
} }
} }
void APU::Square1::clocksweep() { auto APU::Square1::clocksweep() -> void {
if(enable && sweep.frequency && --sweep.period == 0) { if(enable && sweep.frequency && --sweep.period == 0) {
sweep.period = sweep.frequency; sweep.period = sweep.frequency;
runsweep(1); runsweep(1);
@ -22,7 +22,7 @@ void APU::Square1::clocksweep() {
} }
} }
uint8 APU::Square1::read(unsigned addr) const { auto APU::Square1::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4); case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4);
case 1: return (duty << 6); case 1: return (duty << 6);
@ -32,7 +32,7 @@ uint8 APU::Square1::read(unsigned addr) const {
} }
} }
void APU::Square1::write(unsigned addr, uint8 byte) { auto APU::Square1::write(uint addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
case 0: //NR10 case 0: //NR10
if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false; if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false;
@ -50,7 +50,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
envelope.frequency = byte >> 0; envelope.frequency = byte >> 0;
envelope.direction = byte >> 3; envelope.direction = byte >> 3;
envelope.volume = byte >> 4; envelope.volume = byte >> 4;
if(envelope.dacenable() == false) enable = false; if(!envelope.dacEnable()) enable = false;
break; break;
case 3: //NR13 case 3: //NR13
@ -63,7 +63,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
initialize = byte >> 7; initialize = byte >> 7;
if(initialize) { if(initialize) {
enable = envelope.dacenable(); enable = envelope.dacEnable();
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
volume = envelope.volume; volume = envelope.volume;
@ -78,7 +78,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
} }
} }
void APU::Square1::power() { auto APU::Square1::power() -> void {
envelope.frequency = 0; envelope.frequency = 0;
envelope.direction = 0; envelope.direction = 0;
envelope.direction = 0; envelope.direction = 0;

View File

@ -1,4 +1,4 @@
uint8 APU::Square2::read(unsigned addr) const { auto APU::Square2::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 1: return (duty << 6); case 1: return (duty << 6);
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4); case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
@ -7,7 +7,7 @@ uint8 APU::Square2::read(unsigned addr) const {
} }
} }
void APU::Square2::write(unsigned addr, uint8 byte) { auto APU::Square2::write(uint addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
case 1: //NR21 case 1: //NR21
length = byte >> 0; length = byte >> 0;
@ -18,7 +18,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
envelope.frequency = byte >> 0; envelope.frequency = byte >> 0;
envelope.direction = byte >> 3; envelope.direction = byte >> 3;
envelope.volume = byte >> 4; envelope.volume = byte >> 4;
if(envelope.dacenable() == false) enable = false; if(!envelope.dacEnable()) enable = false;
break; break;
case 3: //NR23 case 3: //NR23
@ -31,7 +31,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
initialize = byte >> 7; initialize = byte >> 7;
if(initialize) { if(initialize) {
enable = envelope.dacenable(); enable = envelope.dacEnable();
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope.period = envelope.frequency; envelope.period = envelope.frequency;
volume = envelope.volume; volume = envelope.volume;
@ -41,7 +41,7 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
} }
} }
void APU::Square2::power() { auto APU::Square2::power() -> void {
envelope.frequency = 0; envelope.frequency = 0;
envelope.direction = 0; envelope.direction = 0;
envelope.direction = 0; envelope.direction = 0;

View File

@ -1,4 +1,4 @@
void APU::Wave::run() { auto APU::Wave::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = 1 * (2048 - frequency); period = 1 * (2048 - frequency);
patternsample = pattern[patternbank * 16 + patternaddr++]; patternsample = pattern[patternbank * 16 + patternaddr++];
@ -6,18 +6,18 @@ void APU::Wave::run() {
} }
output = patternsample; output = patternsample;
static unsigned multiplier[] = {0, 4, 2, 1, 3, 3, 3, 3}; static uint multiplier[] = {0, 4, 2, 1, 3, 3, 3, 3};
output = (output * multiplier[volume]) / 4; output = (output * multiplier[volume]) / 4;
if(enable == false) output = 0; if(enable == false) output = 0;
} }
void APU::Wave::clocklength() { auto APU::Wave::clocklength() -> void {
if(enable && counter) { if(enable && counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
uint8 APU::Wave::read(unsigned addr) const { auto APU::Wave::read(uint addr) const -> uint8 {
switch(addr) { switch(addr) {
case 0: return (mode << 5) | (bank << 6) | (dacenable << 7); case 0: return (mode << 5) | (bank << 6) | (dacenable << 7);
case 1: return 0; case 1: return 0;
@ -27,7 +27,7 @@ uint8 APU::Wave::read(unsigned addr) const {
} }
} }
void APU::Wave::write(unsigned addr, uint8 byte) { auto APU::Wave::write(uint addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
case 0: //NR30 case 0: //NR30
mode = byte >> 5; mode = byte >> 5;
@ -64,19 +64,19 @@ void APU::Wave::write(unsigned addr, uint8 byte) {
} }
} }
uint8 APU::Wave::readram(unsigned addr) const { auto APU::Wave::readram(uint addr) const -> uint8 {
uint8 byte = 0; uint8 byte = 0;
byte |= pattern[addr * 2 + 0] << 0; byte |= pattern[addr * 2 + 0] << 0;
byte |= pattern[addr * 2 + 1] << 4; byte |= pattern[addr * 2 + 1] << 4;
return byte; return byte;
} }
void APU::Wave::writeram(unsigned addr, uint8 byte) { auto APU::Wave::writeram(uint addr, uint8 byte) -> void {
pattern[addr * 2 + 0] = byte >> 0; pattern[addr * 2 + 0] = byte >> 0;
pattern[addr * 2 + 1] = byte >> 4; pattern[addr * 2 + 1] = byte >> 4;
} }
void APU::Wave::power() { auto APU::Wave::power() -> void {
mode = 0; mode = 0;
bank = 0; bank = 0;
dacenable = 0; dacenable = 0;

View File

@ -12,6 +12,32 @@ namespace GameBoyAdvance {
#include "serialization.cpp" #include "serialization.cpp"
CPU cpu; CPU cpu;
CPU::CPU() {
iwram = new uint8[ 32 * 1024];
ewram = new uint8[256 * 1024];
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
}
CPU::~CPU() {
delete[] iwram;
delete[] ewram;
}
auto CPU::Enter() -> void { auto CPU::Enter() -> void {
while(true) { while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) { if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
@ -59,12 +85,12 @@ auto CPU::main() -> void {
exec(); exec();
} }
auto CPU::step(unsigned clocks) -> void { auto CPU::step(uint clocks) -> void {
timer_step(clocks); timer_step(clocks);
sync_step(clocks); sync_step(clocks);
} }
auto CPU::sync_step(unsigned clocks) -> void { auto CPU::sync_step(uint clocks) -> void {
ppu.clock -= clocks; ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread); if(ppu.clock < 0) co_switch(ppu.thread);
@ -125,40 +151,14 @@ auto CPU::power() -> void {
active.dma = false; active.dma = false;
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA for(uint n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
for(unsigned n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers for(uint n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
for(unsigned n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial for(uint n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
for(unsigned n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad for(uint n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
for(unsigned n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial for(uint n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
for(unsigned n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System for(uint n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
for(unsigned n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System for(uint n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
//0x080-0x083 mirrored via gba/memory/memory.cpp //System //0x080-0x083 mirrored via gba/memory/memory.cpp //System
}
CPU::CPU() {
iwram = new uint8[ 32 * 1024];
ewram = new uint8[256 * 1024];
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
}
CPU::~CPU() {
delete[] iwram;
delete[] ewram;
} }
} }

View File

@ -2,41 +2,39 @@ struct CPU : Processor::ARM, Thread, MMIO {
using ARM::read; using ARM::read;
using ARM::write; using ARM::write;
uint8* iwram = nullptr;
uint8* ewram = nullptr;
#include "registers.hpp" #include "registers.hpp"
#include "prefetch.hpp" #include "prefetch.hpp"
#include "state.hpp" #include "state.hpp"
//cpu.cpp //cpu.cpp
CPU();
~CPU();
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto step(unsigned clocks) -> void override; auto step(uint clocks) -> void override;
auto sync_step(unsigned clocks) -> void; auto sync_step(uint clocks) -> void;
auto keypad_run() -> void; auto keypad_run() -> void;
auto power() -> void; auto power() -> void;
CPU();
~CPU();
//bus.cpp //bus.cpp
auto bus_idle() -> void override; auto bus_idle() -> void override;
auto bus_read(unsigned mode, uint32 addr) -> uint32 override; auto bus_read(uint mode, uint32 addr) -> uint32 override;
auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void override; auto bus_write(uint mode, uint32 addr, uint32 word) -> void override;
auto bus_wait(unsigned mode, uint32 addr) -> unsigned; auto bus_wait(uint mode, uint32 addr) -> uint;
//mmio.cpp //mmio.cpp
auto read(uint32 addr) -> uint8; auto read(uint32 addr) -> uint8;
auto write(uint32 addr, uint8 byte) -> void; auto write(uint32 addr, uint8 byte) -> void;
auto iwram_read(unsigned mode, uint32 addr) -> uint32; auto iwram_read(uint mode, uint32 addr) -> uint32;
auto iwram_write(unsigned mode, uint32 addr, uint32 word) -> void; auto iwram_write(uint mode, uint32 addr, uint32 word) -> void;
auto ewram_read(unsigned mode, uint32 addr) -> uint32; auto ewram_read(uint mode, uint32 addr) -> uint32;
auto ewram_write(unsigned mode, uint32 addr, uint32 word) -> void; auto ewram_write(uint mode, uint32 addr, uint32 word) -> void;
//dma.cpp //dma.cpp
auto dma_run() -> void; auto dma_run() -> void;
@ -46,12 +44,15 @@ struct CPU : Processor::ARM, Thread, MMIO {
auto dma_hdma() -> void; auto dma_hdma() -> void;
//timer.cpp //timer.cpp
auto timer_step(unsigned clocks) -> void; auto timer_step(uint clocks) -> void;
auto timer_increment(unsigned n) -> void; auto timer_increment(uint n) -> void;
auto timer_fifo_run(unsigned n) -> void; auto timer_fifo_run(uint n) -> void;
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint8* iwram = nullptr;
uint8* ewram = nullptr;
}; };
extern CPU cpu; extern CPU cpu;

View File

@ -20,8 +20,8 @@ auto CPU::dma_run() -> void {
} }
auto CPU::dma_exec(Registers::DMA& dma) -> void { auto CPU::dma_exec(Registers::DMA& dma) -> void {
unsigned seek = dma.control.size ? 4 : 2; uint seek = dma.control.size ? 4 : 2;
unsigned mode = dma.control.size ? Word : Half; uint mode = dma.control.size ? Word : Half;
mode |= dma.run.length == dma.length ? Nonsequential : Sequential; mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
if(mode & Nonsequential) { if(mode & Nonsequential) {

View File

@ -1,4 +1,4 @@
auto CPU::iwram_read(unsigned mode, uint32 addr) -> uint32 { auto CPU::iwram_read(uint mode, uint32 addr) -> uint32 {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction; if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16; if(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16;
@ -7,7 +7,7 @@ auto CPU::iwram_read(unsigned mode, uint32 addr) -> uint32 {
return iwram[addr & 0x7fff]; return iwram[addr & 0x7fff];
} }
auto CPU::iwram_write(unsigned mode, uint32 addr, uint32 word) -> void { auto CPU::iwram_write(uint mode, uint32 addr, uint32 word) -> void {
if(regs.memory.control.disable) return; if(regs.memory.control.disable) return;
if(mode & Word) { if(mode & Word) {
@ -25,7 +25,7 @@ auto CPU::iwram_write(unsigned mode, uint32 addr, uint32 word) -> void {
iwram[addr & 0x7fff] = word; iwram[addr & 0x7fff] = word;
} }
auto CPU::ewram_read(unsigned mode, uint32 addr) -> uint32 { auto CPU::ewram_read(uint mode, uint32 addr) -> uint32 {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction; if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(!regs.memory.control.ewram) return iwram_read(mode, addr); if(!regs.memory.control.ewram) return iwram_read(mode, addr);
@ -35,7 +35,7 @@ auto CPU::ewram_read(unsigned mode, uint32 addr) -> uint32 {
return ewram[addr & 0x3ffff]; return ewram[addr & 0x3ffff];
} }
auto CPU::ewram_write(unsigned mode, uint32 addr, uint32 word) -> void { auto CPU::ewram_write(uint mode, uint32 addr, uint32 word) -> void {
if(regs.memory.control.disable) return; if(regs.memory.control.disable) return;
if(!regs.memory.control.ewram) return iwram_write(mode, addr, word); if(!regs.memory.control.ewram) return iwram_write(mode, addr, word);

View File

@ -6,7 +6,7 @@ auto CPU::prefetch_sync(uint32 addr) -> void {
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load); prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
} }
auto CPU::prefetch_step(unsigned clocks) -> void { auto CPU::prefetch_step(uint clocks) -> void {
step(clocks); step(clocks);
if(!regs.wait.control.prefetch || active.dma) return; if(!regs.wait.control.prefetch || active.dma) return;

View File

@ -2,13 +2,13 @@ struct {
uint16 slot[8] = {0}; uint16 slot[8] = {0};
uint32 addr = 0; //read location of slot buffer uint32 addr = 0; //read location of slot buffer
uint32 load = 0; //write location of slot buffer uint32 load = 0; //write location of slot buffer
signed wait = 0; //number of clocks before next slot load int wait = 0; //number of clocks before next slot load
auto empty() const { return addr == load; } auto empty() const { return addr == load; }
auto full() const { return load - addr == 16; } auto full() const { return load - addr == 16; }
} prefetch; } prefetch;
auto prefetch_sync(uint32 addr) -> void; auto prefetch_sync(uint32 addr) -> void;
auto prefetch_step(unsigned clocks) -> void; auto prefetch_step(uint clocks) -> void;
auto prefetch_wait() -> void; auto prefetch_wait() -> void;
auto prefetch_read() -> uint16; auto prefetch_read() -> uint16;

View File

@ -183,6 +183,6 @@ struct Registers {
} memory; } memory;
uint1 postboot; uint1 postboot;
enum class Mode : unsigned { Normal, Halt, Stop } mode; enum class Mode : uint { Normal, Halt, Stop } mode;
unsigned clock; uint clock;
} regs; } regs;

View File

@ -103,7 +103,7 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(regs.memory.control.unknown2); s.integer(regs.memory.control.unknown2);
s.integer(regs.postboot); s.integer(regs.postboot);
s.integer((unsigned&)regs.mode); s.integer((uint&)regs.mode);
s.integer(regs.clock); s.integer(regs.clock);
s.integer(pending.dma.vblank); s.integer(pending.dma.vblank);

View File

@ -1,6 +1,6 @@
auto CPU::timer_step(unsigned clocks) -> void { auto CPU::timer_step(uint clocks) -> void {
for(unsigned c = 0; c < clocks; c++) { for(auto c : range(clocks)) {
for(unsigned n = 0; n < 4; n++) { for(auto n : range(4)) {
auto& timer = regs.timer[n]; auto& timer = regs.timer[n];
if(timer.pending) { if(timer.pending) {
@ -13,7 +13,7 @@ auto CPU::timer_step(unsigned clocks) -> void {
if(timer.control.enable == false || timer.control.cascade == true) continue; if(timer.control.enable == false || timer.control.cascade == true) continue;
static unsigned mask[] = {0, 63, 255, 1023}; static uint mask[] = {0, 63, 255, 1023};
if((regs.clock & mask[timer.control.frequency]) == 0) { if((regs.clock & mask[timer.control.frequency]) == 0) {
timer_increment(n); timer_increment(n);
} }
@ -23,7 +23,7 @@ auto CPU::timer_step(unsigned clocks) -> void {
} }
} }
auto CPU::timer_increment(unsigned n) -> void { auto CPU::timer_increment(uint n) -> void {
auto& timer = regs.timer[n]; auto& timer = regs.timer[n];
if(++timer.period == 0) { if(++timer.period == 0) {
timer.period = timer.reload; timer.period = timer.reload;
@ -39,7 +39,7 @@ auto CPU::timer_increment(unsigned n) -> void {
} }
} }
auto CPU::timer_fifo_run(unsigned n) -> void { auto CPU::timer_fifo_run(uint n) -> void {
apu.fifo[n].read(); apu.fifo[n].read();
if(apu.fifo[n].size > 16) return; if(apu.fifo[n].size > 16) return;

View File

@ -4,23 +4,56 @@ namespace GameBoyAdvance {
Interface* interface = nullptr; Interface* interface = nullptr;
string Interface::title() { Interface::Interface() {
interface = this;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = false;
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
{ Device device{0, ID::Device, "Controller"};
device.input.append({ 0, 0, "A" });
device.input.append({ 1, 0, "B" });
device.input.append({ 2, 0, "Select"});
device.input.append({ 3, 0, "Start" });
device.input.append({ 4, 0, "Right" });
device.input.append({ 5, 0, "Left" });
device.input.append({ 6, 0, "Up" });
device.input.append({ 7, 0, "Down" });
device.input.append({ 8, 0, "R" });
device.input.append({ 9, 0, "L" });
device.input.append({10, 2, "Rumble"});
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
this->device.append(device);
}
port.append({0, "Device", {device[0]}});
}
auto Interface::title() -> string {
return cartridge.title(); return cartridge.title();
} }
double Interface::videoFrequency() { auto Interface::videoFrequency() -> double {
return 16777216.0 / (228.0 * 1232.0); return 16777216.0 / (228.0 * 1232.0);
} }
double Interface::audioFrequency() { auto Interface::audioFrequency() -> double {
return 16777216.0 / 512.0; return 16777216.0 / 512.0;
} }
bool Interface::loaded() { auto Interface::loaded() -> bool {
return cartridge.loaded(); return cartridge.loaded();
} }
unsigned Interface::group(unsigned id) { auto Interface::group(uint id) -> uint {
switch(id) { switch(id) {
case ID::SystemManifest: case ID::SystemManifest:
case ID::BIOS: case ID::BIOS:
@ -36,17 +69,17 @@ unsigned Interface::group(unsigned id) {
throw; throw;
} }
void Interface::load(unsigned id) { auto Interface::load(uint id) -> void {
cartridge.load(); cartridge.load();
} }
void Interface::save() { auto Interface::save() -> void {
for(auto& memory : cartridge.memory) { for(auto& memory : cartridge.memory) {
interface->saveRequest(memory.id, memory.name); interface->saveRequest(memory.id, memory.name);
} }
} }
void Interface::load(unsigned id, const stream& stream) { auto Interface::load(uint id, const stream& stream) -> void {
if(id == ID::SystemManifest) { if(id == ID::SystemManifest) {
system.information.manifest = stream.text(); system.information.manifest = stream.text();
} }
@ -76,7 +109,7 @@ void Interface::load(unsigned id, const stream& stream) {
} }
} }
void Interface::save(unsigned id, const stream& stream) { auto Interface::save(uint id, const stream& stream) -> void {
if(id == ID::SRAM) { if(id == ID::SRAM) {
stream.write(cartridge.sram.data, cartridge.sram.size); stream.write(cartridge.sram.data, cartridge.sram.size);
} }
@ -90,68 +123,34 @@ void Interface::save(unsigned id, const stream& stream) {
} }
} }
void Interface::unload() { auto Interface::unload() -> void {
save(); save();
cartridge.unload(); cartridge.unload();
} }
void Interface::power() { auto Interface::power() -> void {
system.power(); system.power();
} }
void Interface::reset() { auto Interface::reset() -> void {
system.power(); system.power();
} }
void Interface::run() { auto Interface::run() -> void {
system.run(); system.run();
} }
serializer Interface::serialize() { auto Interface::serialize() -> serializer {
system.runtosave(); system.runtosave();
return system.serialize(); return system.serialize();
} }
bool Interface::unserialize(serializer& s) { auto Interface::unserialize(serializer& s) -> bool {
return system.unserialize(s); return system.unserialize(s);
} }
void Interface::paletteUpdate(PaletteMode mode) { auto Interface::paletteUpdate(PaletteMode mode) -> void {
video.generate_palette(mode); video.generatePalette(mode);
}
Interface::Interface() {
interface = this;
information.name = "Game Boy Advance";
information.width = 240;
information.height = 160;
information.overscan = false;
information.aspectRatio = 1.0;
information.resettable = false;
information.capability.states = true;
information.capability.cheats = false;
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
{
Device device{0, ID::Device, "Controller"};
device.input.append({ 0, 0, "A" });
device.input.append({ 1, 0, "B" });
device.input.append({ 2, 0, "Select"});
device.input.append({ 3, 0, "Start" });
device.input.append({ 4, 0, "Right" });
device.input.append({ 5, 0, "Left" });
device.input.append({ 6, 0, "Up" });
device.input.append({ 7, 0, "Down" });
device.input.append({ 8, 0, "R" });
device.input.append({ 9, 0, "L" });
device.input.append({10, 2, "Rumble"});
device.order = {6, 7, 5, 4, 1, 0, 9, 8, 2, 3, 10};
this->device.append(device);
}
port.append({0, "Device", {device[0]}});
} }
} }

View File

@ -3,12 +3,12 @@ namespace GameBoyAdvance {
#endif #endif
struct ID { struct ID {
enum : unsigned { enum : uint {
System, System,
GameBoyAdvance, GameBoyAdvance,
}; };
enum : unsigned { enum : uint {
SystemManifest, SystemManifest,
BIOS, BIOS,
@ -19,35 +19,35 @@ struct ID {
FLASH, FLASH,
}; };
enum : unsigned { enum : uint {
Device = 1, Device = 1,
}; };
}; };
struct Interface : Emulator::Interface { struct Interface : Emulator::Interface {
string title();
double videoFrequency();
double audioFrequency();
bool loaded();
unsigned group(unsigned id);
void load(unsigned id);
void save();
void load(unsigned id, const stream& stream);
void save(unsigned id, const stream& stream);
void unload();
void power();
void reset();
void run();
serializer serialize();
bool unserialize(serializer&);
void paletteUpdate(PaletteMode mode);
Interface(); Interface();
auto title() -> string;
auto videoFrequency() -> double;
auto audioFrequency() -> double;
auto loaded() -> bool;
auto group(uint id) -> uint;
auto load(uint id) -> void;
auto save() -> void;
auto load(uint id, const stream& stream) -> void;
auto save(uint id, const stream& stream) -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto run() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
auto paletteUpdate(PaletteMode mode) -> void;
private: private:
vector<Device> device; vector<Device> device;
}; };

View File

@ -6,8 +6,8 @@ namespace GameBoyAdvance {
Bus bus; Bus bus;
struct UnmappedMemory : Memory { struct UnmappedMemory : Memory {
auto read(unsigned mode, uint32 addr) -> uint32 override { return 0; } auto read(uint mode, uint32 addr) -> uint32 override { return 0; }
auto write(unsigned mode, uint32 addr, uint32 word) -> void override {} auto write(uint mode, uint32 addr, uint32 word) -> void override {}
}; };
static UnmappedMemory unmappedMemory; static UnmappedMemory unmappedMemory;

View File

@ -1,13 +1,13 @@
struct Memory { struct Memory {
virtual auto read(unsigned mode, uint32 addr) -> uint32 = 0; virtual auto read(uint mode, uint32 addr) -> uint32 = 0;
virtual auto write(unsigned mode, uint32 addr, uint32 word) -> void = 0; virtual auto write(uint mode, uint32 addr, uint32 word) -> void = 0;
}; };
struct MMIO : Memory { struct MMIO : Memory {
virtual auto read(uint32 addr) -> uint8 = 0; virtual auto read(uint32 addr) -> uint8 = 0;
virtual auto write(uint32 addr, uint8 data) -> void = 0; virtual auto write(uint32 addr, uint8 data) -> void = 0;
auto read(unsigned mode, uint32 addr) -> uint32 final; auto read(uint mode, uint32 addr) -> uint32 final;
auto write(unsigned mode, uint32 addr, uint32 word) -> void final; auto write(uint mode, uint32 addr, uint32 word) -> void final;
}; };
struct Bus { struct Bus {

View File

@ -1,4 +1,4 @@
auto MMIO::read(unsigned mode, uint32 addr) -> uint32 { auto MMIO::read(uint mode, uint32 addr) -> uint32 {
uint32 word = 0; uint32 word = 0;
if(mode & Word) { if(mode & Word) {
@ -18,7 +18,7 @@ auto MMIO::read(unsigned mode, uint32 addr) -> uint32 {
return word; return word;
} }
auto MMIO::write(unsigned mode, uint32 addr, uint32 word) -> void { auto MMIO::write(uint mode, uint32 addr, uint32 word) -> void {
if(mode & Word) { if(mode & Word) {
addr &= ~3; addr &= ~3;
write(addr + 0, word >> 0); write(addr + 0, word >> 0);

View File

@ -7,7 +7,7 @@ namespace GameBoyAdvance {
#include "serialization.cpp" #include "serialization.cpp"
Player player; Player player;
void Player::power() { auto Player::power() -> void {
status.enable = false; status.enable = false;
status.rumble = false; status.rumble = false;
@ -19,7 +19,7 @@ void Player::power() {
status.recv = 0; status.recv = 0;
} }
void Player::frame() { auto Player::frame() -> void {
uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value(); uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value();
status.logoDetected = (hash == 0x7776eb55); status.logoDetected = (hash == 0x7776eb55);
@ -29,7 +29,7 @@ void Player::frame() {
status.packet = 0; status.packet = 0;
} }
if(status.enable == false) return; if(!status.enable) return;
if(cpu.regs.joybus.settings == 0x0000 && cpu.regs.serial.control == 0x5088) { if(cpu.regs.joybus.settings == 0x0000 && cpu.regs.serial.control == 0x5088) {
status.packet = (status.packet + 1) % 17; status.packet = (status.packet + 1) % 17;
@ -56,7 +56,7 @@ void Player::frame() {
} }
} }
maybe<uint16> Player::keyinput() { auto Player::keyinput() -> maybe<uint16> {
if(status.logoDetected) { if(status.logoDetected) {
switch(status.logoCounter) { switch(status.logoCounter) {
case 0: return 0x03ff; case 0: return 0x03ff;
@ -67,15 +67,15 @@ maybe<uint16> Player::keyinput() {
return nothing; return nothing;
} }
maybe<uint32> Player::read() { auto Player::read() -> maybe<uint32> {
if(status.enable) return status.send; if(status.enable) return status.send;
return nothing; return nothing;
} }
void Player::write(uint8 byte, uint2 addr) { auto Player::write(uint8 byte, uint2 addr) -> void {
if(status.enable == false) return; if(!status.enable) return;
unsigned shift = addr << 3; uint shift = addr << 3;
status.recv &= ~(255 << shift); status.recv &= ~(255 << shift);
status.recv |= byte << shift; status.recv |= byte << shift;

View File

@ -4,21 +4,21 @@ struct Player {
bool rumble; bool rumble;
bool logoDetected; bool logoDetected;
unsigned logoCounter; uint logoCounter;
unsigned packet; uint packet;
uint32 send; uint32 send;
uint32 recv; uint32 recv;
} status; } status;
void power(); auto power() -> void;
void frame(); auto frame() -> void;
maybe<uint16> keyinput(); auto keyinput() -> maybe<uint16>;
maybe<uint32> read(); auto read() -> maybe<uint32>;
void write(uint8 byte, uint2 addr); auto write(uint8 byte, uint2 addr) -> void;
void serialize(serializer& s); auto serialize(serializer& s) -> void;
}; };
extern Player player; extern Player player;

View File

@ -1,4 +1,4 @@
void Player::serialize(serializer& s) { auto Player::serialize(serializer& s) -> void {
s.integer(status.enable); s.integer(status.enable);
s.integer(status.rumble); s.integer(status.rumble);

View File

@ -1,4 +1,4 @@
void PPU::render_backgrounds() { auto PPU::render_backgrounds() -> void {
switch(regs.control.bgmode) { switch(regs.control.bgmode) {
case 0: case 0:
render_background_linear(regs.bg[3]); render_background_linear(regs.bg[3]);
@ -21,7 +21,7 @@ void PPU::render_backgrounds() {
} }
} }
void PPU::render_background_linear(Registers::Background& bg) { auto PPU::render_background_linear(Registers::Background& bg) -> void {
if(regs.control.enable[bg.id] == false) return; if(regs.control.enable[bg.id] == false) return;
auto& output = layer[bg.id]; auto& output = layer[bg.id];
@ -32,19 +32,19 @@ void PPU::render_background_linear(Registers::Background& bg) {
uint9 voffset = bg.vmosaic + bg.voffset; uint9 voffset = bg.vmosaic + bg.voffset;
uint9 hoffset = bg.hoffset; uint9 hoffset = bg.hoffset;
unsigned basemap = bg.control.screenbaseblock << 11; uint basemap = bg.control.screenbaseblock << 11;
unsigned basechr = bg.control.characterbaseblock << 14; uint basechr = bg.control.characterbaseblock << 14;
unsigned px = hoffset & 7, py = voffset & 7; uint px = hoffset & 7, py = voffset & 7;
Tile tile; Tile tile;
uint8 data[8]; uint8 data[8];
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
if(x == 0 || px & 8) { if(x == 0 || px & 8) {
px &= 7; px &= 7;
unsigned tx = hoffset / 8, ty = voffset / 8; uint tx = hoffset / 8, ty = voffset / 8;
unsigned offset = (ty & 31) * 32 + (tx & 31); uint offset = (ty & 31) * 32 + (tx & 31);
if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32; if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32;
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1)); if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
offset = basemap + offset * 2; offset = basemap + offset * 2;
@ -58,13 +58,13 @@ void PPU::render_background_linear(Registers::Background& bg) {
if(bg.control.colormode == 0) { if(bg.control.colormode == 0) {
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4; offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
uint32 word = vram_read(Word, offset); uint32 word = vram_read(Word, offset);
for(unsigned n = 0; n < 8; n++) data[n] = (word >> (n * 4)) & 15; for(auto n : range(8)) data[n] = (word >> (n * 4)) & 15;
} else { } else {
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8; offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
uint32 wordlo = vram_read(Word, offset + 0); uint32 wordlo = vram_read(Word, offset + 0);
uint32 wordhi = vram_read(Word, offset + 4); uint32 wordhi = vram_read(Word, offset + 4);
for(unsigned n = 0; n < 4; n++) data[0 + n] = (wordlo >> (n * 8)) & 255; for(auto n : range(4)) data[0 + n] = (wordlo >> (n * 8)) & 255;
for(unsigned n = 0; n < 4; n++) data[4 + n] = (wordhi >> (n * 8)) & 255; for(auto n : range(4)) data[4 + n] = (wordhi >> (n * 8)) & 255;
} }
} }
@ -78,14 +78,14 @@ void PPU::render_background_linear(Registers::Background& bg) {
} }
} }
void PPU::render_background_affine(Registers::Background& bg) { auto PPU::render_background_affine(Registers::Background& bg) -> void {
if(regs.control.enable[bg.id] == false) return; if(regs.control.enable[bg.id] == false) return;
auto& output = layer[bg.id]; auto& output = layer[bg.id];
unsigned basemap = bg.control.screenbaseblock << 11; uint basemap = bg.control.screenbaseblock << 11;
unsigned basechr = bg.control.characterbaseblock << 14; uint basechr = bg.control.characterbaseblock << 14;
unsigned screensize = 16 << bg.control.screensize; uint screensize = 16 << bg.control.screensize;
unsigned screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1; uint screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1;
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) { if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
bg.hmosaic = bg.lx; bg.hmosaic = bg.lx;
@ -95,9 +95,9 @@ void PPU::render_background_affine(Registers::Background& bg) {
int28 fx = bg.hmosaic; int28 fx = bg.hmosaic;
int28 fy = bg.vmosaic; int28 fy = bg.vmosaic;
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
unsigned cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7; uint cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7;
unsigned cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7; uint cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7;
if(tx < screensize && ty < screensize) { if(tx < screensize && ty < screensize) {
uint8 character = vram[basemap + ty * screensize + tx]; uint8 character = vram[basemap + ty * screensize + tx];
@ -113,16 +113,16 @@ void PPU::render_background_affine(Registers::Background& bg) {
bg.ly += bg.pd; bg.ly += bg.pd;
} }
void PPU::render_background_bitmap(Registers::Background& bg) { auto PPU::render_background_bitmap(Registers::Background& bg) -> void {
if(regs.control.enable[bg.id] == false) return; if(regs.control.enable[bg.id] == false) return;
auto& output = layer[bg.id]; auto& output = layer[bg.id];
uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5) uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5)
unsigned basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame; uint basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame;
unsigned width = regs.control.bgmode == 5 ? 160 : 240; uint width = regs.control.bgmode == 5 ? 160 : 240;
unsigned height = regs.control.bgmode == 5 ? 128 : 160; uint height = regs.control.bgmode == 5 ? 128 : 160;
unsigned mode = depth ? Half : Byte; uint mode = depth ? Half : Byte;
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) { if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
bg.hmosaic = bg.lx; bg.hmosaic = bg.lx;
@ -132,13 +132,13 @@ void PPU::render_background_bitmap(Registers::Background& bg) {
int28 fx = bg.hmosaic; int28 fx = bg.hmosaic;
int28 fy = bg.vmosaic; int28 fy = bg.vmosaic;
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
unsigned px = fx >> 8; uint px = fx >> 8;
unsigned py = fy >> 8; uint py = fy >> 8;
if(px < width && py < height) { if(px < width && py < height) {
unsigned offset = py * width + px; uint offset = py * width + px;
unsigned color = vram_read(mode, basemap + (offset << depth)); uint color = vram_read(mode, basemap + (offset << depth));
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
if(depth == 0) color = pram[color]; if(depth == 0) color = pram[color];

View File

@ -1,4 +1,4 @@
auto PPU::vram_read(unsigned mode, uint32 addr) -> uint32 { auto PPU::vram_read(uint mode, uint32 addr) -> uint32 {
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff; addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
if(mode & Word) { if(mode & Word) {
@ -14,7 +14,7 @@ auto PPU::vram_read(unsigned mode, uint32 addr) -> uint32 {
return 0; //should never occur return 0; //should never occur
} }
auto PPU::vram_write(unsigned mode, uint32 addr, uint32 word) -> void { auto PPU::vram_write(uint mode, uint32 addr, uint32 word) -> void {
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff; addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
if(mode & Word) { if(mode & Word) {
@ -38,13 +38,13 @@ auto PPU::vram_write(unsigned mode, uint32 addr, uint32 word) -> void {
} }
} }
auto PPU::pram_read(unsigned mode, uint32 addr) -> uint32 { auto PPU::pram_read(uint mode, uint32 addr) -> uint32 {
if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16; if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16;
if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8); if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8);
return pram[addr >> 1 & 511]; return pram[addr >> 1 & 511];
} }
auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void { auto PPU::pram_write(uint mode, uint32 addr, uint32 word) -> void {
if(mode & Word) { if(mode & Word) {
pram_write(Half, addr & ~2, word >> 0); pram_write(Half, addr & ~2, word >> 0);
pram_write(Half, addr | 2, word >> 16); pram_write(Half, addr | 2, word >> 16);
@ -59,7 +59,7 @@ auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void {
pram[addr >> 1 & 511] = (uint16)word; pram[addr >> 1 & 511] = (uint16)word;
} }
auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 { auto PPU::oam_read(uint mode, uint32 addr) -> uint32 {
if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16; if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16;
if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8); if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8);
@ -103,7 +103,7 @@ auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 {
} }
} }
auto PPU::oam_write(unsigned mode, uint32 addr, uint32 word) -> void { auto PPU::oam_write(uint mode, uint32 addr, uint32 word) -> void {
if(mode & Word) { if(mode & Word) {
oam_write(Half, addr & ~2, word >> 0); oam_write(Half, addr & ~2, word >> 0);
oam_write(Half, addr | 2, word >> 16); oam_write(Half, addr | 2, word >> 16);

View File

@ -1,4 +1,4 @@
uint8 PPU::read(uint32 addr) { auto PPU::read(uint32 addr) -> uint8 {
switch(addr) { switch(addr) {
//DISPCNT //DISPCNT
@ -48,7 +48,7 @@ uint8 PPU::read(uint32 addr) {
return 0u; return 0u;
} }
void PPU::write(uint32 addr, uint8 byte) { auto PPU::write(uint32 addr, uint8 byte) -> void {
switch(addr) { switch(addr) {
//DISPCNT //DISPCNT

View File

@ -1,10 +1,10 @@
void PPU::render_mosaic_background(unsigned id) { auto PPU::render_mosaic_background(uint id) -> void {
if(regs.mosaic.bghsize == 0) return; if(regs.mosaic.bghsize == 0) return;
unsigned width = 1 + regs.mosaic.bghsize; uint width = 1 + regs.mosaic.bghsize;
auto& buffer = layer[id]; auto& buffer = layer[id];
for(unsigned x = 0; x < 240;) { for(uint x = 0; x < 240;) {
for(unsigned m = 1; m < width; m++) { for(uint m = 1; m < width; m++) {
if(x + m >= 240) break; if(x + m >= 240) break;
buffer[x + m] = buffer[x]; buffer[x + m] = buffer[x];
} }
@ -12,16 +12,16 @@ void PPU::render_mosaic_background(unsigned id) {
} }
} }
void PPU::render_mosaic_object() { auto PPU::render_mosaic_object() -> void {
if(regs.mosaic.objhsize == 0) return; if(regs.mosaic.objhsize == 0) return;
unsigned width = 1 + regs.mosaic.objhsize; uint width = 1 + regs.mosaic.objhsize;
auto& buffer = layer[OBJ]; auto& buffer = layer[OBJ];
Pixel mosaicPixel; Pixel mosaicPixel;
mosaicPixel.mosaic = false; mosaicPixel.mosaic = false;
unsigned counter = 0; uint counter = 0;
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
if(counter == width || mosaicPixel.mosaic == false) { if(counter == width || mosaicPixel.mosaic == false) {
mosaicPixel = buffer[x]; mosaicPixel = buffer[x];
if(counter == width) counter = 0; if(counter == width) counter = 0;

View File

@ -1,23 +1,23 @@
void PPU::render_objects() { auto PPU::render_objects() -> void {
if(regs.control.enable[OBJ] == false) return; if(regs.control.enable[OBJ] == false) return;
for(unsigned n = 0; n < 128; n++) render_object(object[n]); for(auto n : range(128)) render_object(object[n]);
} }
//px,py = pixel coordinates within sprite [0,0 - width,height) //px,py = pixel coordinates within sprite [0,0 - width,height)
//fx,fy = affine pixel coordinates //fx,fy = affine pixel coordinates
//pa,pb,pc,pd = affine pixel adjustments //pa,pb,pc,pd = affine pixel adjustments
//x,y = adjusted coordinates within sprite (linear = vflip/hflip, affine = rotation/zoom) //x,y = adjusted coordinates within sprite (linear = vflip/hflip, affine = rotation/zoom)
void PPU::render_object(Object& obj) { auto PPU::render_object(Object& obj) -> void {
uint8 py = regs.vcounter - obj.y; uint8 py = regs.vcounter - obj.y;
if(obj.affine == 0 && obj.affinesize == 1) return; //hidden if(obj.affine == 0 && obj.affinesize == 1) return; //hidden
if(py >= obj.height << obj.affinesize) return; //offscreen if(py >= obj.height << obj.affinesize) return; //offscreen
auto& output = layer[OBJ]; auto& output = layer[OBJ];
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; uint rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
unsigned baseaddr = obj.character * 32; uint baseaddr = obj.character * 32;
if(obj.mosaic && regs.mosaic.objvsize) { if(obj.mosaic && regs.mosaic.objvsize) {
signed mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize); int mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0; py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0;
} }
@ -38,8 +38,8 @@ void PPU::render_object(Object& obj) {
int28 fx = originx * pa + originy * pb; int28 fx = originx * pa + originy * pb;
int28 fy = originx * pc + originy * pd; int28 fy = originx * pc + originy * pd;
for(unsigned px = 0; px < (obj.width << obj.affinesize); px++) { for(uint px = 0; px < (obj.width << obj.affinesize); px++) {
unsigned x, y; uint x, y;
if(obj.affine == 0) { if(obj.affine == 0) {
x = px; x = px;
y = py; y = py;
@ -52,7 +52,7 @@ void PPU::render_object(Object& obj) {
uint9 ox = obj.x + px; uint9 ox = obj.x + px;
if(ox < 240 && x < obj.width && y < obj.height) { if(ox < 240 && x < obj.width && y < obj.height) {
unsigned offset = (y / 8) * rowsize + (x / 8); uint offset = (y / 8) * rowsize + (x / 8);
offset = offset * 64 + (y & 7) * 8 + (x & 7); offset = offset * 64 + (y & 7) * 8 + (x & 7);
uint8 color = object_vram_read(baseaddr + (offset >> !obj.colors)); uint8 color = object_vram_read(baseaddr + (offset >> !obj.colors));
@ -72,7 +72,7 @@ void PPU::render_object(Object& obj) {
} }
} }
uint8 PPU::object_vram_read(unsigned addr) const { auto PPU::object_vram_read(uint addr) const -> uint8 {
if(regs.control.bgmode == 3 || regs.control.bgmode == 4 || regs.control.bgmode == 5) { if(regs.control.bgmode == 3 || regs.control.bgmode == 4 || regs.control.bgmode == 5) {
if(addr <= 0x3fff) return 0u; if(addr <= 0x3fff) return 0u;
} }

View File

@ -22,7 +22,20 @@ namespace GameBoyAdvance {
#include "serialization.cpp" #include "serialization.cpp"
PPU ppu; PPU ppu;
void PPU::Enter() { PPU::PPU() {
output = new uint32[240 * 160];
regs.bg[0].id = BG0;
regs.bg[1].id = BG1;
regs.bg[2].id = BG2;
regs.bg[3].id = BG3;
}
PPU::~PPU() {
delete[] output;
}
auto PPU::Enter() -> void {
while(true) { while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) { if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
@ -32,22 +45,22 @@ void PPU::Enter() {
} }
} }
void PPU::main() { auto PPU::main() -> void {
scanline(); scanline();
} }
void PPU::step(unsigned clocks) { auto PPU::step(uint clocks) -> void {
clock += clocks; clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} }
void PPU::power() { auto PPU::power() -> void {
create(PPU::Enter, 16777216); create(PPU::Enter, 16777216);
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0; for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000); for(uint n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000); for(uint n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
regs.control = 0; regs.control = 0;
regs.greenswap = 0; regs.greenswap = 0;
@ -84,10 +97,10 @@ void PPU::power() {
regs.blend.evb = 0; regs.blend.evb = 0;
regs.blend.evy = 0; regs.blend.evy = 0;
for(unsigned n = 0x000; n <= 0x055; n++) bus.mmio[n] = this; for(uint n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
} }
void PPU::scanline() { auto PPU::scanline() -> void {
cpu.keypad_run(); cpu.keypad_run();
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226; regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
@ -116,7 +129,7 @@ void PPU::scanline() {
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) { if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
render_forceblank(); render_forceblank();
} else { } else {
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
windowmask[0][x] = false; windowmask[0][x] = false;
windowmask[1][x] = false; windowmask[1][x] = false;
windowmask[2][x] = false; windowmask[2][x] = false;
@ -148,22 +161,9 @@ void PPU::scanline() {
if(++regs.vcounter == 228) regs.vcounter = 0; if(++regs.vcounter == 228) regs.vcounter = 0;
} }
void PPU::frame() { auto PPU::frame() -> void {
player.frame(); player.frame();
scheduler.exit(Scheduler::ExitReason::FrameEvent); scheduler.exit(Scheduler::ExitReason::FrameEvent);
} }
PPU::PPU() {
output = new uint32[240 * 160];
regs.bg[0].id = BG0;
regs.bg[1].id = BG1;
regs.bg[2].id = BG2;
regs.bg[3].id = BG3;
}
PPU::~PPU() {
delete[] output;
}
} }

View File

@ -1,50 +1,52 @@
struct PPU : Thread, MMIO { struct PPU : Thread, MMIO {
uint8 vram[96 * 1024];
uint16 pram[512];
#include "registers.hpp" #include "registers.hpp"
#include "state.hpp" #include "state.hpp"
uint32* output;
static void Enter();
void main();
void step(unsigned clocks);
void power();
void scanline();
void frame();
uint8 read(uint32 addr);
void write(uint32 addr, uint8 byte);
auto vram_read(unsigned mode, uint32 addr) -> uint32;
auto vram_write(unsigned mode, uint32 addr, uint32 word) -> void;
auto pram_read(unsigned mode, uint32 addr) -> uint32;
auto pram_write(unsigned mode, uint32 addr, uint32 word) -> void;
auto oam_read(unsigned mode, uint32 addr) -> uint32;
auto oam_write(unsigned mode, uint32 addr, uint32 word) -> void;
void render_backgrounds();
void render_background_linear(Registers::Background&);
void render_background_affine(Registers::Background&);
void render_background_bitmap(Registers::Background&);
void render_objects();
void render_object(Object&);
uint8 object_vram_read(unsigned addr) const;
void render_mosaic_background(unsigned id);
void render_mosaic_object();
void render_forceblank();
void render_screen();
void render_window(unsigned window);
unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
void serialize(serializer&);
PPU(); PPU();
~PPU(); ~PPU();
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto scanline() -> void;
auto frame() -> void;
auto read(uint32 addr) -> uint8;
auto write(uint32 addr, uint8 byte) -> void;
auto vram_read(uint mode, uint32 addr) -> uint32;
auto vram_write(uint mode, uint32 addr, uint32 word) -> void;
auto pram_read(uint mode, uint32 addr) -> uint32;
auto pram_write(uint mode, uint32 addr, uint32 word) -> void;
auto oam_read(uint mode, uint32 addr) -> uint32;
auto oam_write(uint mode, uint32 addr, uint32 word) -> void;
auto render_backgrounds() -> void;
auto render_background_linear(Registers::Background&) -> void;
auto render_background_affine(Registers::Background&) -> void;
auto render_background_bitmap(Registers::Background&) -> void;
auto render_objects() -> void;
auto render_object(Object&) -> void;
auto object_vram_read(uint addr) const -> uint8;
auto render_mosaic_background(uint id) -> void;
auto render_mosaic_object() -> void;
auto render_forceblank() -> void;
auto render_screen() -> void;
auto render_window(uint window) -> void;
auto blend(uint above, uint eva, uint below, uint evb) -> uint;
auto serialize(serializer&) -> void;
uint8 vram[96 * 1024];
uint16 pram[512];
uint32* output;
}; };
extern PPU ppu; extern PPU ppu;

View File

@ -17,7 +17,7 @@ PPU::Registers::Control::operator uint16() const {
); );
} }
uint16 PPU::Registers::Control::operator=(uint16 source) { auto PPU::Registers::Control::operator=(uint16 source) -> uint16 {
bgmode = source >> 0; bgmode = source >> 0;
cgbmode = source >> 3; cgbmode = source >> 3;
frame = source >> 4; frame = source >> 4;
@ -47,7 +47,7 @@ PPU::Registers::Status::operator uint16() const {
); );
} }
uint16 PPU::Registers::Status::operator=(uint16 source) { auto PPU::Registers::Status::operator=(uint16 source) -> uint16 {
vblank = source >> 0; vblank = source >> 0;
hblank = source >> 1; hblank = source >> 1;
vcoincidence = source >> 2; vcoincidence = source >> 2;
@ -71,7 +71,7 @@ PPU::Registers::BackgroundControl::operator uint16() const {
); );
} }
uint16 PPU::Registers::BackgroundControl::operator=(uint16 source) { auto PPU::Registers::BackgroundControl::operator=(uint16 source) -> uint16 {
priority = source >> 0; priority = source >> 0;
characterbaseblock = source >> 2; characterbaseblock = source >> 2;
unused = source >> 4; unused = source >> 4;
@ -94,7 +94,7 @@ PPU::Registers::WindowFlags::operator uint8() const {
); );
} }
uint8 PPU::Registers::WindowFlags::operator=(uint8 source) { auto PPU::Registers::WindowFlags::operator=(uint8 source) -> uint8 {
enable[BG0] = source >> 0; enable[BG0] = source >> 0;
enable[BG1] = source >> 1; enable[BG1] = source >> 1;
enable[BG2] = source >> 2; enable[BG2] = source >> 2;
@ -122,7 +122,7 @@ PPU::Registers::BlendControl::operator uint16() const {
); );
} }
uint16 PPU::Registers::BlendControl::operator=(uint16 source) { auto PPU::Registers::BlendControl::operator=(uint16 source) -> uint16 {
above[BG0] = source >> 0; above[BG0] = source >> 0;
above[BG1] = source >> 1; above[BG1] = source >> 1;
above[BG2] = source >> 2; above[BG2] = source >> 2;

View File

@ -1,5 +1,5 @@
enum : unsigned { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 }; enum : uint { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 };
enum : unsigned { In0 = 0, In1 = 1, Obj = 2, Out = 3 }; enum : uint { In0 = 0, In1 = 1, Obj = 2, Out = 3 };
struct Registers { struct Registers {
struct Control { struct Control {
@ -13,8 +13,8 @@ struct Registers {
uint1 enablewindow[3]; uint1 enablewindow[3];
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); auto operator=(uint16 source) -> uint16;
Control& operator=(const Control&) = delete; auto operator=(const Control&) -> Control& = delete;
} control; } control;
uint1 greenswap; uint1 greenswap;
@ -29,8 +29,8 @@ struct Registers {
uint8 vcompare; uint8 vcompare;
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); auto operator=(uint16 source) -> uint16;
Status& operator=(const Status&) = delete; auto operator=(const Status&) -> Status& = delete;
} status; } status;
uint16 vcounter; uint16 vcounter;
@ -46,8 +46,8 @@ struct Registers {
uint2 screensize; uint2 screensize;
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); auto operator=(uint16 source) -> uint16;
BackgroundControl& operator=(const BackgroundControl&) = delete; auto operator=(const BackgroundControl&) -> BackgroundControl& = delete;
}; };
struct Background { struct Background {
@ -61,17 +61,17 @@ struct Registers {
//internal //internal
int28 lx, ly; int28 lx, ly;
unsigned vmosaic; uint vmosaic;
unsigned hmosaic; uint hmosaic;
unsigned id; uint id;
} bg[4]; } bg[4];
struct WindowFlags { struct WindowFlags {
uint1 enable[6]; uint1 enable[6];
operator uint8() const; operator uint8() const;
uint8 operator=(uint8 source); auto operator=(uint8 source) -> uint8;
WindowFlags& operator=(const WindowFlags&) = delete; auto operator=(const WindowFlags&) -> WindowFlags& = delete;
}; };
struct Window { struct Window {
@ -94,8 +94,8 @@ struct Registers {
uint2 mode; uint2 mode;
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); auto operator=(uint16 source) -> uint16;
BlendControl& operator=(const BlendControl&) = delete; auto operator=(const BlendControl&) -> BlendControl& = delete;
}; };
struct Blend { struct Blend {

View File

@ -1,11 +1,9 @@
void PPU::render_forceblank() { auto PPU::render_forceblank() -> void {
uint32* line = output + regs.vcounter * 240; uint32* line = output + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) line[x] = 0x7fff;
line[x] = 0x7fff;
}
} }
void PPU::render_screen() { auto PPU::render_screen() -> void {
uint32* line = output + regs.vcounter * 240; uint32* line = output + regs.vcounter * 240;
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0); if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
@ -14,7 +12,7 @@ void PPU::render_screen() {
if(regs.bg[3].control.mosaic) render_mosaic_background(BG3); if(regs.bg[3].control.mosaic) render_mosaic_background(BG3);
render_mosaic_object(); render_mosaic_object();
for(unsigned x = 0; x < 240; x++) { for(auto x : range(240)) {
Registers::WindowFlags flags; Registers::WindowFlags flags;
flags = ~0; //enable all layers if no windows are enabled flags = ~0; //enable all layers if no windows are enabled
@ -27,9 +25,9 @@ void PPU::render_screen() {
} }
//priority sorting: find topmost two pixels //priority sorting: find topmost two pixels
unsigned a = 5, b = 5; uint a = 5, b = 5;
for(signed p = 3; p >= 0; p--) { for(int p = 3; p >= 0; p--) {
for(signed l = 5; l >= 0; l--) { for(int l = 5; l >= 0; l--) {
if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) { if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) {
b = a; b = a;
a = l; a = l;
@ -41,18 +39,21 @@ void PPU::render_screen() {
auto& below = layer[b]; auto& below = layer[b];
bool blendabove = regs.blend.control.above[a]; bool blendabove = regs.blend.control.above[a];
bool blendbelow = regs.blend.control.below[b]; bool blendbelow = regs.blend.control.below[b];
unsigned color = above[x].color; uint color = above[x].color;
auto eva = min(16u, (unsigned)regs.blend.eva);
auto evb = min(16u, (unsigned)regs.blend.evb);
auto evy = min(16u, (unsigned)regs.blend.evy);
//perform blending, if needed //perform blending, if needed
if(flags.enable[SFX] == false) { if(flags.enable[SFX] == false) {
} else if(above[x].translucent && blendbelow) { } else if(above[x].translucent && blendbelow) {
color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); color = blend(above[x].color, eva, below[x].color, evb);
} else if(regs.blend.control.mode == 1 && blendabove && blendbelow) { } else if(regs.blend.control.mode == 1 && blendabove && blendbelow) {
color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); color = blend(above[x].color, eva, below[x].color, evb);
} else if(regs.blend.control.mode == 2 && blendabove) { } else if(regs.blend.control.mode == 2 && blendabove) {
color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy); color = blend(above[x].color, 16 - evy, 0x7fff, evy);
} else if(regs.blend.control.mode == 3 && blendabove) { } else if(regs.blend.control.mode == 3 && blendabove) {
color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy); color = blend(above[x].color, 16 - evy, 0x0000, evy);
} }
//output pixel //output pixel
@ -60,32 +61,29 @@ void PPU::render_screen() {
} }
} }
void PPU::render_window(unsigned w) { auto PPU::render_window(uint w) -> void {
unsigned y = regs.vcounter; uint y = regs.vcounter;
unsigned y1 = regs.window[w].y1, y2 = regs.window[w].y2; uint y1 = regs.window[w].y1, y2 = regs.window[w].y2;
unsigned x1 = regs.window[w].x1, x2 = regs.window[w].x2; uint x1 = regs.window[w].x1, x2 = regs.window[w].x2;
if(y2 < y1 || y2 > 160) y2 = 160; if(y2 < y1 || y2 > 160) y2 = 160;
if(x2 < x1 || x2 > 240) x2 = 240; if(x2 < x1 || x2 > 240) x2 = 240;
if(y >= y1 && y < y2) { if(y >= y1 && y < y2) {
for(unsigned x = x1; x < x2; x++) { for(uint x = x1; x < x2; x++) {
windowmask[w][x] = true; windowmask[w][x] = true;
} }
} }
} }
unsigned PPU::blend(unsigned above, unsigned eva, unsigned below, unsigned evb) { auto PPU::blend(uint above, uint eva, uint below, uint evb) -> uint {
eva = min(16, eva);
evb = min(16, evb);
uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10; uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10;
uint5 br = below >> 0, bg = below >> 5, bb = below >> 10; uint5 br = below >> 0, bg = below >> 5, bb = below >> 10;
unsigned r = (ar * eva + br * evb) >> 4; uint r = (ar * eva + br * evb) >> 4;
unsigned g = (ag * eva + bg * evb) >> 4; uint g = (ag * eva + bg * evb) >> 4;
unsigned b = (ab * eva + bb * evb) >> 4; uint b = (ab * eva + bb * evb) >> 4;
return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10; return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10;
} }

View File

@ -1,4 +1,4 @@
void PPU::serialize(serializer& s) { auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.array(vram, 96 * 1024); s.array(vram, 96 * 1024);
@ -72,8 +72,8 @@ void PPU::serialize(serializer& s) {
s.integer(regs.blend.evb); s.integer(regs.blend.evb);
s.integer(regs.blend.evy); s.integer(regs.blend.evy);
for(unsigned l = 0; l < 6; l++) { for(auto l : range(6)) {
for(unsigned p = 0; p < 240; p++) { for(auto p : range(240)) {
auto& pixel = layer[l][p]; auto& pixel = layer[l][p];
s.integer(pixel.enable); s.integer(pixel.enable);
s.integer(pixel.priority); s.integer(pixel.priority);
@ -83,8 +83,8 @@ void PPU::serialize(serializer& s) {
} }
} }
for(unsigned w = 0; w < 3; w++) { for(auto w : range(3)) {
for(unsigned p = 0; p < 240; p++) { for(auto p : range(240)) {
s.integer(windowmask[w][p]); s.integer(windowmask[w][p]);
} }
} }

View File

@ -4,22 +4,6 @@ namespace GameBoyAdvance {
Scheduler scheduler; Scheduler scheduler;
void Scheduler::enter() {
host = co_active();
co_switch(active);
}
void Scheduler::exit(ExitReason reason) {
exit_reason = reason;
active = co_active();
co_switch(host);
}
void Scheduler::power() {
host = co_active();
active = cpu.thread;
}
Scheduler::Scheduler() { Scheduler::Scheduler() {
sync = SynchronizeMode::None; sync = SynchronizeMode::None;
exit_reason = ExitReason::UnknownEvent; exit_reason = ExitReason::UnknownEvent;
@ -27,4 +11,20 @@ Scheduler::Scheduler() {
active = nullptr; active = nullptr;
} }
auto Scheduler::enter() -> void {
host = co_active();
co_switch(active);
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
active = co_active();
co_switch(host);
}
auto Scheduler::power() -> void {
host = co_active();
active = cpu.thread;
}
} }

View File

@ -1,16 +1,15 @@
struct Scheduler : property<Scheduler> { struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : unsigned { None, CPU, All } sync; enum class SynchronizeMode : uint { None, CPU, All } sync;
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent }; enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason; readonly<ExitReason> exit_reason;
cothread_t host; cothread_t host;
cothread_t active; cothread_t active;
void enter();
void exit(ExitReason);
void power();
Scheduler(); Scheduler();
auto enter() -> void;
auto exit(ExitReason) -> void;
auto power() -> void;
}; };
extern Scheduler scheduler; extern Scheduler scheduler;

View File

@ -7,7 +7,7 @@ BIOS::~BIOS() {
delete[] data; delete[] data;
} }
auto BIOS::read(unsigned mode, uint32 addr) -> uint32 { auto BIOS::read(uint mode, uint32 addr) -> uint32 {
//unmapped memory //unmapped memory
if(addr >= 0x0000'4000) return cpu.pipeline.fetch.instruction; //0000'4000-01ff'ffff if(addr >= 0x0000'4000) return cpu.pipeline.fetch.instruction; //0000'4000-01ff'ffff
@ -21,5 +21,5 @@ auto BIOS::read(unsigned mode, uint32 addr) -> uint32 {
return mdr = data[addr]; return mdr = data[addr];
} }
auto BIOS::write(unsigned mode, uint32 addr, uint32 word) -> void { auto BIOS::write(uint mode, uint32 addr, uint32 word) -> void {
} }

View File

@ -1,7 +1,7 @@
serializer System::serialize() { auto System::serialize() -> serializer {
serializer s(serialize_size); serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion; uint signature = 0x31545342, version = Info::SerializerVersion;
char hash[64], description[512]; char hash[64], description[512];
memcpy(&hash, (const char*)cartridge.sha256(), 64); memcpy(&hash, (const char*)cartridge.sha256(), 64);
memset(&description, 0, sizeof description); memset(&description, 0, sizeof description);
@ -15,8 +15,8 @@ serializer System::serialize() {
return s; return s;
} }
bool System::unserialize(serializer& s) { auto System::unserialize(serializer& s) -> bool {
unsigned signature, version; uint signature, version;
char hash[64], description[512]; char hash[64], description[512];
s.integer(signature); s.integer(signature);
@ -32,12 +32,12 @@ bool System::unserialize(serializer& s) {
return true; return true;
} }
void System::serialize(serializer& s) { auto System::serialize(serializer& s) -> void {
s.integer(bios.size); s.integer(bios.size);
s.integer(bios.mdr); s.integer(bios.mdr);
} }
void System::serialize_all(serializer& s) { auto System::serialize_all(serializer& s) -> void {
cartridge.serialize(s); cartridge.serialize(s);
system.serialize(s); system.serialize(s);
cpu.serialize(s); cpu.serialize(s);
@ -46,10 +46,10 @@ void System::serialize_all(serializer& s) {
player.serialize(s); player.serialize(s);
} }
void System::serialize_init() { auto System::serialize_init() -> void {
serializer s; serializer s;
unsigned signature = 0, version = 0; uint signature = 0, version = 0;
char hash[64], description[512]; char hash[64], description[512];
s.integer(signature); s.integer(signature);

View File

@ -7,13 +7,13 @@ namespace GameBoyAdvance {
BIOS bios; BIOS bios;
System system; System system;
void System::init() { auto System::init() -> void {
} }
void System::term() { auto System::term() -> void {
} }
void System::power() { auto System::power() -> void {
bus.power(); bus.power();
player.power(); player.power();
cpu.power(); cpu.power();
@ -23,7 +23,7 @@ void System::power() {
scheduler.power(); scheduler.power();
} }
void System::load() { auto System::load() -> void {
interface->loadRequest(ID::SystemManifest, "manifest.bml", true); interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
@ -34,7 +34,7 @@ void System::load() {
serialize_init(); serialize_init();
} }
void System::run() { auto System::run() -> void {
while(true) { while(true) {
scheduler.enter(); scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break; if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
@ -42,7 +42,7 @@ void System::run() {
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160); interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
} }
void System::runtosave() { auto System::runtosave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::CPU; scheduler.sync = Scheduler::SynchronizeMode::CPU;
runthreadtosave(); runthreadtosave();
@ -57,7 +57,7 @@ void System::runtosave() {
scheduler.sync = Scheduler::SynchronizeMode::None; scheduler.sync = Scheduler::SynchronizeMode::None;
} }
void System::runthreadtosave() { auto System::runthreadtosave() -> void {
while(true) { while(true) {
scheduler.enter(); scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break; if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;

View File

@ -1,4 +1,4 @@
enum class Input : unsigned { enum class Input : uint {
A, B, Select, Start, Right, Left, Up, Down, R, L, A, B, Select, Start, Right, Left, Up, Down, R, L,
}; };
@ -6,35 +6,35 @@ struct BIOS : Memory {
BIOS(); BIOS();
~BIOS(); ~BIOS();
auto read(unsigned mode, uint32 addr) -> uint32 override; auto read(uint mode, uint32 addr) -> uint32 override;
auto write(unsigned mode, uint32 addr, uint32 word) -> void override; auto write(uint mode, uint32 addr, uint32 word) -> void override;
uint8* data = nullptr; uint8* data = nullptr;
unsigned size = 0; uint size = 0;
uint32 mdr = 0; uint32 mdr = 0;
}; };
struct System { struct System {
void init(); auto init() -> void;
void term(); auto term() -> void;
void load(); auto load() -> void;
void power(); auto power() -> void;
void run(); auto run() -> void;
void runtosave(); auto runtosave() -> void;
void runthreadtosave(); auto runthreadtosave() -> void;
unsigned serialize_size; auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
serializer serialize(); auto serialize(serializer&) -> void;
bool unserialize(serializer&); auto serialize_all(serializer&) -> void;
auto serialize_init() -> void;
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();
struct Information { struct Information {
string manifest; string manifest;
} information; } information;
uint serialize_size;
}; };
extern BIOS bios; extern BIOS bios;

View File

@ -4,16 +4,24 @@ namespace GameBoyAdvance {
Video video; Video video;
void Video::generate_palette(Emulator::Interface::PaletteMode mode) { Video::Video() {
for(unsigned color = 0; color < (1 << 15); color++) { palette = new uint32[1 << 15]();
}
Video::~Video() {
delete[] palette;
}
auto Video::generatePalette(Emulator::Interface::PaletteMode mode) -> void {
for(auto color : range(1 << 15)) {
if(mode == Emulator::Interface::PaletteMode::Literal) { if(mode == Emulator::Interface::PaletteMode::Literal) {
palette[color] = color; palette[color] = color;
continue; continue;
} }
unsigned B = (color >> 10) & 31; uint B = (color >> 10) & 31;
unsigned G = (color >> 5) & 31; uint G = (color >> 5) & 31;
unsigned R = (color >> 0) & 31; uint R = (color >> 0) & 31;
if(mode == Emulator::Interface::PaletteMode::Channel) { if(mode == Emulator::Interface::PaletteMode::Channel) {
R = image::normalize(R, 5, 16); R = image::normalize(R, 5, 16);
@ -32,43 +40,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
} }
if(mode == Emulator::Interface::PaletteMode::Emulation) { if(mode == Emulator::Interface::PaletteMode::Emulation) {
#if 0
R = curve[R];
G = curve[G];
B = curve[B];
unsigned Rr = R * 16;
unsigned Gr = R * 4;
unsigned Br = R * 4;
unsigned Rg = G * 8;
unsigned Gg = G * 16;
unsigned Bg = G * 8;
unsigned Rb = B * 0; //intentionally always zero
unsigned Gb = B * 8;
unsigned Bb = B * 16;
if(Rr < Rg) std::swap(Rr, Rg);
if(Rr < Rb) std::swap(Rr, Rb);
if(Rg < Rb) std::swap(Rg, Rb);
if(Gr < Gg) std::swap(Gr, Gg);
if(Gr < Gb) std::swap(Gr, Gb);
if(Gg < Gb) std::swap(Gg, Gb);
if(Br < Bg) std::swap(Br, Bg);
if(Br < Bb) std::swap(Br, Bb);
if(Bg < Bb) std::swap(Bg, Bb);
R = (((4 * Rr + 2 * Rg + Rb) * 160) >> 14) + 32;
G = (((4 * Gr + 2 * Gg + Gb) * 160) >> 14) + 32;
B = (((4 * Br + 2 * Bg + Bb) * 160) >> 14) + 32;
R = image::normalize(R, 8, 16);
G = image::normalize(G, 8, 16);
B = image::normalize(B, 8, 16);
#else
double lcdGamma = 4.0, outGamma = 2.2; double lcdGamma = 4.0, outGamma = 2.2;
double lb = pow(B / 31.0, lcdGamma); double lb = pow(B / 31.0, lcdGamma);
double lg = pow(G / 31.0, lcdGamma); double lg = pow(G / 31.0, lcdGamma);
@ -76,7 +47,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
B = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); B = pow((220 * lb + 10 * lg + 50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
G = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); G = pow(( 30 * lb + 230 * lg + 10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
R = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280); R = pow(( 0 * lb + 50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
#endif
palette[color] = interface->videoColor(color, 0, R, G, B); palette[color] = interface->videoColor(color, 0, R, G, B);
continue; continue;
@ -86,14 +56,6 @@ void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
} }
} }
Video::Video() {
palette = new uint32_t[1 << 15]();
}
Video::~Video() {
delete[] palette;
}
const uint8 Video::curve[32] = { const uint8 Video::curve[32] = {
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x10, 0x12,
0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38, 0x14, 0x16, 0x18, 0x1c, 0x20, 0x28, 0x38, 0x38,

View File

@ -1,10 +1,11 @@
struct Video { struct Video {
uint32_t* palette = nullptr;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video(); Video();
~Video(); ~Video();
auto generatePalette(Emulator::Interface::PaletteMode mode) -> void;
uint32* palette = nullptr;
private: private:
static const uint8 curve[32]; static const uint8 curve[32];
}; };

View File

@ -60,6 +60,13 @@
#if defined(Hiro_Object) #if defined(Hiro_Object)
struct Object : sObject { struct Object : sObject {
DeclareSharedObject(Object) DeclareSharedObject(Object)
template<typename T> auto cast() -> T {
if(auto pointer = dynamic_cast<typename T::internalType*>(data())) {
if(auto shared = pointer->instance.acquire()) return T(shared);
}
return T();
}
}; };
#endif #endif
@ -152,6 +159,7 @@ struct MenuCheckItem : sMenuCheckItem {
#if defined(Hiro_MenuRadioItem) #if defined(Hiro_MenuRadioItem)
struct MenuRadioItem : sMenuRadioItem { struct MenuRadioItem : sMenuRadioItem {
DeclareSharedAction(MenuRadioItem) DeclareSharedAction(MenuRadioItem)
using internalType = mMenuRadioItem;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
@ -580,6 +588,7 @@ struct ProgressBar : sProgressBar {
#if defined(Hiro_RadioButton) #if defined(Hiro_RadioButton)
struct RadioButton : sRadioButton { struct RadioButton : sRadioButton {
DeclareSharedWidget(RadioButton) DeclareSharedWidget(RadioButton)
using internalType = mRadioButton;
auto bordered() const { return self().bordered(); } auto bordered() const { return self().bordered(); }
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
@ -600,6 +609,7 @@ struct RadioButton : sRadioButton {
#if defined(Hiro_RadioLabel) #if defined(Hiro_RadioLabel)
struct RadioLabel : sRadioLabel { struct RadioLabel : sRadioLabel {
DeclareSharedWidget(RadioLabel) DeclareSharedWidget(RadioLabel)
using internalType = mRadioLabel;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }

View File

@ -2,7 +2,6 @@
#define NALL_ATOI_HPP #define NALL_ATOI_HPP
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
#include <nall/varint.hpp>
namespace nall { namespace nall {
@ -22,10 +21,10 @@ constexpr inline auto octal_(const char* s, uintmax sum = 0) -> uintmax {
); );
} }
constexpr inline auto natural_(const char* s, uintmax sum = 0) -> uintmax { constexpr inline auto decimal_(const char* s, uintmax sum = 0) -> uintmax {
return ( return (
*s >= '0' && *s <= '9' ? natural_(s + 1, (sum * 10) + *s - '0') : *s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
*s == '\'' ? natural_(s + 1, sum) : *s == '\'' ? decimal_(s + 1, sum) :
sum sum
); );
} }
@ -44,44 +43,44 @@ constexpr inline auto hex_(const char* s, uintmax sum = 0) -> uintmax {
constexpr inline auto binary(const char* s) -> uintmax { constexpr inline auto binary(const char* s) -> uintmax {
return ( return (
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) : *s == '0' && (*(s + 1) == 'B' || *(s + 1) == 'b') ? binary_(s + 2) :
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) : *s == '%' ? binary_(s + 1) : binary_(s)
*s == '%' ? binary_(s + 1) :
binary_(s)
); );
} }
constexpr inline auto octal(const char* s) -> uintmax { constexpr inline auto octal(const char* s) -> uintmax {
return ( return (
*s == '0' && *(s + 1) == 'O' ? octal_(s + 2) : *s == '0' && (*(s + 1) == 'O' || *(s + 1) == 'o') ? octal_(s + 2) :
*s == '0' && *(s + 1) == 'o' ? octal_(s + 2) :
octal_(s) octal_(s)
); );
} }
constexpr inline auto integer(const char* s) -> intmax {
return (
*s == '+' ? +natural_(s + 1) :
*s == '-' ? -natural_(s + 1) :
natural_(s)
);
}
constexpr inline auto natural(const char* s) -> uintmax {
return (
natural_(s)
);
}
constexpr inline auto hex(const char* s) -> uintmax { constexpr inline auto hex(const char* s) -> uintmax {
return ( return (
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) : *s == '0' && (*(s + 1) == 'X' || *(s + 1) == 'x') ? hex_(s + 2) :
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) : *s == '$' ? hex_(s + 1) : hex_(s)
*s == '$' ? hex_(s + 1) :
hex_(s)
); );
} }
//
constexpr inline auto natural(const char* s) -> uintmax {
return (
*s == '0' && (*(s + 1) == 'B' || *(s + 1) == 'b') ? binary_(s + 2) :
*s == '0' && (*(s + 1) == 'O' || *(s + 1) == 'o') ? octal_(s + 2) :
*s == '0' && (*(s + 1) == 'X' || *(s + 1) == 'x') ? hex_(s + 2) :
*s == '%' ? binary_(s + 1) : *s == '$' ? hex_(s + 1) : decimal_(s)
);
}
constexpr inline auto integer(const char* s) -> intmax {
return (
*s == '+' ? +natural(s + 1) : *s == '-' ? -natural(s + 1) : natural(s)
);
}
//
inline auto real(const char* s) -> double { inline auto real(const char* s) -> double {
return atof(s); return atof(s);
} }

View File

@ -5,47 +5,47 @@ namespace nall {
struct range_t { struct range_t {
struct iterator { struct iterator {
iterator(signed position, signed step = 0) : position(position), step(step) {} iterator(int position, int step = 0) : position(position), step(step) {}
auto operator*() const -> signed { return position; } auto operator*() const -> int { return position; }
auto operator!=(const iterator& source) const -> bool { return step > 0 ? position < source.position : position > source.position; } auto operator!=(const iterator& source) const -> bool { return step > 0 ? position < source.position : position > source.position; }
auto operator++() -> iterator& { position += step; return *this; } auto operator++() -> iterator& { position += step; return *this; }
private: private:
signed position; int position;
const signed step; const int step;
}; };
auto begin() const -> const iterator { return iterator(origin, stride); } auto begin() const -> const iterator { return iterator(origin, stride); }
auto end() const -> const iterator { return iterator(target); } auto end() const -> const iterator { return iterator(target); }
signed origin; int origin;
signed target; int target;
signed stride; int stride;
}; };
inline auto range(signed size) { inline auto range(int size) {
return range_t{0, size, 1}; return range_t{0, size, 1};
} }
inline auto range(signed offset, signed size) { inline auto range(int offset, int size) {
return range_t{offset, size, 1}; return range_t{offset, size, 1};
} }
inline auto range(signed offset, signed size, signed step) { inline auto range(int offset, int size, int step) {
return range_t{offset, size, step}; return range_t{offset, size, step};
} }
//reverse-range //reverse-range
inline auto rrange(signed size) { inline auto rrange(int size) {
return range_t{size - 1, -1, -1}; return range_t{size - 1, -1, -1};
} }
template<typename T> inline auto range(const vector<T>& container) { template<typename T> inline auto range(const vector<T>& container) {
return range_t{0, (signed)container.size(), 1}; return range_t{0, (int)container.size(), 1};
} }
template<typename T> inline auto rrange(const vector<T>& container) { template<typename T> inline auto rrange(const vector<T>& container) {
return range_t{(signed)container.size() - 1, -1, -1}; return range_t{(int)container.size() - 1, -1, -1};
} }
} }

View File

@ -40,7 +40,7 @@ struct serial {
} }
//-1 on error, otherwise return bytes read //-1 on error, otherwise return bytes read
auto read(uint8_t* data, unsigned length) -> int { auto read(uint8_t* data, uint length) -> int {
if(port_open == false) return -1; if(port_open == false) return -1;
return ::read(port, (void*)data, length); return ::read(port, (void*)data, length);
} }
@ -59,12 +59,12 @@ struct serial {
} }
//-1 on error, otherwise return bytes written //-1 on error, otherwise return bytes written
auto write(const uint8_t* data, unsigned length) -> int { auto write(const uint8_t* data, uint length) -> int {
if(port_open == false) return -1; if(port_open == false) return -1;
return ::write(port, (void*)data, length); return ::write(port, (void*)data, length);
} }
auto open(const string& portname, unsigned rate, bool flowcontrol) -> bool { auto open(const string& portname, uint rate, bool flowcontrol) -> bool {
close(); close();
port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);

View File

@ -28,21 +28,21 @@ struct has_serialize {
}; };
struct serializer { struct serializer {
enum mode_t { Load, Save, Size }; enum Mode : uint { Load, Save, Size };
auto mode() const -> mode_t { auto mode() const -> Mode {
return _mode; return _mode;
} }
auto data() const -> const uint8_t* { auto data() const -> const uint8* {
return _data; return _data;
} }
auto size() const -> unsigned { auto size() const -> uint {
return _size; return _size;
} }
auto capacity() const -> unsigned { auto capacity() const -> uint {
return _capacity; return _capacity;
} }
@ -50,11 +50,11 @@ struct serializer {
enum { size = sizeof(T) }; enum { size = sizeof(T) };
//this is rather dangerous, and not cross-platform safe; //this is rather dangerous, and not cross-platform safe;
//but there is no standardized way to export FP-values //but there is no standardized way to export FP-values
uint8_t* p = (uint8_t*)&value; auto p = (uint8*)&value;
if(_mode == Save) { if(_mode == Save) {
for(unsigned n = 0; n < size; n++) _data[_size++] = p[n]; for(uint n = 0; n < size; n++) _data[_size++] = p[n];
} else if(_mode == Load) { } else if(_mode == Load) {
for(unsigned n = 0; n < size; n++) p[n] = _data[_size++]; for(uint n = 0; n < size; n++) p[n] = _data[_size++];
} else { } else {
_size += size; _size += size;
} }
@ -64,10 +64,10 @@ struct serializer {
template<typename T> auto integer(T& value) -> serializer& { template<typename T> auto integer(T& value) -> serializer& {
enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) }; enum { size = std::is_same<bool, T>::value ? 1 : sizeof(T) };
if(_mode == Save) { if(_mode == Save) {
for(unsigned n = 0; n < size; n++) _data[_size++] = (uintmax_t)value >> (n << 3); for(uint n = 0; n < size; n++) _data[_size++] = (uintmax)value >> (n << 3);
} else if(_mode == Load) { } else if(_mode == Load) {
value = 0; value = 0;
for(unsigned n = 0; n < size; n++) value |= (uintmax_t)_data[_size++] << (n << 3); for(uint n = 0; n < size; n++) value |= (uintmax)_data[_size++] << (n << 3);
} else if(_mode == Size) { } else if(_mode == Size) {
_size += size; _size += size;
} }
@ -75,12 +75,12 @@ struct serializer {
} }
template<typename T, int N> auto array(T (&array)[N]) -> serializer& { template<typename T, int N> auto array(T (&array)[N]) -> serializer& {
for(unsigned n = 0; n < N; n++) operator()(array[n]); for(uint n = 0; n < N; n++) operator()(array[n]);
return *this; return *this;
} }
template<typename T> auto array(T array, unsigned size) -> serializer& { template<typename T> auto array(T array, uint size) -> serializer& {
for(unsigned n = 0; n < size; n++) operator()(array[n]); for(uint n = 0; n < size; n++) operator()(array[n]);
return *this; return *this;
} }
@ -88,13 +88,13 @@ struct serializer {
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); } template<typename T> auto operator()(T& value, typename std::enable_if<std::is_integral<T>::value>::type* = 0) -> serializer& { return integer(value); }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); } template<typename T> auto operator()(T& value, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) -> serializer& { return floatingpoint(value); }
template<typename T> auto operator()(T& value, typename std::enable_if<std::is_array<T>::value>::type* = 0) -> serializer& { return array(value); } template<typename T> auto operator()(T& value, typename std::enable_if<std::is_array<T>::value>::type* = 0) -> serializer& { return array(value); }
template<typename T> auto operator()(T& value, unsigned size, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) -> serializer& { return array(value, size); } template<typename T> auto operator()(T& value, uint size, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) -> serializer& { return array(value, size); }
auto operator=(const serializer& s) -> serializer& { auto operator=(const serializer& s) -> serializer& {
if(_data) delete[] _data; if(_data) delete[] _data;
_mode = s._mode; _mode = s._mode;
_data = new uint8_t[s._capacity]; _data = new uint8[s._capacity];
_size = s._size; _size = s._size;
_capacity = s._capacity; _capacity = s._capacity;
@ -118,16 +118,16 @@ struct serializer {
serializer(const serializer& s) { operator=(s); } serializer(const serializer& s) { operator=(s); }
serializer(serializer&& s) { operator=(move(s)); } serializer(serializer&& s) { operator=(move(s)); }
serializer(unsigned capacity) { serializer(uint capacity) {
_mode = Save; _mode = Save;
_data = new uint8_t[capacity](); _data = new uint8[capacity]();
_size = 0; _size = 0;
_capacity = capacity; _capacity = capacity;
} }
serializer(const uint8_t* data, unsigned capacity) { serializer(const uint8* data, uint capacity) {
_mode = Load; _mode = Load;
_data = new uint8_t[capacity]; _data = new uint8[capacity];
_size = 0; _size = 0;
_capacity = capacity; _capacity = capacity;
memcpy(_data, data, capacity); memcpy(_data, data, capacity);
@ -138,10 +138,10 @@ struct serializer {
} }
private: private:
mode_t _mode = Size; Mode _mode = Size;
uint8_t* _data = nullptr; uint8* _data = nullptr;
unsigned _size = 0; uint _size = 0;
unsigned _capacity = 0; uint _capacity = 0;
}; };
}; };

View File

@ -1,8 +1,6 @@
#ifndef NALL_STDINT_HPP #ifndef NALL_STDINT_HPP
#define NALL_STDINT_HPP #define NALL_STDINT_HPP
using uint = unsigned int;
#if defined(_MSC_VER) #if defined(_MSC_VER)
typedef signed char int8_t; typedef signed char int8_t;
typedef signed short int16_t; typedef signed short int16_t;
@ -46,6 +44,26 @@ static_assert(sizeof(uint16_t) == 2, "int16_t is not of the correct size");
static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size"); static_assert(sizeof(uint32_t) == 4, "int32_t is not of the correct size");
static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size"); static_assert(sizeof(uint64_t) == 8, "int64_t is not of the correct size");
using int8 = int8_t;
using int16 = int16_t;
using int32 = int32_t;
using int64 = int64_t;
using intmax = intmax_t;
using intptr = intptr_t;
using uint = unsigned int;
using uint8 = uint8_t;
using uint16 = uint16_t;
using uint32 = uint32_t;
using uint64 = uint64_t;
using uintmax = uintmax_t;
using uintptr = uintptr_t;
#if defined(__SIZEOF_INT128__)
using int128 = int128_t;
using uint128 = uint128_t;
#endif
} }
#endif #endif

View File

@ -41,7 +41,7 @@ auto string::reset() -> type& {
return *this; return *this;
} }
auto string::reserve(unsigned capacity) -> type& { auto string::reserve(uint capacity) -> type& {
if(capacity <= _capacity) return *this; if(capacity <= _capacity) return *this;
capacity = bit::round(capacity + 1) - 1; capacity = bit::round(capacity + 1) - 1;
if(_capacity < SSO) { if(_capacity < SSO) {
@ -57,7 +57,7 @@ auto string::reserve(unsigned capacity) -> type& {
return *this; return *this;
} }
auto string::resize(unsigned size) -> type& { auto string::resize(uint size) -> type& {
reserve(size); reserve(size);
get()[_size = size] = 0; get()[_size = size] = 0;
return *this; return *this;
@ -94,27 +94,27 @@ auto string::operator=(string&& source) -> type& {
auto string::_allocate() -> void { auto string::_allocate() -> void {
char _temp[SSO]; char _temp[SSO];
memory::copy(_temp, _text, SSO); memory::copy(_temp, _text, SSO);
_data = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned)); _data = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
memory::copy(_data, _temp, SSO); memory::copy(_data, _temp, SSO);
_refs = (unsigned*)(_data + _capacity + 1); //always aligned by 32 via reserve() _refs = (uint*)(_data + _capacity + 1); //always aligned by 32 via reserve()
*_refs = 1; *_refs = 1;
} }
//COW -> Unique //COW -> Unique
auto string::_copy() -> void { auto string::_copy() -> void {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned)); auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
memory::copy(_temp, _data, _size = min(_capacity, _size)); memory::copy(_temp, _data, _size = min(_capacity, _size));
_temp[_size] = 0; _temp[_size] = 0;
--*_refs; --*_refs;
_data = _temp; _data = _temp;
_refs = (unsigned*)(_data + _capacity + 1); _refs = (uint*)(_data + _capacity + 1);
*_refs = 1; *_refs = 1;
} }
//COW -> Resize //COW -> Resize
auto string::_resize() -> void { auto string::_resize() -> void {
_data = (char*)memory::resize(_data, _capacity + 1 + sizeof(unsigned)); _data = (char*)memory::resize(_data, _capacity + 1 + sizeof(uint));
_refs = (unsigned*)(_data + _capacity + 1); _refs = (uint*)(_data + _capacity + 1);
*_refs = 1; *_refs = 1;
} }

View File

@ -28,7 +28,7 @@ auto string::reset() -> type& {
return *this; return *this;
} }
auto string::reserve(unsigned capacity) -> type& { auto string::reserve(uint capacity) -> type& {
if(capacity > _capacity) { if(capacity > _capacity) {
_capacity = bit::round(max(31u, capacity) + 1) - 1; _capacity = bit::round(max(31u, capacity) + 1) - 1;
_data = _data ? _copy() : _allocate(); _data = _data ? _copy() : _allocate();
@ -36,7 +36,7 @@ auto string::reserve(unsigned capacity) -> type& {
return *this; return *this;
} }
auto string::resize(unsigned size) -> type& { auto string::resize(uint size) -> type& {
reserve(size); reserve(size);
get()[_size = size] = 0; get()[_size = size] = 0;
return *this; return *this;
@ -70,19 +70,19 @@ auto string::operator=(string&& source) -> string& {
} }
auto string::_allocate() -> char* { auto string::_allocate() -> char* {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned)); auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
*_temp = 0; *_temp = 0;
_refs = (unsigned*)(_temp + _capacity + 1); //this will always be aligned by 32 via reserve() _refs = (uint*)(_temp + _capacity + 1); //this will always be aligned by 32 via reserve()
*_refs = 1; *_refs = 1;
return _temp; return _temp;
} }
auto string::_copy() -> char* { auto string::_copy() -> char* {
auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(unsigned)); auto _temp = (char*)memory::allocate(_capacity + 1 + sizeof(uint));
memory::copy(_temp, _data, _size = min(_capacity, _size)); memory::copy(_temp, _data, _size = min(_capacity, _size));
_temp[_size] = 0; _temp[_size] = 0;
--*_refs; --*_refs;
_refs = (unsigned*)(_temp + _capacity + 1); _refs = (uint*)(_temp + _capacity + 1);
*_refs = 1; *_refs = 1;
return _temp; return _temp;
} }

View File

@ -44,7 +44,7 @@ auto string::reset() -> type& {
return *this; return *this;
} }
auto string::reserve(unsigned capacity) -> type& { auto string::reserve(uint capacity) -> type& {
if(capacity <= _capacity) return *this; if(capacity <= _capacity) return *this;
capacity = bit::round(capacity + 1) - 1; capacity = bit::round(capacity + 1) - 1;
if(_capacity < SSO) { if(_capacity < SSO) {
@ -58,7 +58,7 @@ auto string::reserve(unsigned capacity) -> type& {
return *this; return *this;
} }
auto string::resize(unsigned size) -> type& { auto string::resize(uint size) -> type& {
reserve(size); reserve(size);
get()[_size = size] = 0; get()[_size = size] = 0;
return *this; return *this;

View File

@ -36,7 +36,7 @@ auto string::reset() -> type& {
return *this; return *this;
} }
auto string::reserve(unsigned capacity) -> type& { auto string::reserve(uint capacity) -> type& {
if(capacity > _capacity) { if(capacity > _capacity) {
_capacity = bit::round(capacity + 1) - 1; _capacity = bit::round(capacity + 1) - 1;
_data = (char*)memory::resize(_data, _capacity + 1); _data = (char*)memory::resize(_data, _capacity + 1);
@ -45,7 +45,7 @@ auto string::reserve(unsigned capacity) -> type& {
return *this; return *this;
} }
auto string::resize(unsigned size) -> type& { auto string::resize(uint size) -> type& {
reserve(size); reserve(size);
get()[_size = size] = 0; get()[_size = size] = 0;
return *this; return *this;

View File

@ -3,16 +3,10 @@
namespace nall { namespace nall {
auto string::integer() const -> intmax { auto string::integer() const -> intmax {
if(beginsWith("0b") || beginsWith("0B")) return nall::binary(data());
if(beginsWith("0o") || beginsWith("0O")) return nall::octal(data());
if(beginsWith("0x") || beginsWith("0X")) return nall::hex(data());
return nall::integer(data()); return nall::integer(data());
} }
auto string::natural() const -> uintmax { auto string::natural() const -> uintmax {
if(beginsWith("0b") || beginsWith("0B")) return nall::binary(data());
if(beginsWith("0o") || beginsWith("0O")) return nall::octal(data());
if(beginsWith("0x") || beginsWith("0X")) return nall::hex(data());
return nall::natural(data()); return nall::natural(data());
} }

View File

@ -20,13 +20,13 @@ template<typename T> struct stringify;
//format.hpp //format.hpp
template<typename... P> inline auto print(P&&...) -> void; template<typename... P> inline auto print(P&&...) -> void;
inline auto integer(intmax_t value, long precision = 0, char padchar = '0') -> string; inline auto integer(intmax value, long precision = 0, char padchar = '0') -> string;
inline auto decimal(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto decimal(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto hex(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto octal(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string;
inline auto binary(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string;
template<typename T> inline auto pointer(const T* value, long precision = 0) -> string; template<typename T> inline auto pointer(const T* value, long precision = 0) -> string;
inline auto pointer(uintptr_t value, long precision = 0) -> string; inline auto pointer(uintptr value, long precision = 0) -> string;
inline auto real(long double value) -> string; inline auto real(long double value) -> string;
//hash.hpp //hash.hpp
@ -58,11 +58,11 @@ inline auto sharedpath() -> string;
inline auto temppath() -> string; inline auto temppath() -> string;
//utility.hpp //utility.hpp
inline auto slice(rstring self, signed offset = 0, signed length = -1) -> string; inline auto slice(rstring self, int offset = 0, int length = -1) -> string;
inline auto integer(char* result, intmax_t value) -> char*; inline auto integer(char* result, intmax value) -> char*;
inline auto decimal(char* result, uintmax_t value) -> char*; inline auto decimal(char* result, uintmax value) -> char*;
inline auto real(char* str, long double value) -> unsigned; inline auto real(char* str, long double value) -> uint;
struct string { struct string {
using type = string; using type = string;
@ -70,11 +70,11 @@ struct string {
protected: protected:
#if defined(NALL_STRING_ALLOCATOR_ADAPTIVE) #if defined(NALL_STRING_ALLOCATOR_ADAPTIVE)
enum : unsigned { SSO = 24 }; enum : uint { SSO = 24 };
union { union {
struct { //copy-on-write struct { //copy-on-write
char* _data; char* _data;
unsigned* _refs; uint* _refs;
}; };
struct { //small-string-optimization struct { //small-string-optimization
char _text[SSO]; char _text[SSO];
@ -87,13 +87,13 @@ protected:
#if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE) #if defined(NALL_STRING_ALLOCATOR_COPY_ON_WRITE)
char* _data; char* _data;
mutable unsigned* _refs; mutable uint* _refs;
inline auto _allocate() -> char*; inline auto _allocate() -> char*;
inline auto _copy() -> char*; inline auto _copy() -> char*;
#endif #endif
#if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION) #if defined(NALL_STRING_ALLOCATOR_SMALL_STRING_OPTIMIZATION)
enum : unsigned { SSO = 24 }; enum : uint { SSO = 24 };
union { union {
char* _data; char* _data;
char _text[SSO]; char _text[SSO];
@ -104,16 +104,16 @@ protected:
char* _data; char* _data;
#endif #endif
unsigned _capacity; uint _capacity;
unsigned _size; uint _size;
public: public:
inline string(); inline string();
inline auto get() -> char*; inline auto get() -> char*;
inline auto data() const -> const char*; inline auto data() const -> const char*;
inline auto reset() -> type&; inline auto reset() -> type&;
inline auto reserve(unsigned) -> type&; inline auto reserve(uint) -> type&;
inline auto resize(unsigned) -> type&; inline auto resize(uint) -> type&;
inline auto operator=(const string&) -> type&; inline auto operator=(const string&) -> type&;
inline auto operator=(string&&) -> type&; inline auto operator=(string&&) -> type&;
@ -125,8 +125,8 @@ public:
operator const char*() const { return (const char*)data(); } operator const char*() const { return (const char*)data(); }
auto binary() const -> const uint8_t* { return (const uint8_t*)data(); } auto binary() const -> const uint8_t* { return (const uint8_t*)data(); }
auto size() const -> unsigned { return _size; } auto size() const -> uint { return _size; }
auto capacity() const -> unsigned { return _capacity; } auto capacity() const -> uint { return _capacity; }
auto operator==(const string& source) const -> bool { return size() == source.size() && memory::compare(data(), source.data(), size()) == 0; } auto operator==(const string& source) const -> bool { return size() == source.size() && memory::compare(data(), source.data(), size()) == 0; }
auto operator!=(const string& source) const -> bool { return size() != source.size() || memory::compare(data(), source.data(), size()) != 0; } auto operator!=(const string& source) const -> bool { return size() != source.size() || memory::compare(data(), source.data(), size()) != 0; }
@ -155,14 +155,14 @@ public:
inline auto real() const -> double; inline auto real() const -> double;
//core.hpp //core.hpp
inline auto operator[](signed) const -> const char&; inline auto operator[](int) const -> const char&;
template<typename... P> inline auto assign(P&&...) -> type&; template<typename... P> inline auto assign(P&&...) -> type&;
template<typename T, typename... P> inline auto append(const T&, P&&...) -> type&; template<typename T, typename... P> inline auto append(const T&, P&&...) -> type&;
template<typename... P> inline auto append(const nall::format&, P&&...) -> type&; template<typename... P> inline auto append(const nall::format&, P&&...) -> type&;
inline auto append() -> type&; inline auto append() -> type&;
template<typename T> inline auto _append(const stringify<T>&) -> string&; template<typename T> inline auto _append(const stringify<T>&) -> string&;
inline auto empty() const -> bool; inline auto empty() const -> bool;
inline auto length() const -> unsigned; inline auto length() const -> uint;
//datetime.hpp //datetime.hpp
inline static auto date(time_t = 0) -> string; inline static auto date(time_t = 0) -> string;
@ -170,21 +170,21 @@ public:
inline static auto datetime(time_t = 0) -> string; inline static auto datetime(time_t = 0) -> string;
//find.hpp //find.hpp
template<bool, bool> inline auto _find(signed, rstring) const -> maybe<unsigned>; template<bool, bool> inline auto _find(int, rstring) const -> maybe<uint>;
inline auto find(rstring source) const -> maybe<unsigned>; inline auto find(rstring source) const -> maybe<unsigned>;
inline auto ifind(rstring source) const -> maybe<unsigned>; inline auto ifind(rstring source) const -> maybe<unsigned>;
inline auto qfind(rstring source) const -> maybe<unsigned>; inline auto qfind(rstring source) const -> maybe<unsigned>;
inline auto iqfind(rstring source) const -> maybe<unsigned>; inline auto iqfind(rstring source) const -> maybe<unsigned>;
inline auto findFrom(signed offset, rstring source) const -> maybe<unsigned>; inline auto findFrom(int offset, rstring source) const -> maybe<uint>;
inline auto ifindFrom(signed offset, rstring source) const -> maybe<unsigned>; inline auto ifindFrom(int offset, rstring source) const -> maybe<uint>;
//format.hpp //format.hpp
inline auto format(const nall::format& params) -> type&; inline auto format(const nall::format& params) -> type&;
//compare.hpp //compare.hpp
template<bool> inline static auto _compare(const char*, unsigned, const char*, unsigned) -> signed; template<bool> inline static auto _compare(const char*, uint, const char*, uint) -> signed;
inline static auto compare(rstring, rstring) -> signed; inline static auto compare(rstring, rstring) -> signed;
inline static auto icompare(rstring, rstring) -> signed; inline static auto icompare(rstring, rstring) -> signed;
@ -242,12 +242,12 @@ public:
//utility.hpp //utility.hpp
inline static auto read(rstring filename) -> string; inline static auto read(rstring filename) -> string;
inline static auto repeat(rstring pattern, unsigned times) -> string; inline static auto repeat(rstring pattern, uint times) -> string;
inline auto fill(char fill = ' ') -> type&; inline auto fill(char fill = ' ') -> type&;
inline auto hash() const -> unsigned; inline auto hash() const -> uint;
inline auto remove(unsigned offset, unsigned length) -> type&; inline auto remove(uint offset, uint length) -> type&;
inline auto reverse() -> type&; inline auto reverse() -> type&;
inline auto size(signed length, char fill = ' ') -> type&; inline auto size(int length, char fill = ' ') -> type&;
}; };
struct lstring : vector<string> { struct lstring : vector<string> {
@ -255,7 +255,7 @@ struct lstring : vector<string> {
lstring(const lstring& source) { vector::operator=(source); } lstring(const lstring& source) { vector::operator=(source); }
lstring(lstring& source) { vector::operator=(source); } lstring(lstring& source) { vector::operator=(source); }
lstring(lstring&& source) { vector::operator=(std::move(source)); } lstring(lstring&& source) { vector::operator=(move(source)); }
template<typename... P> lstring(P&&... p) { append(forward<P>(p)...); } template<typename... P> lstring(P&&... p) { append(forward<P>(p)...); }
//list.hpp //list.hpp
@ -264,15 +264,15 @@ struct lstring : vector<string> {
inline auto operator=(const lstring& source) -> type& { return vector::operator=(source), *this; } inline auto operator=(const lstring& source) -> type& { return vector::operator=(source), *this; }
inline auto operator=(lstring& source) -> type& { return vector::operator=(source), *this; } inline auto operator=(lstring& source) -> type& { return vector::operator=(source), *this; }
inline auto operator=(lstring&& source) -> type& { return vector::operator=(std::move(source)), *this; } inline auto operator=(lstring&& source) -> type& { return vector::operator=(move(source)), *this; }
inline auto isort() -> type&; inline auto isort() -> type&;
template<typename... P> inline auto append(const string&, P&&...) -> type&; template<typename... P> inline auto append(const string&, P&&...) -> type&;
inline auto append() -> type&; inline auto append() -> type&;
inline auto find(rstring source) const -> maybe<unsigned>; inline auto find(rstring source) const -> maybe<uint>;
inline auto ifind(rstring source) const -> maybe<unsigned>; inline auto ifind(rstring source) const -> maybe<uint>;
inline auto match(rstring pattern) const -> lstring; inline auto match(rstring pattern) const -> lstring;
inline auto merge(rstring separator) const -> string; inline auto merge(rstring separator) const -> string;
inline auto strip() -> type&; inline auto strip() -> type&;

View File

@ -3,25 +3,25 @@
namespace nall { namespace nall {
template<bool Insensitive> template<bool Insensitive>
auto string::_compare(const char* target, unsigned capacity, const char* source, unsigned size) -> signed { auto string::_compare(const char* target, uint capacity, const char* source, uint size) -> signed {
if(Insensitive) return memory::icompare(target, capacity, source, size); if(Insensitive) return memory::icompare(target, capacity, source, size);
return memory::compare(target, capacity, source, size); return memory::compare(target, capacity, source, size);
} }
//size() + 1 includes null-terminator; required to properly compare strings of differing lengths //size() + 1 includes null-terminator; required to properly compare strings of differing lengths
auto string::compare(rstring x, rstring y) -> signed { auto string::compare(rstring x, rstring y) -> int {
return memory::compare(x.data(), x.size() + 1, y.data(), y.size() + 1); return memory::compare(x.data(), x.size() + 1, y.data(), y.size() + 1);
} }
auto string::icompare(rstring x, rstring y) -> signed { auto string::icompare(rstring x, rstring y) -> int {
return memory::icompare(x.data(), x.size() + 1, y.data(), y.size() + 1); return memory::icompare(x.data(), x.size() + 1, y.data(), y.size() + 1);
} }
auto string::compare(rstring source) const -> signed { auto string::compare(rstring source) const -> int {
return memory::compare(data(), size() + 1, source.data(), source.size() + 1); return memory::compare(data(), size() + 1, source.data(), source.size() + 1);
} }
auto string::icompare(rstring source) const -> signed { auto string::icompare(rstring source) const -> int {
return memory::icompare(data(), size() + 1, source.data(), source.size() + 1); return memory::icompare(data(), size() + 1, source.data(), source.size() + 1);
} }

View File

@ -15,7 +15,7 @@
namespace nall { namespace nall {
auto string::operator[](signed position) const -> const char& { auto string::operator[](int position) const -> const char& {
if(position > size() + 1) throw exception_out_of_bounds{}; if(position > size() + 1) throw exception_out_of_bounds{};
return data()[position]; return data()[position];
} }
@ -49,7 +49,7 @@ auto string::empty() const -> bool {
return size() == 0; return size() == 0;
} }
auto string::length() const -> unsigned { auto string::length() const -> uint {
return strlen(data()); return strlen(data());
} }

View File

@ -3,7 +3,7 @@
namespace nall { namespace nall {
namespace Eval { namespace Eval {
inline string evaluateExpression(Node* node) { inline auto evaluateExpression(Node* node) -> string {
#define p(n) evaluateExpression(node->link[n]) #define p(n) evaluateExpression(node->link[n])
switch(node->type) { switch(node->type) {
case Node::Type::Null: return "Null"; case Node::Type::Null: return "Null";
@ -37,15 +37,8 @@ inline string evaluateExpression(Node* node) {
throw "invalid operator"; throw "invalid operator";
} }
inline int64_t evaluateInteger(Node* node) { inline auto evaluateInteger(Node* node) -> int64 {
if(node->type == Node::Type::Literal) { if(node->type == Node::Type::Literal) return nall::integer(node->literal);
if(node->literal.beginsWith("0b")) return nall::binary(node->literal);
if(node->literal.beginsWith("0o")) return nall::octal(node->literal);
if(node->literal.beginsWith("0x")) return nall::hex(node->literal);
if(node->literal.beginsWith("%")) return nall::binary(node->literal);
if(node->literal.beginsWith("$")) return nall::hex(node->literal);
return nall::integer(node->literal);
}
#define p(n) evaluateInteger(node->link[n]) #define p(n) evaluateInteger(node->link[n])
switch(node->type) { switch(node->type) {
@ -93,7 +86,7 @@ inline int64_t evaluateInteger(Node* node) {
throw "invalid operator"; throw "invalid operator";
} }
inline maybe<int64_t> integer(const string& expression) { inline auto integer(const string& expression) -> maybe<int64> {
try { try {
auto tree = new Node; auto tree = new Node;
const char* p = expression; const char* p = expression;
@ -106,7 +99,7 @@ inline maybe<int64_t> integer(const string& expression) {
} }
} }
inline long double evaluateReal(Node* node) { inline auto evaluateReal(Node* node) -> long double {
if(node->type == Node::Type::Literal) return nall::real(node->literal); if(node->type == Node::Type::Literal) return nall::real(node->literal);
#define p(n) evaluateReal(node->link[n]) #define p(n) evaluateReal(node->link[n])
@ -138,7 +131,7 @@ inline long double evaluateReal(Node* node) {
throw "invalid operator"; throw "invalid operator";
} }
inline maybe<long double> real(const string& expression) { inline auto real(const string& expression) -> maybe<long double> {
try { try {
auto tree = new Node; auto tree = new Node;
const char* p = expression; const char* p = expression;

View File

@ -3,7 +3,7 @@
namespace nall { namespace nall {
namespace Eval { namespace Eval {
inline bool isLiteral(const char*& s) { inline auto isLiteral(const char*& s) -> bool {
char n = s[0]; char n = s[0];
return (n >= 'A' && n <= 'Z') return (n >= 'A' && n <= 'Z')
|| (n >= 'a' && n <= 'z') || (n >= 'a' && n <= 'z')
@ -12,7 +12,7 @@ inline bool isLiteral(const char*& s) {
|| (n == '\'' || n == '\"'); || (n == '\'' || n == '\"');
} }
inline string literalNumber(const char*& s) { inline auto literalNumber(const char*& s) -> string {
const char* p = s; const char* p = s;
//binary //binary
@ -64,7 +64,7 @@ inline string literalNumber(const char*& s) {
return result; return result;
} }
inline string literalString(const char*& s) { inline auto literalString(const char*& s) -> string {
const char* p = s; const char* p = s;
char escape = *p++; char escape = *p++;
@ -76,7 +76,7 @@ inline string literalString(const char*& s) {
return result; return result;
} }
inline string literalVariable(const char*& s) { inline auto literalVariable(const char*& s) -> string {
const char* p = s; const char* p = s;
while(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++; while(p[0] == '_' || p[0] == '.' || (p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z') || (p[0] >= '0' && p[0] <= '9')) p++;
@ -86,7 +86,7 @@ inline string literalVariable(const char*& s) {
return result; return result;
} }
inline string literal(const char*& s) { inline auto literal(const char*& s) -> string {
const char* p = s; const char* p = s;
if(p[0] >= '0' && p[0] <= '9') return literalNumber(s); if(p[0] >= '0' && p[0] <= '9') return literalNumber(s);

View File

@ -4,7 +4,7 @@ namespace nall {
namespace Eval { namespace Eval {
struct Node { struct Node {
enum class Type : unsigned { enum class Type : uint {
Null, Null,
Literal, Literal,
Function, Subscript, Member, SuffixIncrement, SuffixDecrement, Function, Subscript, Member, SuffixIncrement, SuffixDecrement,

View File

@ -3,32 +3,32 @@
namespace nall { namespace nall {
namespace Eval { namespace Eval {
inline bool whitespace(char n) { inline auto whitespace(char n) -> bool {
return n == ' ' || n == '\t' || n == '\r' || n == '\n'; return n == ' ' || n == '\t' || n == '\r' || n == '\n';
} }
inline void parse(Node*& node, const char*& s, unsigned depth) { inline auto parse(Node*& node, const char*& s, uint depth) -> void {
auto unaryPrefix = [&](Node::Type type, unsigned seek, unsigned depth) { auto unaryPrefix = [&](Node::Type type, uint seek, uint depth) {
auto parent = new Node(type); auto parent = new Node(type);
parse(parent->link(0) = new Node, s += seek, depth); parse(parent->link(0) = new Node, s += seek, depth);
node = parent; node = parent;
}; };
auto unarySuffix = [&](Node::Type type, unsigned seek, unsigned depth) { auto unarySuffix = [&](Node::Type type, uint seek, uint depth) {
auto parent = new Node(type); auto parent = new Node(type);
parent->link(0) = node; parent->link(0) = node;
parse(parent, s += seek, depth); parse(parent, s += seek, depth);
node = parent; node = parent;
}; };
auto binary = [&](Node::Type type, unsigned seek, unsigned depth) { auto binary = [&](Node::Type type, uint seek, uint depth) {
auto parent = new Node(type); auto parent = new Node(type);
parent->link(0) = node; parent->link(0) = node;
parse(parent->link(1) = new Node, s += seek, depth); parse(parent->link(1) = new Node, s += seek, depth);
node = parent; node = parent;
}; };
auto ternary = [&](Node::Type type, unsigned seek, unsigned depth) { auto ternary = [&](Node::Type type, uint seek, uint depth) {
auto parent = new Node(type); auto parent = new Node(type);
parent->link(0) = node; parent->link(0) = node;
parse(parent->link(1) = new Node, s += seek, depth); parse(parent->link(1) = new Node, s += seek, depth);
@ -37,9 +37,9 @@ inline void parse(Node*& node, const char*& s, unsigned depth) {
node = parent; node = parent;
}; };
auto separator = [&](Node::Type type, unsigned seek, unsigned depth) { auto separator = [&](Node::Type type, uint seek, uint depth) {
if(node->type != Node::Type::Separator) return binary(type, seek, depth); if(node->type != Node::Type::Separator) return binary(type, seek, depth);
unsigned n = node->link.size(); uint n = node->link.size();
parse(node->link(n) = new Node, s += seek, depth); parse(node->link(n) = new Node, s += seek, depth);
}; };
@ -155,7 +155,7 @@ inline void parse(Node*& node, const char*& s, unsigned depth) {
#undef p #undef p
} }
inline Node* parse(const string& expression) { inline auto parse(const string& expression) -> Node* {
auto result = new Node; auto result = new Node;
const char* p = expression; const char* p = expression;
parse(result, p, 0); parse(result, p, 0);

View File

@ -2,11 +2,11 @@
namespace nall { namespace nall {
template<bool Insensitive, bool Quoted> auto string::_find(signed offset, rstring source) const -> maybe<unsigned> { template<bool Insensitive, bool Quoted> auto string::_find(int offset, rstring source) const -> maybe<uint> {
if(source.size() == 0) return nothing; if(source.size() == 0) return nothing;
auto p = data(); auto p = data();
for(unsigned n = offset, quoted = 0; n < size();) { for(uint n = offset, quoted = 0; n < size();) {
if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } } if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
if(_compare<Insensitive>(p + n, size() - n, source.data(), source.size())) { n++; continue; } if(_compare<Insensitive>(p + n, size() - n, source.data(), source.size())) { n++; continue; }
return n - offset; return n - offset;
@ -15,13 +15,13 @@ template<bool Insensitive, bool Quoted> auto string::_find(signed offset, rstrin
return nothing; return nothing;
} }
auto string::find(rstring source) const -> maybe<unsigned> { return _find<0, 0>(0, source); } auto string::find(rstring source) const -> maybe<uint> { return _find<0, 0>(0, source); }
auto string::ifind(rstring source) const -> maybe<unsigned> { return _find<1, 0>(0, source); } auto string::ifind(rstring source) const -> maybe<uint> { return _find<1, 0>(0, source); }
auto string::qfind(rstring source) const -> maybe<unsigned> { return _find<0, 1>(0, source); } auto string::qfind(rstring source) const -> maybe<uint> { return _find<0, 1>(0, source); }
auto string::iqfind(rstring source) const -> maybe<unsigned> { return _find<1, 1>(0, source); } auto string::iqfind(rstring source) const -> maybe<uint> { return _find<1, 1>(0, source); }
auto string::findFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<0, 0>(offset, source); } auto string::findFrom(int offset, rstring source) const -> maybe<uint> { return _find<0, 0>(offset, source); }
auto string::ifindFrom(signed offset, rstring source) const -> maybe<unsigned> { return _find<1, 0>(offset, source); } auto string::ifindFrom(int offset, rstring source) const -> maybe<uint> { return _find<1, 0>(offset, source); }
} }

View File

@ -6,8 +6,8 @@ namespace nall {
//each {#} token will be replaced with its appropriate format parameter //each {#} token will be replaced with its appropriate format parameter
auto string::format(const nall::format& params) -> type& { auto string::format(const nall::format& params) -> type& {
signed size = this->size(); auto size = (int)this->size();
char* data = (char*)memory::allocate(size); auto data = (char*)memory::allocate(size);
memory::copy(data, this->data(), size); memory::copy(data, this->data(), size);
signed x = 0; signed x = 0;
@ -32,19 +32,19 @@ auto string::format(const nall::format& params) -> type& {
}; };
if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; } if(!isNumeric(&data[x + 1], &data[y - 1])) { x++; continue; }
unsigned index = nall::natural(&data[x + 1]); uint index = nall::natural(&data[x + 1]);
if(index >= params.size()) { x++; continue; } if(index >= params.size()) { x++; continue; }
unsigned sourceSize = y - x; uint sourceSize = y - x;
unsigned targetSize = params[index].size(); uint targetSize = params[index].size();
unsigned remaining = size - x; uint remaining = size - x;
if(sourceSize > targetSize) { if(sourceSize > targetSize) {
unsigned difference = sourceSize - targetSize; uint difference = sourceSize - targetSize;
memory::move(&data[x], &data[x + difference], remaining); memory::move(&data[x], &data[x + difference], remaining);
size -= difference; size -= difference;
} else if(targetSize > sourceSize) { } else if(targetSize > sourceSize) {
unsigned difference = targetSize - sourceSize; uint difference = targetSize - sourceSize;
data = (char*)realloc(data, size + difference); data = (char*)realloc(data, size + difference);
size += difference; size += difference;
memory::move(&data[x + difference], &data[x], remaining); memory::move(&data[x + difference], &data[x], remaining);
@ -73,14 +73,14 @@ template<typename... P> auto print(P&&... p) -> void {
fputs(s.data(), stdout); fputs(s.data(), stdout);
} }
auto integer(intmax_t value, long precision, char padchar) -> string { auto integer(intmax value, long precision, char padchar) -> string {
string buffer; string buffer;
buffer.resize(1 + sizeof(intmax_t) * 3); buffer.resize(1 + sizeof(intmax) * 3);
char* p = buffer.get(); char* p = buffer.get();
bool negative = value < 0; bool negative = value < 0;
value = abs(value); value = abs(value);
unsigned size = 0; uint size = 0;
do { do {
p[size++] = '0' + (value % 10); p[size++] = '0' + (value % 10);
value /= 10; value /= 10;
@ -92,12 +92,12 @@ auto integer(intmax_t value, long precision, char padchar) -> string {
return buffer; return buffer;
} }
auto decimal(uintmax_t value, long precision, char padchar) -> string { auto decimal(uintmax value, long precision, char padchar) -> string {
string buffer; string buffer;
buffer.resize(sizeof(uintmax_t) * 3); buffer.resize(sizeof(uintmax) * 3);
char* p = buffer.get(); char* p = buffer.get();
unsigned size = 0; uint size = 0;
do { do {
p[size++] = '0' + (value % 10); p[size++] = '0' + (value % 10);
value /= 10; value /= 10;
@ -108,14 +108,14 @@ auto decimal(uintmax_t value, long precision, char padchar) -> string {
return buffer; return buffer;
} }
auto hex(uintmax_t value, long precision, char padchar) -> string { auto hex(uintmax value, long precision, char padchar) -> string {
string buffer; string buffer;
buffer.resize(sizeof(uintmax_t) * 2); buffer.resize(sizeof(uintmax) * 2);
char* p = buffer.get(); char* p = buffer.get();
unsigned size = 0; uint size = 0;
do { do {
unsigned n = value & 15; uint n = value & 15;
p[size++] = n < 10 ? '0' + n : 'a' + n - 10; p[size++] = n < 10 ? '0' + n : 'a' + n - 10;
value >>= 4; value >>= 4;
} while(value); } while(value);
@ -125,12 +125,12 @@ auto hex(uintmax_t value, long precision, char padchar) -> string {
return buffer; return buffer;
} }
auto octal(uintmax_t value, long precision, char padchar) -> string { auto octal(uintmax value, long precision, char padchar) -> string {
string buffer; string buffer;
buffer.resize(sizeof(uintmax_t) * 3); buffer.resize(sizeof(uintmax) * 3);
char* p = buffer.get(); char* p = buffer.get();
unsigned size = 0; uint size = 0;
do { do {
p[size++] = '0' + (value & 7); p[size++] = '0' + (value & 7);
value >>= 3; value >>= 3;
@ -141,12 +141,12 @@ auto octal(uintmax_t value, long precision, char padchar) -> string {
return buffer; return buffer;
} }
auto binary(uintmax_t value, long precision, char padchar) -> string { auto binary(uintmax value, long precision, char padchar) -> string {
string buffer; string buffer;
buffer.resize(sizeof(uintmax_t) * 8); buffer.resize(sizeof(uintmax) * 8);
char* p = buffer.get(); char* p = buffer.get();
unsigned size = 0; uint size = 0;
do { do {
p[size++] = '0' + (value & 1); p[size++] = '0' + (value & 1);
value >>= 1; value >>= 1;
@ -158,12 +158,12 @@ auto binary(uintmax_t value, long precision, char padchar) -> string {
} }
template<typename T> auto pointer(const T* value, long precision) -> string { template<typename T> auto pointer(const T* value, long precision) -> string {
if(value == nullptr) return "(null)"; if(value == nullptr) return "(nullptr)";
return {"0x", hex((uintptr_t)value, precision)}; return {"0x", hex((uintptr)value, precision)};
} }
auto pointer(uintptr_t value, long precision) -> string { auto pointer(uintptr value, long precision) -> string {
if(value == 0) return "(null)"; if(value == 0) return "(nullptr)";
return {"0x", hex(value, precision)}; return {"0x", hex(value, precision)};
} }

View File

@ -5,7 +5,7 @@ namespace nall {
auto lstring::operator==(const lstring& source) const -> bool { auto lstring::operator==(const lstring& source) const -> bool {
if(this == &source) return true; if(this == &source) return true;
if(size() != source.size()) return false; if(size() != source.size()) return false;
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
if(operator[](n) != source[n]) return false; if(operator[](n) != source[n]) return false;
} }
return true; return true;
@ -32,15 +32,15 @@ auto lstring::append() -> lstring& {
return *this; return *this;
} }
auto lstring::find(rstring source) const -> maybe<unsigned> { auto lstring::find(rstring source) const -> maybe<uint> {
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
if(operator[](n).equals(source)) return n; if(operator[](n).equals(source)) return n;
} }
return nothing; return nothing;
} }
auto lstring::ifind(rstring source) const -> maybe<unsigned> { auto lstring::ifind(rstring source) const -> maybe<uint> {
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
if(operator[](n).iequals(source)) return n; if(operator[](n).iequals(source)) return n;
} }
return nothing; return nothing;
@ -48,7 +48,7 @@ auto lstring::ifind(rstring source) const -> maybe<unsigned> {
auto lstring::match(rstring pattern) const -> lstring { auto lstring::match(rstring pattern) const -> lstring {
lstring result; lstring result;
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
if(operator[](n).match(pattern)) result.append(operator[](n)); if(operator[](n).match(pattern)) result.append(operator[](n));
} }
return result; return result;
@ -56,7 +56,7 @@ auto lstring::match(rstring pattern) const -> lstring {
auto lstring::merge(rstring separator) const -> string { auto lstring::merge(rstring separator) const -> string {
string output; string output;
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
output.append(operator[](n)); output.append(operator[](n));
if(n < size() - 1) output.append(separator.data()); if(n < size() - 1) output.append(separator.data());
} }
@ -64,7 +64,7 @@ auto lstring::merge(rstring separator) const -> string {
} }
auto lstring::strip() -> lstring& { auto lstring::strip() -> lstring& {
for(unsigned n = 0; n < size(); n++) { for(uint n = 0; n < size(); n++) {
operator[](n).strip(); operator[](n).strip();
} }
return *this; return *this;

View File

@ -19,22 +19,22 @@ protected:
} }
//determine indentation level, without incrementing pointer //determine indentation level, without incrementing pointer
auto readDepth(const char* p) -> unsigned { auto readDepth(const char* p) -> uint {
unsigned depth = 0; uint depth = 0;
while(p[depth] == '\t' || p[depth] == ' ') depth++; while(p[depth] == '\t' || p[depth] == ' ') depth++;
return depth; return depth;
} }
//determine indentation level //determine indentation level
auto parseDepth(const char*& p) -> unsigned { auto parseDepth(const char*& p) -> uint {
unsigned depth = readDepth(p); uint depth = readDepth(p);
p += depth; p += depth;
return depth; return depth;
} }
//read name //read name
auto parseName(const char*& p) -> void { auto parseName(const char*& p) -> void {
unsigned length = 0; uint length = 0;
while(valid(p[length])) length++; while(valid(p[length])) length++;
if(length == 0) throw "Invalid node name"; if(length == 0) throw "Invalid node name";
_name = slice(p, 0, length); _name = slice(p, 0, length);
@ -43,19 +43,19 @@ protected:
auto parseData(const char*& p) -> void { auto parseData(const char*& p) -> void {
if(*p == '=' && *(p + 1) == '\"') { if(*p == '=' && *(p + 1) == '\"') {
unsigned length = 2; uint length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++; while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
if(p[length] != '\"') throw "Unescaped value"; if(p[length] != '\"') throw "Unescaped value";
_value = {slice(p, 2, length - 2), "\n"}; _value = {slice(p, 2, length - 2), "\n"};
p += length + 1; p += length + 1;
} else if(*p == '=') { } else if(*p == '=') {
unsigned length = 1; uint length = 1;
while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++; while(p[length] && p[length] != '\n' && p[length] != '\"' && p[length] != ' ') length++;
if(p[length] == '\"') throw "Illegal character in value"; if(p[length] == '\"') throw "Illegal character in value";
_value = {slice(p, 1, length - 1), "\n"}; _value = {slice(p, 1, length - 1), "\n"};
p += length; p += length;
} else if(*p == ':') { } else if(*p == ':') {
unsigned length = 1; uint length = 1;
while(p[length] && p[length] != '\n') length++; while(p[length] && p[length] != '\n') length++;
_value = {slice(p, 1, length - 1), "\n"}; _value = {slice(p, 1, length - 1), "\n"};
p += length; p += length;
@ -70,7 +70,7 @@ protected:
if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments if(*(p + 0) == '/' && *(p + 1) == '/') break; //skip comments
SharedNode node(new ManagedNode); SharedNode node(new ManagedNode);
unsigned length = 0; uint length = 0;
while(valid(p[length])) length++; while(valid(p[length])) length++;
if(length == 0) throw "Invalid attribute name"; if(length == 0) throw "Invalid attribute name";
node->_name = slice(p, 0, length); node->_name = slice(p, 0, length);
@ -81,7 +81,7 @@ protected:
} }
//read a node and all of its child nodes //read a node and all of its child nodes
auto parseNode(const lstring& text, unsigned& y) -> void { auto parseNode(const lstring& text, uint& y) -> void {
const char* p = text[y++]; const char* p = text[y++];
_metadata = parseDepth(p); _metadata = parseDepth(p);
parseName(p); parseName(p);
@ -89,7 +89,7 @@ protected:
parseAttributes(p); parseAttributes(p);
while(y < text.size()) { while(y < text.size()) {
unsigned depth = readDepth(text[y]); uint depth = readDepth(text[y]);
if(depth <= _metadata) break; if(depth <= _metadata) break;
if(text[y][depth] == ':') { if(text[y][depth] == ':') {
@ -132,7 +132,7 @@ protected:
if(document.size() == 0) return; //empty document if(document.size() == 0) return; //empty document
auto text = document.split("\n"); auto text = document.split("\n");
unsigned y = 0; uint y = 0;
while(y < text.size()) { while(y < text.size()) {
SharedNode node(new ManagedNode); SharedNode node(new ManagedNode);
node->parseNode(text, y); node->parseNode(text, y);
@ -154,7 +154,7 @@ inline auto unserialize(const string& markup) -> Markup::Node {
return (Markup::SharedNode&)node; return (Markup::SharedNode&)node;
} }
inline auto serialize(const Markup::Node& node, unsigned depth = 0) -> string { inline auto serialize(const Markup::Node& node, uint depth = 0) -> string {
if(!node.name()) { if(!node.name()) {
string result; string result;
for(auto leaf : node) { for(auto leaf : node) {

View File

@ -7,7 +7,7 @@ auto ManagedNode::_evaluate(string query) const -> bool {
if(!query) return true; if(!query) return true;
for(auto& rule : query.replace(" ", "").split(",")) { for(auto& rule : query.replace(" ", "").split(",")) {
enum class Comparator : unsigned { ID, EQ, NE, LT, LE, GT, GE }; enum class Comparator : uint { ID, EQ, NE, LT, LE, GT, GE };
auto comparator = Comparator::ID; auto comparator = Comparator::ID;
if(rule.match("*!=*")) comparator = Comparator::NE; if(rule.match("*!=*")) comparator = Comparator::NE;
else if(rule.match("*<=*")) comparator = Comparator::LE; else if(rule.match("*<=*")) comparator = Comparator::LE;
@ -58,7 +58,7 @@ auto ManagedNode::_find(const string& query) const -> vector<Node> {
lstring path = query.split("/"); lstring path = query.split("/");
string name = path.take(0), rule; string name = path.take(0), rule;
unsigned lo = 0u, hi = ~0u; uint lo = 0u, hi = ~0u;
if(name.match("*[*]")) { if(name.match("*[*]")) {
auto p = name.rtrim("]", 1L).split("[", 1L); auto p = name.rtrim("]", 1L).split("[", 1L);
@ -78,7 +78,7 @@ auto ManagedNode::_find(const string& query) const -> vector<Node> {
rule = p(1); rule = p(1);
} }
unsigned position = 0; uint position = 0;
for(auto& node : _children) { for(auto& node : _children) {
if(!node->_name.match(name)) continue; if(!node->_name.match(name)) continue;
if(!node->_evaluate(rule)) continue; if(!node->_evaluate(rule)) continue;

View File

@ -62,12 +62,13 @@ struct Node {
auto boolean() const -> bool { return text() == "true"; } auto boolean() const -> bool { return text() == "true"; }
auto integer() const -> intmax { return text().integer(); } auto integer() const -> intmax { return text().integer(); }
auto natural() const -> uintmax { return text().natural(); } auto natural() const -> uintmax { return text().natural(); }
auto real() const -> double { return text().real(); }
auto setName(const string& name = "") -> Node& { shared->_name = name; return *this; } auto setName(const string& name = "") -> Node& { shared->_name = name; return *this; }
auto setValue(const string& value = "") -> Node& { shared->_value = value; return *this; } auto setValue(const string& value = "") -> Node& { shared->_value = value; return *this; }
auto reset() -> void { shared->_children.reset(); } auto reset() -> void { shared->_children.reset(); }
auto size() const -> unsigned { return shared->_children.size(); } auto size() const -> uint { return shared->_children.size(); }
auto prepend(const Node& node) -> void { shared->_children.prepend(node.shared); } auto prepend(const Node& node) -> void { shared->_children.prepend(node.shared); }
auto append(const Node& node) -> void { shared->_children.append(node.shared); } auto append(const Node& node) -> void { shared->_children.append(node.shared); }
@ -80,17 +81,17 @@ struct Node {
return false; return false;
} }
auto insert(unsigned position, const Node& node) -> bool { auto insert(uint position, const Node& node) -> bool {
if(position > size()) return false; //used > instead of >= to allow indexed-equivalent of append() if(position > size()) return false; //used > instead of >= to allow indexed-equivalent of append()
return shared->_children.insert(position, node.shared), true; return shared->_children.insert(position, node.shared), true;
} }
auto remove(unsigned position) -> bool { auto remove(uint position) -> bool {
if(position >= size()) return false; if(position >= size()) return false;
return shared->_children.remove(position), true; return shared->_children.remove(position), true;
} }
auto swap(unsigned x, unsigned y) -> bool { auto swap(uint x, uint y) -> bool {
if(x >= size() || y >= size()) return false; if(x >= size() || y >= size()) return false;
return std::swap(shared->_children[x], shared->_children[y]), true; return std::swap(shared->_children[x], shared->_children[y]), true;
} }
@ -103,7 +104,7 @@ struct Node {
}); });
} }
auto operator[](signed position) -> Node { auto operator[](int position) -> Node {
if(position >= size()) return {}; if(position >= size()) return {};
return shared->_children[position]; return shared->_children[position];
} }
@ -116,11 +117,11 @@ struct Node {
auto operator*() -> Node { return {source.shared->_children[position]}; } auto operator*() -> Node { return {source.shared->_children[position]}; }
auto operator!=(const iterator& source) const -> bool { return position != source.position; } auto operator!=(const iterator& source) const -> bool { return position != source.position; }
auto operator++() -> iterator& { return position++, *this; } auto operator++() -> iterator& { return position++, *this; }
iterator(const Node& source, unsigned position) : source(source), position(position) {} iterator(const Node& source, uint position) : source(source), position(position) {}
private: private:
const Node& source; const Node& source;
unsigned position; uint position;
}; };
auto begin() const -> iterator { return iterator(*this, 0); } auto begin() const -> iterator { return iterator(*this, 0); }
@ -136,7 +137,7 @@ protected:
namespace nall { namespace nall {
inline range_t range(const Markup::Node& node) { inline range_t range(const Markup::Node& node) {
return range_t{0, (signed)node.size(), 1}; return range_t{0, (int)node.size(), 1};
} }
} }

View File

@ -43,7 +43,7 @@ protected:
} }
//copy part of string from source document into target string; decode markup while copying //copy part of string from source document into target string; decode markup while copying
inline void copy(string& target, const char* source, unsigned length) { inline void copy(string& target, const char* source, uint length) {
target.reserve(length + 1); target.reserve(length + 1);
#if defined(NALL_XML_LITERAL) #if defined(NALL_XML_LITERAL)
@ -106,7 +106,7 @@ protected:
//DOCTYPE //DOCTYPE
if(!memory::compare(p, "<!DOCTYPE", 9)) { if(!memory::compare(p, "<!DOCTYPE", 9)) {
unsigned counter = 0; uint counter = 0;
do { do {
char n = *p++; char n = *p++;
if(!n) throw "unclosed DOCTYPE"; if(!n) throw "unclosed DOCTYPE";

View File

@ -29,7 +29,7 @@ private:
}; };
vector<Variable> variables; vector<Variable> variables;
auto parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool; auto parseDocument(const string& filedata, const string& pathname, uint depth) -> bool;
}; };
auto CML::parse(const string& filename) -> string { auto CML::parse(const string& filename) -> string {
@ -45,7 +45,7 @@ auto CML::parse(const string& filedata, const string& pathname) -> string {
return state.output; return state.output;
} }
auto CML::parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool { auto CML::parseDocument(const string& filedata, const string& pathname, uint depth) -> bool {
if(depth >= 100) return false; //prevent infinite recursion if(depth >= 100) return false; //prevent infinite recursion
auto vendorAppend = [&](const string& name, const string& value) { auto vendorAppend = [&](const string& name, const string& value) {

View File

@ -27,12 +27,12 @@ private:
struct State { struct State {
string output; string output;
unsigned sections = 0; uint sections = 0;
} state; } state;
auto parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool; auto parseDocument(const string& filedata, const string& pathname, uint depth) -> bool;
auto parseBlock(string& block, const string& pathname, unsigned depth) -> bool; auto parseBlock(string& block, const string& pathname, uint depth) -> bool;
auto count(const string& text, char value) -> unsigned; auto count(const string& text, char value) -> uint;
auto escape(const string& text) -> string; auto escape(const string& text) -> string;
auto markup(const string& text) -> string; auto markup(const string& text) -> string;
@ -51,7 +51,7 @@ auto DML::parse(const string& filename) -> string {
return state.output; return state.output;
} }
auto DML::parseDocument(const string& filedata, const string& pathname, unsigned depth) -> bool { auto DML::parseDocument(const string& filedata, const string& pathname, uint depth) -> bool {
if(depth >= 100) return false; //attempt to prevent infinite recursion with reasonable limit if(depth >= 100) return false; //attempt to prevent infinite recursion with reasonable limit
auto blocks = filedata.split("\n\n"); auto blocks = filedata.split("\n\n");
@ -60,7 +60,7 @@ auto DML::parseDocument(const string& filedata, const string& pathname, unsigned
return true; return true;
} }
auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> bool { auto DML::parseBlock(string& block, const string& pathname, uint depth) -> bool {
if(block.rstrip().empty()) return true; if(block.rstrip().empty()) return true;
auto lines = block.split("\n"); auto lines = block.split("\n");
@ -114,7 +114,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
//navigation //navigation
else if(count(block, '-')) { else if(count(block, '-')) {
state.output.append("<nav>\n"); state.output.append("<nav>\n");
unsigned level = 0; uint level = 0;
for(auto& line : lines) { for(auto& line : lines) {
if(auto depth = count(line, '-')) { if(auto depth = count(line, '-')) {
while(level < depth) level++, state.output.append("<ul>\n"); while(level < depth) level++, state.output.append("<ul>\n");
@ -131,7 +131,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
//list //list
else if(count(block, '*')) { else if(count(block, '*')) {
unsigned level = 0; uint level = 0;
for(auto& line : lines) { for(auto& line : lines) {
if(auto depth = count(line, '*')) { if(auto depth = count(line, '*')) {
while(level < depth) level++, state.output.append("<ul>\n"); while(level < depth) level++, state.output.append("<ul>\n");
@ -145,7 +145,7 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
//quote //quote
else if(count(block, '>')) { else if(count(block, '>')) {
unsigned level = 0; uint level = 0;
for(auto& line : lines) { for(auto& line : lines) {
if(auto depth = count(line, '>')) { if(auto depth = count(line, '>')) {
while(level < depth) level++, state.output.append("<blockquote>\n"); while(level < depth) level++, state.output.append("<blockquote>\n");
@ -180,8 +180,8 @@ auto DML::parseBlock(string& block, const string& pathname, unsigned depth) -> b
return true; return true;
} }
auto DML::count(const string& text, char value) -> unsigned { auto DML::count(const string& text, char value) -> uint {
for(unsigned n = 0; n < text.size(); n++) { for(uint n = 0; n < text.size(); n++) {
if(text[n] != value) { if(text[n] != value) {
if(text[n] == ' ') return n; if(text[n] == ' ') return n;
break; break;
@ -206,8 +206,8 @@ auto DML::markup(const string& text) -> string {
string output; string output;
char match = 0; char match = 0;
unsigned offset = 0; uint offset = 0;
for(unsigned n = 0; n < text.size();) { for(uint n = 0; n < text.size();) {
char a = n ? text[n - 1] : 0; char a = n ? text[n - 1] : 0;
char b = text[n]; char b = text[n];
char c = text[n++ + 1]; char c = text[n++ + 1];

View File

@ -13,7 +13,7 @@ auto string::read(rstring filename) -> string {
if(!fp) return result; if(!fp) return result;
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
signed filesize = ftell(fp); int filesize = ftell(fp);
if(filesize < 0) return fclose(fp), result; if(filesize < 0) return fclose(fp), result;
rewind(fp); rewind(fp);
@ -22,7 +22,7 @@ auto string::read(rstring filename) -> string {
return fclose(fp), result; return fclose(fp), result;
} }
auto string::repeat(rstring pattern, unsigned times) -> string { auto string::repeat(rstring pattern, uint times) -> string {
string result; string result;
while(times--) result.append(pattern.data()); while(times--) result.append(pattern.data());
return result; return result;
@ -35,13 +35,13 @@ auto string::fill(char fill) -> string& {
auto string::hash() const -> unsigned { auto string::hash() const -> unsigned {
const char* p = data(); const char* p = data();
unsigned length = size(); uint length = size();
unsigned result = 5381; uint result = 5381;
while(length--) result = (result << 5) + result + *p++; while(length--) result = (result << 5) + result + *p++;
return result; return result;
} }
auto string::remove(unsigned offset, unsigned length) -> string& { auto string::remove(uint offset, uint length) -> string& {
char* p = get(); char* p = get();
length = min(length, size()); length = min(length, size());
memory::move(p + offset, p + offset + length, size() - length); memory::move(p + offset, p + offset + length, size() - length);
@ -50,16 +50,16 @@ auto string::remove(unsigned offset, unsigned length) -> string& {
auto string::reverse() -> string& { auto string::reverse() -> string& {
char* p = get(); char* p = get();
unsigned length = size(); uint length = size();
unsigned pivot = length >> 1; uint pivot = length >> 1;
for(signed x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]); for(int x = 0, y = length - 1; x < pivot && y >= 0; x++, y--) std::swap(p[x], p[y]);
return *this; return *this;
} }
//+length => insert/delete from start (right justify) //+length => insert/delete from start (right justify)
//-length => insert/delete from end (left justify) //-length => insert/delete from end (left justify)
auto string::size(signed length, char fill) -> string& { auto string::size(int length, char fill) -> string& {
unsigned size = this->size(); uint size = this->size();
if(size == length) return *this; if(size == length) return *this;
bool right = length >= 0; bool right = length >= 0;
@ -68,13 +68,13 @@ auto string::size(signed length, char fill) -> string& {
if(size < length) { //expand if(size < length) { //expand
resize(length); resize(length);
char* p = get(); char* p = get();
unsigned displacement = length - size; uint displacement = length - size;
if(right) memory::move(p + displacement, p, size); if(right) memory::move(p + displacement, p, size);
else p += size; else p += size;
while(displacement--) *p++ = fill; while(displacement--) *p++ = fill;
} else { //shrink } else { //shrink
char* p = get(); char* p = get();
unsigned displacement = size - length; uint displacement = size - length;
if(right) memory::move(p, p + displacement, length); if(right) memory::move(p, p + displacement, length);
resize(length); resize(length);
} }
@ -82,7 +82,7 @@ auto string::size(signed length, char fill) -> string& {
return *this; return *this;
} }
auto slice(rstring self, signed offset, signed length) -> string { auto slice(rstring self, int offset, int length) -> string {
string result; string result;
if(offset < self.size()) { if(offset < self.size()) {
if(length < 0) length = self.size() - offset; if(length < 0) length = self.size() - offset;
@ -92,36 +92,36 @@ auto slice(rstring self, signed offset, signed length) -> string {
return result; return result;
} }
auto integer(char* result, intmax_t value) -> char* { auto integer(char* result, intmax value) -> char* {
bool negative = value < 0; bool negative = value < 0;
if(negative) value = -value; if(negative) value = -value;
char buffer[64]; char buffer[64];
unsigned size = 0; uint size = 0;
do { do {
unsigned n = value % 10; uint n = value % 10;
buffer[size++] = '0' + n; buffer[size++] = '0' + n;
value /= 10; value /= 10;
} while(value); } while(value);
if(negative) buffer[size++] = '-'; if(negative) buffer[size++] = '-';
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y]; for(int x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
result[size] = 0; result[size] = 0;
return result; return result;
} }
auto decimal(char* result, uintmax_t value) -> char* { auto decimal(char* result, uintmax value) -> char* {
char buffer[64]; char buffer[64];
unsigned size = 0; uint size = 0;
do { do {
unsigned n = value % 10; uint n = value % 10;
buffer[size++] = '0' + n; buffer[size++] = '0' + n;
value /= 10; value /= 10;
} while(value); } while(value);
for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y]; for(int x = size - 1, y = 0; x >= 0 && y < size; x--, y++) result[x] = buffer[y];
result[size] = 0; result[size] = 0;
return result; return result;
} }
@ -129,7 +129,7 @@ auto decimal(char* result, uintmax_t value) -> char* {
//using sprintf is certainly not the most ideal method to convert //using sprintf is certainly not the most ideal method to convert
//a double to a string ... but attempting to parse a double by //a double to a string ... but attempting to parse a double by
//hand, digit-by-digit, results in subtle rounding errors. //hand, digit-by-digit, results in subtle rounding errors.
auto real(char* result, long double value) -> unsigned { auto real(char* result, long double value) -> uint {
char buffer[256]; char buffer[256];
#ifdef _WIN32 #ifdef _WIN32
//Windows C-runtime does not support long double via sprintf() //Windows C-runtime does not support long double via sprintf()
@ -150,7 +150,7 @@ auto real(char* result, long double value) -> unsigned {
} }
} }
unsigned length = strlen(buffer); uint length = strlen(buffer);
if(result) strcpy(result, buffer); if(result) strcpy(result, buffer);
return length + 1; return length + 1;
} }

View File

@ -151,7 +151,7 @@ using int4 = nall::int_t<4>;
using int5 = nall::int_t<5>; using int5 = nall::int_t<5>;
using int6 = nall::int_t<6>; using int6 = nall::int_t<6>;
using int7 = nall::int_t<7>; using int7 = nall::int_t<7>;
using int8 = int8_t;
using int9 = nall::int_t<9>; using int9 = nall::int_t<9>;
using int10 = nall::int_t<10>; using int10 = nall::int_t<10>;
using int11 = nall::int_t<11>; using int11 = nall::int_t<11>;
@ -159,7 +159,7 @@ using int12 = nall::int_t<12>;
using int13 = nall::int_t<13>; using int13 = nall::int_t<13>;
using int14 = nall::int_t<14>; using int14 = nall::int_t<14>;
using int15 = nall::int_t<15>; using int15 = nall::int_t<15>;
using int16 = int16_t;
using int17 = nall::int_t<17>; using int17 = nall::int_t<17>;
using int18 = nall::int_t<18>; using int18 = nall::int_t<18>;
using int19 = nall::int_t<19>; using int19 = nall::int_t<19>;
@ -175,7 +175,7 @@ using int28 = nall::int_t<28>;
using int29 = nall::int_t<29>; using int29 = nall::int_t<29>;
using int30 = nall::int_t<30>; using int30 = nall::int_t<30>;
using int31 = nall::int_t<31>; using int31 = nall::int_t<31>;
using int32 = int32_t;
using int33 = nall::int_t<33>; using int33 = nall::int_t<33>;
using int34 = nall::int_t<34>; using int34 = nall::int_t<34>;
using int35 = nall::int_t<35>; using int35 = nall::int_t<35>;
@ -207,9 +207,6 @@ using int60 = nall::int_t<60>;
using int61 = nall::int_t<61>; using int61 = nall::int_t<61>;
using int62 = nall::int_t<62>; using int62 = nall::int_t<62>;
using int63 = nall::int_t<63>; using int63 = nall::int_t<63>;
using int64 = int64_t;
using intmax = intmax_t;
using intptr = intptr_t;
using uint1 = nall::uint_t<1>; using uint1 = nall::uint_t<1>;
using uint2 = nall::uint_t<2>; using uint2 = nall::uint_t<2>;
@ -218,7 +215,7 @@ using uint4 = nall::uint_t<4>;
using uint5 = nall::uint_t<5>; using uint5 = nall::uint_t<5>;
using uint6 = nall::uint_t<6>; using uint6 = nall::uint_t<6>;
using uint7 = nall::uint_t<7>; using uint7 = nall::uint_t<7>;
using uint8 = uint8_t;
using uint9 = nall::uint_t<9>; using uint9 = nall::uint_t<9>;
using uint10 = nall::uint_t<10>; using uint10 = nall::uint_t<10>;
using uint11 = nall::uint_t<11>; using uint11 = nall::uint_t<11>;
@ -226,7 +223,7 @@ using uint12 = nall::uint_t<12>;
using uint13 = nall::uint_t<13>; using uint13 = nall::uint_t<13>;
using uint14 = nall::uint_t<14>; using uint14 = nall::uint_t<14>;
using uint15 = nall::uint_t<15>; using uint15 = nall::uint_t<15>;
using uint16 = uint16_t;
using uint17 = nall::uint_t<17>; using uint17 = nall::uint_t<17>;
using uint18 = nall::uint_t<18>; using uint18 = nall::uint_t<18>;
using uint19 = nall::uint_t<19>; using uint19 = nall::uint_t<19>;
@ -242,7 +239,7 @@ using uint28 = nall::uint_t<28>;
using uint29 = nall::uint_t<29>; using uint29 = nall::uint_t<29>;
using uint30 = nall::uint_t<30>; using uint30 = nall::uint_t<30>;
using uint31 = nall::uint_t<31>; using uint31 = nall::uint_t<31>;
using uint32 = uint32_t;
using uint33 = nall::uint_t<33>; using uint33 = nall::uint_t<33>;
using uint34 = nall::uint_t<34>; using uint34 = nall::uint_t<34>;
using uint35 = nall::uint_t<35>; using uint35 = nall::uint_t<35>;
@ -274,13 +271,5 @@ using uint60 = nall::uint_t<60>;
using uint61 = nall::uint_t<61>; using uint61 = nall::uint_t<61>;
using uint62 = nall::uint_t<62>; using uint62 = nall::uint_t<62>;
using uint63 = nall::uint_t<63>; using uint63 = nall::uint_t<63>;
using uint64 = uint64_t;
using uintmax = uintmax_t;
using uintptr = uintptr_t;
#if defined(__SIZEOF_INT128__)
using int128 = int128_t;
using uint128 = uint128_t;
#endif
#endif #endif

View File

@ -1,7 +1,7 @@
Video video; Video video;
Video::Video() { Video::Video() {
palette = new uint32_t[1 << 19](); palette = new uint32[1 << 19]();
} }
Video::~Video() { Video::~Video() {
@ -88,15 +88,15 @@ auto Video::draw_cursor(uint16 color, int x, int y) -> void {
for(int cx = 0; cx < 15; cx++) { for(int cx = 0; cx < 15; cx++) {
int vx = x + cx - 7; int vx = x + cx - 7;
if(vx < 0 || vx >= 256) continue; //do not draw offscreen if(vx < 0 || vx >= 256) continue; //do not draw offscreen
uint8_t pixel = cursor[cy * 15 + cx]; uint8 pixel = cursor[cy * 15 + cx];
if(pixel == 0) continue; if(pixel == 0) continue;
uint32_t pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color); uint32 pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color);
if(hires == false) { if(hires == false) {
*((uint32_t*)data + vy * 1024 + vx) = pixelcolor; *((uint32*)data + vy * 1024 + vx) = pixelcolor;
} else { } else {
*((uint32_t*)data + vy * 1024 + vx * 2 + 0) = pixelcolor; *((uint32*)data + vy * 1024 + vx * 2 + 0) = pixelcolor;
*((uint32_t*)data + vy * 1024 + vx * 2 + 1) = pixelcolor; *((uint32*)data + vy * 1024 + vx * 2 + 1) = pixelcolor;
} }
} }
} }
@ -106,14 +106,14 @@ auto Video::update() -> void {
switch(configuration.controllerPort2) { switch(configuration.controllerPort2) {
case Device::ID::SuperScope: case Device::ID::SuperScope:
if(dynamic_cast<SuperScope*>(device.controllerPort2)) { if(dynamic_cast<SuperScope*>(device.controllerPort2)) {
auto controller = (SuperScope&)*device.controllerPort2; auto& controller = (SuperScope&)*device.controllerPort2;
draw_cursor(0x7c00, controller.x, controller.y); draw_cursor(0x7c00, controller.x, controller.y);
} }
break; break;
case Device::ID::Justifier: case Device::ID::Justifier:
case Device::ID::Justifiers: case Device::ID::Justifiers:
if(dynamic_cast<Justifier*>(device.controllerPort2)) { if(dynamic_cast<Justifier*>(device.controllerPort2)) {
auto controller = (Justifier&)*device.controllerPort2; auto& controller = (Justifier&)*device.controllerPort2;
draw_cursor(0x001f, controller.player1.x, controller.player1.y); draw_cursor(0x001f, controller.player1.x, controller.player1.y);
if(!controller.chained) break; if(!controller.chained) break;
draw_cursor(0x02e0, controller.player2.x, controller.player2.y); draw_cursor(0x02e0, controller.player2.x, controller.player2.y);

View File

@ -1,76 +1,49 @@
#include "../tomoko.hpp" #include "../tomoko.hpp"
ConfigurationManager* config = nullptr; Settings settings;
EmulatorSettings* emulatorSettings = nullptr;
ConfigurationManager::ConfigurationManager() { Settings::Settings() {
config = this; Markup::Node::operator=(BML::unserialize(string::read(locate({configpath(), "tomoko/"}, "settings.bml"))));
userInterface.append(userInterface.showStatusBar, "ShowStatusBar"); auto set = [&](const string& name, const string& value) {
append(userInterface, "UserInterface"); //create node and set to default value only if it does not already exist
if(!operator[](name)) operator()(name).setValue(value);
};
library.append(library.location, "Location"); set("UserInterface/ShowStatusBar", true);
append(library, "Library");
video.append(video.driver, "Driver"); set("Library/Location", {userpath(), "Emulation/"});
video.append(video.synchronize, "Synchronize"); set("Library/IgnoreManifests", false);
video.append(video.scale, "Scale");
video.append(video.aspectCorrection, "AspectCorrection");
video.append(video.filter, "Filter");
video.append(video.shader, "Shader");
video.append(video.colorEmulation, "ColorEmulation");
video.append(video.saturation, "Saturation");
video.append(video.gamma, "Gamma");
video.append(video.luminance, "Luminance");
video.overscan.append(video.overscan.mask, "Mask");
video.overscan.append(video.overscan.horizontal, "Horizontal");
video.overscan.append(video.overscan.vertical, "Vertical");
video.append(video.overscan, "Overscan");
append(video, "Video");
audio.append(audio.driver, "Driver"); set("Video/Driver", ruby::Video::optimalDriver());
audio.append(audio.device, "Device"); set("Video/Synchronize", false);
audio.append(audio.synchronize, "Synchronize"); set("Video/Scale", "Small");
audio.append(audio.mute, "Mute"); set("Video/AspectCorrection", true);
audio.append(audio.volume, "Volume"); set("Video/Filter", "Blur");
audio.append(audio.frequency, "Frequency"); set("Video/Shader", "None");
audio.append(audio.latency, "Latency"); set("Video/ColorEmulation", true);
audio.append(audio.resampler, "Resampler"); set("Video/Saturation", 100);
append(audio, "Audio"); set("Video/Gamma", 100);
set("Video/Luminance", 100);
input.append(input.driver, "Driver"); set("Video/Overscan/Mask", false);
append(input, "Input"); set("Video/Overscan/Horizontal", 8);
set("Video/Overscan/Vertical", 8);
timing.append(timing.video, "Video"); set("Audio/Driver", ruby::Audio::optimalDriver());
timing.append(timing.audio, "Audio"); set("Audio/Device", "");
append(timing, "Timing"); set("Audio/Synchronize", true);
set("Audio/Mute", false);
set("Audio/Volume", 100);
set("Audio/Frequency", 48000);
set("Audio/Latency", 60);
set("Audio/Resampler", "Sinc");
load(locate({configpath(), "tomoko/"}, "settings.bml")); set("Input/Driver", ruby::Input::optimalDriver());
if(!library.location) library.location = {userpath(), "Emulation/"};
if(!video.driver) video.driver = ruby::Video::safestDriver(); set("Timing/Video", 60.0);
if(!audio.driver) audio.driver = ruby::Audio::safestDriver(); set("Timing/Audio", 48000.0);
if(!input.driver) input.driver = ruby::Input::safestDriver();
save(locate({configpath(), "tomoko/"}, "settings.bml"));
} }
auto ConfigurationManager::quit() -> void { auto Settings::quit() -> void {
save(locate({configpath(), "tomoko/"}, "settings.bml")); file::write(locate({configpath(), "tomoko/"}, "settings.bml"), BML::serialize(*this));
}
EmulatorSettings::EmulatorSettings() {
emulatorSettings = this;
(Markup::Node&)*this = BML::unserialize(string::read(locate({configpath(), "tomoko/"}, "emulators.bml")));
}
auto EmulatorSettings::quit() -> void {
file::write(locate({configpath(), "tomoko/"}, "emulators.bml"), BML::serialize(*this));
}
auto EmulatorSettings::get(string name) -> string {
name.replace(" ", "");
return (*this)(name).text();
}
auto EmulatorSettings::set(string name, string value) -> void {
name.replace(" ", "");
(*this)(name).setValue(value);
} }

View File

@ -1,62 +1,6 @@
struct ConfigurationManager : Configuration::Document { struct Settings : Markup::Node {
ConfigurationManager(); Settings();
auto quit() -> void; auto quit() -> void;
struct UserInterface : Configuration::Node {
bool showStatusBar = true;
} userInterface;
struct Library : Configuration::Node {
string location;
} library;
struct Video : Configuration::Node {
string driver;
bool synchronize = false;
string scale = "Small";
bool aspectCorrection = true;
string filter = "Blur";
string shader = "None";
bool colorEmulation = true;
unsigned saturation = 100;
unsigned gamma = 100;
unsigned luminance = 100;
struct Overscan : Configuration::Node {
bool mask = false;
unsigned horizontal = 8;
unsigned vertical = 8;
} overscan;
} video;
struct Audio : Configuration::Node {
string driver;
string device;
bool synchronize = true;
bool mute = false;
unsigned volume = 100;
unsigned frequency = 48000;
unsigned latency = 60;
string resampler = "Sinc";
} audio;
struct Input : Configuration::Node {
string driver;
} input;
struct Timing : Configuration::Node {
double video = 60.0;
double audio = 48000.0;
} timing;
}; };
struct EmulatorSettings : Markup::Node { extern Settings settings;
EmulatorSettings();
auto quit() -> void;
auto get(string name) -> string;
auto set(string name, string value) -> void;
};
extern ConfigurationManager* config;
extern EmulatorSettings* emulatorSettings;

View File

@ -46,8 +46,8 @@ auto InputManager::appendHotkeys() -> void {
audio->set(Audio::Synchronize, false); audio->set(Audio::Synchronize, false);
}; };
hotkey->release = [] { hotkey->release = [] {
video->set(Video::Synchronize, ::config->video.synchronize); video->set(Video::Synchronize, settings["Video/Synchronize"].boolean());
audio->set(Audio::Synchronize, ::config->audio.synchronize); audio->set(Audio::Synchronize, settings["Audio/Synchronize"].boolean());
}; };
hotkeys.append(hotkey); hotkeys.append(hotkey);
} }
@ -68,11 +68,11 @@ auto InputManager::appendHotkeys() -> void {
hotkeys.append(hotkey); hotkeys.append(hotkey);
} }
Configuration::Node nodeHotkeys;
for(auto& hotkey : hotkeys) { for(auto& hotkey : hotkeys) {
nodeHotkeys.append(hotkey->assignment, string{hotkey->name}.replace(" ", "")); hotkey->path = string{"Hotkey/", hotkey->name}.replace(" ", "");
hotkey->assignment = settings(hotkey->path).text();
hotkey->bind();
} }
config.append(nodeHotkeys, "Hotkeys");
} }
auto InputManager::pollHotkeys() -> void { auto InputManager::pollHotkeys() -> void {

View File

@ -22,6 +22,8 @@ auto InputMapping::bind() -> void {
if(qualifier == "Rumble") this->qualifier = Qualifier::Rumble; if(qualifier == "Rumble") this->qualifier = Qualifier::Rumble;
break; break;
} }
settings[path].setValue(assignment);
} }
auto InputMapping::bind(shared_pointer<HID::Device> device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool { auto InputMapping::bind(shared_pointer<HID::Device> device, unsigned group, unsigned input, int16 oldValue, int16 newValue) -> bool {
@ -110,11 +112,12 @@ auto InputMapping::rumble(bool enable) -> void {
} }
auto InputMapping::unbind() -> void { auto InputMapping::unbind() -> void {
this->assignment = "None"; assignment = "None";
this->device = nullptr; device = nullptr;
this->group = 0; group = 0;
this->input = 0; input = 0;
this->qualifier = Qualifier::None; qualifier = Qualifier::None;
settings[path].setValue(assignment);
} }
auto InputMapping::assignmentName() -> string { auto InputMapping::assignmentName() -> string {
@ -140,21 +143,15 @@ InputManager::InputManager() {
inputManager = this; inputManager = this;
for(auto& emulator : program->emulators) { for(auto& emulator : program->emulators) {
Configuration::Node nodeEmulator;
emulators.append(InputEmulator()); emulators.append(InputEmulator());
auto& inputEmulator = emulators.last(); auto& inputEmulator = emulators.last();
inputEmulator.name = emulator->information.name; inputEmulator.name = emulator->information.name;
for(auto& port : emulator->port) { for(auto& port : emulator->port) {
Configuration::Node nodePort;
inputEmulator.ports.append(InputPort()); inputEmulator.ports.append(InputPort());
auto& inputPort = inputEmulator.ports.last(); auto& inputPort = inputEmulator.ports.last();
inputPort.name = port.name; inputPort.name = port.name;
for(auto& device : port.device) { for(auto& device : port.device) {
Configuration::Node nodeDevice;
inputPort.devices.append(InputDevice()); inputPort.devices.append(InputDevice());
auto& inputDevice = inputPort.devices.last(); auto& inputDevice = inputPort.devices.last();
inputDevice.name = device.name; inputDevice.name = device.name;
@ -164,23 +161,17 @@ InputManager::InputManager() {
auto& inputMapping = inputDevice.mappings.last(); auto& inputMapping = inputDevice.mappings.last();
inputMapping->name = input.name; inputMapping->name = input.name;
inputMapping->link = &input; inputMapping->link = &input;
input.guid = (uintptr_t)inputMapping; input.guid = (uintptr)inputMapping;
nodeDevice.append(inputMapping->assignment, string{inputMapping->name}.replace(" ", "")); inputMapping->path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping->name}.replace(" ", "");
inputMapping->assignment = settings(inputMapping->path).text();
inputMapping->bind();
} }
nodePort.append(nodeDevice, string{inputDevice.name}.replace(" ", ""));
} }
nodeEmulator.append(nodePort, string{inputPort.name}.replace(" ", ""));
} }
config.append(nodeEmulator, string{inputEmulator.name}.replace(" ", ""));
} }
appendHotkeys(); appendHotkeys();
config.load(locate({configpath(), "tomoko/"}, "input.bml"));
config.save(locate({configpath(), "tomoko/"}, "input.bml"));
} }
auto InputManager::bind() -> void { auto InputManager::bind() -> void {
@ -224,7 +215,6 @@ auto InputManager::onChange(shared_pointer<HID::Device> device, unsigned group,
} }
auto InputManager::quit() -> void { auto InputManager::quit() -> void {
config.save(locate({configpath(), "tomoko/"}, "input.bml"));
emulators.reset(); emulators.reset();
hotkeys.reset(); hotkeys.reset();
} }

View File

@ -12,7 +12,8 @@ struct InputMapping {
auto assignmentName() -> string; auto assignmentName() -> string;
auto deviceName() -> string; auto deviceName() -> string;
string name; string path; //configuration file key path
string name; //input name (human readable)
string assignment = "None"; string assignment = "None";
Emulator::Interface::Device::Input* link = nullptr; Emulator::Interface::Device::Input* link = nullptr;
shared_pointer<HID::Device> device; shared_pointer<HID::Device> device;
@ -59,7 +60,6 @@ struct InputManager {
vector<shared_pointer<HID::Device>> devices; vector<shared_pointer<HID::Device>> devices;
vector<InputEmulator> emulators; vector<InputEmulator> emulators;
vector<InputHotkey*> hotkeys; vector<InputHotkey*> hotkeys;
Configuration::Document config;
}; };
extern InputManager* inputManager; extern InputManager* inputManager;

View File

@ -10,10 +10,10 @@ Presentation::Presentation() {
if(!media.bootable) continue; if(!media.bootable) continue;
auto item = new MenuItem{&libraryMenu}; auto item = new MenuItem{&libraryMenu};
item->setText({media.name, " ..."}).onActivate([=] { item->setText({media.name, " ..."}).onActivate([=] {
directory::create({config->library.location, media.name}); directory::create({settings["Library/Location"].text(), media.name});
auto location = BrowserDialog() auto location = BrowserDialog()
.setTitle({"Load ", media.name}) .setTitle({"Load ", media.name})
.setPath({config->library.location, media.name}) .setPath({settings["Library/Location"].text(), media.name})
.setFilters(string{media.name, "|*.", media.type}) .setFilters(string{media.name, "|*.", media.type})
.openFolder(); .openFolder();
if(directory::exists(location)) { if(directory::exists(location)) {
@ -31,53 +31,59 @@ Presentation::Presentation() {
settingsMenu.setText("Settings"); settingsMenu.setText("Settings");
videoScaleMenu.setText("Video Scale"); videoScaleMenu.setText("Video Scale");
if(config->video.scale == "Small") videoScaleSmall.setChecked(); if(settings["Video/Scale"].text() == "Small") videoScaleSmall.setChecked();
if(config->video.scale == "Medium") videoScaleMedium.setChecked(); if(settings["Video/Scale"].text() == "Medium") videoScaleMedium.setChecked();
if(config->video.scale == "Large") videoScaleLarge.setChecked(); if(settings["Video/Scale"].text() == "Large") videoScaleLarge.setChecked();
videoScaleSmall.setText("Small").onActivate([&] { videoScaleSmall.setText("Small").onActivate([&] {
config->video.scale = "Small"; settings["Video/Scale"].setValue("Small");
resizeViewport(); resizeViewport();
}); });
videoScaleMedium.setText("Medium").onActivate([&] { videoScaleMedium.setText("Medium").onActivate([&] {
config->video.scale = "Medium"; settings["Video/Scale"].setValue("Medium");
resizeViewport(); resizeViewport();
}); });
videoScaleLarge.setText("Large").onActivate([&] { videoScaleLarge.setText("Large").onActivate([&] {
config->video.scale = "Large"; settings["Video/Scale"].setValue("Large");
resizeViewport(); resizeViewport();
}); });
aspectCorrection.setText("Aspect Correction").setChecked(config->video.aspectCorrection).onToggle([&] { aspectCorrection.setText("Aspect Correction").setChecked(settings["Video/AspectCorrection"].boolean()).onToggle([&] {
config->video.aspectCorrection = aspectCorrection.checked(); settings["Video/AspectCorrection"].setValue(aspectCorrection.checked());
resizeViewport(); resizeViewport();
}); });
videoFilterMenu.setText("Video Filter"); videoFilterMenu.setText("Video Filter");
if(config->video.filter == "None") videoFilterNone.setChecked(); if(settings["Video/Filter"].text() == "None") videoFilterNone.setChecked();
if(config->video.filter == "Blur") videoFilterBlur.setChecked(); if(settings["Video/Filter"].text() == "Blur") videoFilterBlur.setChecked();
videoFilterNone.setText("None").onActivate([&] { config->video.filter = "None"; program->updateVideoFilter(); }); videoFilterNone.setText("None").onActivate([&] {
videoFilterBlur.setText("Blur").onActivate([&] { config->video.filter = "Blur"; program->updateVideoFilter(); }); settings["Video/Filter"].setValue("None");
colorEmulation.setText("Color Emulation").setChecked(config->video.colorEmulation).onToggle([&] { program->updateVideoFilter();
config->video.colorEmulation = colorEmulation.checked(); });
videoFilterBlur.setText("Blur").onActivate([&] {
settings["Video/Filter"].setValue("Blur");
program->updateVideoFilter();
});
colorEmulation.setText("Color Emulation").setChecked(settings["Video/ColorEmulation"].boolean()).onToggle([&] {
settings["Video/ColorEmulation"].setValue(colorEmulation.checked());
program->updateVideoPalette(); program->updateVideoPalette();
}); });
maskOverscan.setText("Mask Overscan").setChecked(config->video.overscan.mask).onToggle([&] { maskOverscan.setText("Mask Overscan").setChecked(settings["Video/Overscan/Mask"].boolean()).onToggle([&] {
config->video.overscan.mask = maskOverscan.checked(); settings["Video/Overscan/Mask"].setValue(maskOverscan.checked());
}); });
loadShaders(); loadShaders();
synchronizeVideo.setText("Synchronize Video").setChecked(config->video.synchronize).onToggle([&] { synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Synchronize"].boolean()).onToggle([&] {
config->video.synchronize = synchronizeVideo.checked(); settings["Video/Synchronize"].setValue(synchronizeVideo.checked());
video->set(Video::Synchronize, config->video.synchronize); video->set(Video::Synchronize, synchronizeVideo.checked());
}); });
synchronizeAudio.setText("Synchronize Audio").setChecked(config->audio.synchronize).onToggle([&] { synchronizeAudio.setText("Synchronize Audio").setChecked(settings["Audio/Synchronize"].boolean()).onToggle([&] {
config->audio.synchronize = synchronizeAudio.checked(); settings["Audio/Synchronize"].setValue(synchronizeAudio.checked());
audio->set(Audio::Synchronize, config->audio.synchronize); audio->set(Audio::Synchronize, synchronizeVideo.checked());
}); });
muteAudio.setText("Mute Audio").setChecked(config->audio.mute).onToggle([&] { muteAudio.setText("Mute Audio").setChecked(settings["Audio/Mute"].boolean()).onToggle([&] {
config->audio.mute = muteAudio.checked(); settings["Audio/Mute"].setValue(muteAudio.checked());
program->dsp.setVolume(config->audio.mute ? 0.0 : 1.0); program->updateAudioVolume();
}); });
showStatusBar.setText("Show Status Bar").setChecked(config->userInterface.showStatusBar).onToggle([&] { showStatusBar.setText("Show Status Bar").setChecked(settings["UserInterface/ShowStatusBar"].boolean()).onToggle([&] {
config->userInterface.showStatusBar = showStatusBar.checked(); settings["UserInterface/ShowStatusBar"].setValue(showStatusBar.checked());
statusBar.setVisible(config->userInterface.showStatusBar); statusBar.setVisible(showStatusBar.checked());
if(visible()) resizeViewport(); if(visible()) resizeViewport();
}); });
showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(2); }); showConfiguration.setText("Configuration ...").onActivate([&] { settingsManager->show(2); });
@ -99,7 +105,7 @@ Presentation::Presentation() {
stateManager.setText("State Manager").onActivate([&] { toolsManager->show(1); }); stateManager.setText("State Manager").onActivate([&] { toolsManager->show(1); });
statusBar.setFont(Font().setBold()); statusBar.setFont(Font().setBold());
statusBar.setVisible(config->userInterface.showStatusBar); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
onClose([&] { program->quit(); }); onClose([&] { program->quit(); });
@ -127,18 +133,18 @@ auto Presentation::updateEmulator() -> void {
for(auto& device : port.device) { for(auto& device : port.device) {
MenuRadioItem item{&menu}; MenuRadioItem item{&menu};
item.setText(device.name).onActivate([=] { item.setText(device.name).onActivate([=] {
emulatorSettings->set({emulator->information.name, "/", port.name}, device.name); auto path = string{emulator->information.name, "/", port.name}.replace(" ", "");
settings[path].setValue(device.name);
emulator->connect(port.id, device.id); emulator->connect(port.id, device.id);
}); });
devices.append(item); devices.append(item);
} }
if(devices.objectCount() > 1) { if(devices.objectCount() > 1) {
string device = emulatorSettings->get({emulator->information.name, "/", port.name}); auto path = string{emulator->information.name, "/", port.name}.replace(" ", "");
auto device = settings(path).text();
for(auto object : devices.objects()) { for(auto object : devices.objects()) {
if(auto item = dynamic_cast<mMenuRadioItem*>(object.data())) { if(auto item = object.cast<MenuRadioItem>()) {
if(item->text() == device) { if(item.text() == device) item.setChecked().doActivate();
item->setChecked().doActivate();
}
} }
} }
menu.setVisible(); menu.setVisible();
@ -154,17 +160,17 @@ auto Presentation::resizeViewport() -> void {
double stretch = emulator ? emulator->information.aspectRatio : 1.0; double stretch = emulator ? emulator->information.aspectRatio : 1.0;
if(stretch != 1.0) { if(stretch != 1.0) {
//aspect correction is always enabled in fullscreen mode //aspect correction is always enabled in fullscreen mode
if(!fullScreen() && !config->video.aspectCorrection) stretch = 1.0; if(!fullScreen() && !settings["Video/AspectCorrection"].boolean()) stretch = 1.0;
} }
signed scale = 2; signed scale = 2;
if(config->video.scale == "Small" ) scale = 2; if(settings["Video/Scale"].text() == "Small" ) scale = 2;
if(config->video.scale == "Medium") scale = 3; if(settings["Video/Scale"].text() == "Medium") scale = 3;
if(config->video.scale == "Large" ) scale = 4; if(settings["Video/Scale"].text() == "Large" ) scale = 4;
signed windowWidth = 0, windowHeight = 0; signed windowWidth = 0, windowHeight = 0;
if(!fullScreen()) { if(!fullScreen()) {
windowWidth = 256 * scale * (config->video.aspectCorrection ? 8.0 / 7.0 : 1.0); windowWidth = 256 * scale * (settings["Video/AspectCorrection"].boolean() ? 8.0 / 7.0 : 1.0);
windowHeight = 240 * scale; windowHeight = 240 * scale;
} else { } else {
windowWidth = geometry().width(); windowWidth = geometry().width();
@ -193,7 +199,7 @@ auto Presentation::toggleFullScreen() -> void {
setFullScreen(false); setFullScreen(false);
setResizable(false); setResizable(false);
menuBar.setVisible(true); menuBar.setVisible(true);
statusBar.setVisible(config->userInterface.showStatusBar); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
} }
Application::processEvents(); Application::processEvents();
@ -215,7 +221,7 @@ auto Presentation::drawSplashScreen() -> void {
} }
auto Presentation::loadShaders() -> void { auto Presentation::loadShaders() -> void {
if(config->video.driver != "OpenGL") { if(settings["Video/Driver"].text() != "OpenGL") {
videoShaderMenu.setVisible(false); videoShaderMenu.setVisible(false);
return; return;
} }
@ -224,7 +230,7 @@ auto Presentation::loadShaders() -> void {
for(auto shader : directory::folders(pathname, "*.shader")) { for(auto shader : directory::folders(pathname, "*.shader")) {
MenuRadioItem item{&videoShaderMenu}; MenuRadioItem item{&videoShaderMenu};
item.setText(string{shader}.rtrim(".shader/", 1L)).onActivate([=] { item.setText(string{shader}.rtrim(".shader/", 1L)).onActivate([=] {
config->video.shader = {pathname, shader}; settings["Video/Shader"].setValue({pathname, shader});
program->updateVideoFilter(); program->updateVideoFilter();
}); });
videoShaders.append(item); videoShaders.append(item);
@ -232,13 +238,13 @@ auto Presentation::loadShaders() -> void {
videoShaderMenu.setText("Video Shaders"); videoShaderMenu.setText("Video Shaders");
videoShaderNone.setChecked().setText("None").onActivate([=] { videoShaderNone.setChecked().setText("None").onActivate([=] {
config->video.shader = "None"; settings["Video/Shader"].setValue("None");
program->updateVideoFilter(); program->updateVideoFilter();
}); });
for(auto object : videoShaders.objects()) { for(auto object : videoShaders.objects()) {
if(auto radioItem = dynamic_cast<mMenuRadioItem*>(object.data())) { if(auto radioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
if(config->video.shader == string{pathname, radioItem->text(), ".shader/"}) { if(settings["Video/Shader"].text() == string{pathname, radioItem->text(), ".shader/"}) {
radioItem->setChecked(); radioItem->setChecked();
} }
} }

View File

@ -2,7 +2,7 @@
auto Program::loadRequest(unsigned id, string name, string type, bool required) -> void { auto Program::loadRequest(unsigned id, string name, string type, bool required) -> void {
string location = BrowserDialog() string location = BrowserDialog()
.setTitle({"Load ", name}) .setTitle({"Load ", name})
.setPath({config->library.location, name}) .setPath({settings["Library/Location"].text(), name})
.setFilters({string{name, "|*.", type}}) .setFilters({string{name, "|*.", type}})
.openFolder(); .openFolder();
if(!directory::exists(location)) return; if(!directory::exists(location)) return;
@ -16,25 +16,30 @@ auto Program::loadRequest(unsigned id, string name, string type, bool required)
auto Program::loadRequest(unsigned id, string filename, bool required) -> void { auto Program::loadRequest(unsigned id, string filename, bool required) -> void {
string pathname = mediaPaths(emulator->group(id)); string pathname = mediaPaths(emulator->group(id));
string location = {pathname, filename}; string location = {pathname, filename};
if(filename == "manifest.bml" && !pathname.find(".sys/")) {
if(!file::exists(location) || settings["Library/IgnoreManifests"].boolean()) {
string manifest;
if(auto fp = popen(string{"icarus -m \"", pathname, "\""}, "r")) {
while(true) {
auto byte = fgetc(fp);
if(byte == EOF) break;
manifest.append((char)byte);
}
pclose(fp);
}
if(manifest) {
memorystream stream{(const uint8*)manifest.data(), manifest.size()};
return emulator->load(id, stream);
}
}
}
if(file::exists(location)) { if(file::exists(location)) {
mmapstream stream{location}; mmapstream stream{location};
return emulator->load(id, stream); return emulator->load(id, stream);
} }
if(filename == "manifest.bml") {
string manifest;
if(auto fp = popen(string{"icarus -m \"", pathname, "\""}, "r")) {
while(true) {
auto byte = fgetc(fp);
if(byte == EOF) break;
manifest.append((char)byte);
}
pclose(fp);
}
if(manifest) {
memorystream stream{manifest.binary(), manifest.size()};
return emulator->load(id, stream);
}
}
if(required) MessageDialog().setTitle("higan").setText({ if(required) MessageDialog().setTitle("higan").setText({
"Missing required file: ", nall::filename(location), "\n\n", "Missing required file: ", nall::filename(location), "\n\n",
"From location:\n", nall::pathname(location) "From location:\n", nall::pathname(location)
@ -50,25 +55,25 @@ auto Program::saveRequest(unsigned id, string filename) -> void {
} }
auto Program::videoColor(unsigned source, uint16 a, uint16 r, uint16 g, uint16 b) -> uint32 { auto Program::videoColor(unsigned source, uint16 a, uint16 r, uint16 g, uint16 b) -> uint32 {
if(config->video.saturation != 100) { if(settings["Video/Saturation"].natural() != 100) {
uint16 grayscale = uclamp<16>((r + g + b) / 3); uint16 grayscale = uclamp<16>((r + g + b) / 3);
double saturation = config->video.saturation * 0.01; double saturation = settings["Video/Saturation"].natural() * 0.01;
double inverse = max(0.0, 1.0 - saturation); double inverse = max(0.0, 1.0 - saturation);
r = uclamp<16>(r * saturation + grayscale * inverse); r = uclamp<16>(r * saturation + grayscale * inverse);
g = uclamp<16>(g * saturation + grayscale * inverse); g = uclamp<16>(g * saturation + grayscale * inverse);
b = uclamp<16>(b * saturation + grayscale * inverse); b = uclamp<16>(b * saturation + grayscale * inverse);
} }
if(config->video.gamma != 100) { if(settings["Video/Gamma"].natural() != 100) {
double exponent = config->video.gamma * 0.01; double exponent = settings["Video/Gamma"].natural() * 0.01;
double reciprocal = 1.0 / 32767.0; double reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent); r = r > 32767 ? r : 32767 * pow(r * reciprocal, exponent);
g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent); g = g > 32767 ? g : 32767 * pow(g * reciprocal, exponent);
b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent); b = b > 32767 ? b : 32767 * pow(b * reciprocal, exponent);
} }
if(config->video.luminance != 100) { if(settings["Video/Luminance"].natural() != 100) {
double luminance = config->video.luminance * 0.01; double luminance = settings["Video/Luminance"].natural() * 0.01;
r = r * luminance; r = r * luminance;
g = g * luminance; g = g * luminance;
b = b * luminance; b = b * luminance;
@ -96,9 +101,9 @@ auto Program::videoRefresh(const uint32* palette, const uint32* data, unsigned p
} }
} }
if(emulator->information.overscan && config->video.overscan.mask) { if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
unsigned h = config->video.overscan.horizontal; auto h = settings["Video/Overscan/Horizontal"].natural();
unsigned v = config->video.overscan.vertical; auto v = settings["Video/Overscan/Vertical"].natural();
if(h) for(auto y : range(height)) { if(h) for(auto y : range(height)) {
memory::fill(output + y * length, 4 * h); memory::fill(output + y * length, 4 * h);

View File

@ -16,7 +16,7 @@ auto Program::loadMedia(string location) -> void {
auto Program::loadMedia(Emulator::Interface& emulator_, Emulator::Interface::Media& media, const string& location) -> void { auto Program::loadMedia(Emulator::Interface& emulator_, Emulator::Interface::Media& media, const string& location) -> void {
unloadMedia(); unloadMedia();
mediaPaths(0) = locate({config->library.location, "System/"}, {media.name, ".sys/"}); mediaPaths(0) = locate({settings["Library/Location"].text(), "System/"}, {media.name, ".sys/"});
mediaPaths(media.id) = location; mediaPaths(media.id) = location;
folderPaths.append(location); folderPaths.append(location);

View File

@ -21,8 +21,6 @@ Program::Program(lstring args) {
emulators.append(new GameBoyAdvance::Interface); emulators.append(new GameBoyAdvance::Interface);
for(auto& emulator : emulators) emulator->bind = this; for(auto& emulator : emulators) emulator->bind = this;
new ConfigurationManager;
new EmulatorSettings;
new InputManager; new InputManager;
new SettingsManager; new SettingsManager;
new CheatDatabase; new CheatDatabase;
@ -31,18 +29,18 @@ Program::Program(lstring args) {
presentation->setVisible(); presentation->setVisible();
video = Video::create(config->video.driver); video = Video::create(settings["Video/Driver"].text());
video->set(Video::Handle, presentation->viewport.handle()); video->set(Video::Handle, presentation->viewport.handle());
video->set(Video::Synchronize, config->video.synchronize); video->set(Video::Synchronize, settings["Video/Synchronize"].boolean());
if(!video->init()) { if(!video->init()) {
delete video; delete video;
video = Video::create("None"); video = Video::create("None");
} }
audio = Audio::create(config->audio.driver); audio = Audio::create(settings["Audio/Driver"].text());
audio->set(Audio::Device, config->audio.device); audio->set(Audio::Device, settings["Audio/Device"].text());
audio->set(Audio::Handle, presentation->viewport.handle()); audio->set(Audio::Handle, presentation->viewport.handle());
audio->set(Audio::Synchronize, config->audio.synchronize); audio->set(Audio::Synchronize, settings["Audio/Synchronize"].boolean());
audio->set(Audio::Frequency, 96000u); audio->set(Audio::Frequency, 96000u);
audio->set(Audio::Latency, 80u); audio->set(Audio::Latency, 80u);
if(!audio->init()) { if(!audio->init()) {
@ -50,7 +48,7 @@ Program::Program(lstring args) {
audio = Audio::create("None"); audio = Audio::create("None");
} }
input = Input::create(config->input.driver); input = Input::create(settings["Input/Driver"].text());
input->set(Input::Handle, presentation->viewport.handle()); input->set(Input::Handle, presentation->viewport.handle());
input->onChange({&InputManager::onChange, inputManager}); input->onChange({&InputManager::onChange, inputManager});
if(!input->init()) { if(!input->init()) {
@ -60,7 +58,6 @@ Program::Program(lstring args) {
dsp.setPrecision(16); dsp.setPrecision(16);
dsp.setBalance(0.0); dsp.setBalance(0.0);
dsp.setVolume(config->audio.mute ? 0.0 : 1.0);
dsp.setFrequency(32040); dsp.setFrequency(32040);
dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResampler(DSP::ResampleEngine::Sinc);
dsp.setResamplerFrequency(96000); dsp.setResamplerFrequency(96000);
@ -68,6 +65,7 @@ Program::Program(lstring args) {
presentation->drawSplashScreen(); presentation->drawSplashScreen();
updateVideoFilter(); updateVideoFilter();
updateAudioVolume();
args.takeFirst(); //ignore program location in argument parsing args.takeFirst(); //ignore program location in argument parsing
for(auto& argument : args) { for(auto& argument : args) {
@ -96,8 +94,7 @@ auto Program::main() -> void {
auto Program::quit() -> void { auto Program::quit() -> void {
unloadMedia(); unloadMedia();
config->quit(); settings.quit();
emulatorSettings->quit();
inputManager->quit(); inputManager->quit();
delete video; delete video;
delete audio; delete audio;

View File

@ -35,6 +35,7 @@ struct Program : Emulator::Interface::Bind {
auto updateVideoFilter() -> void; auto updateVideoFilter() -> void;
auto updateVideoPalette() -> void; auto updateVideoPalette() -> void;
auto updateAudio() -> void; auto updateAudio() -> void;
auto updateAudioVolume() -> void;
auto updateDSP() -> void; auto updateDSP() -> void;
DSP dsp; DSP dsp;

View File

@ -36,18 +36,21 @@ auto Program::updateStatusText() -> void {
} }
auto Program::updateVideoFilter() -> void { auto Program::updateVideoFilter() -> void {
if(config->video.driver == "OpenGL" && config->video.shader != "None" && directory::exists(config->video.shader)) { if(settings["Video/Driver"].text() == "OpenGL"
&& settings["Video/Shader"].text() != "None"
&& directory::exists(settings["Video/Shader"].text())
) {
video->set(Video::Filter, Video::FilterNearest); video->set(Video::Filter, Video::FilterNearest);
video->set(Video::Shader, (string)config->video.shader); video->set(Video::Shader, settings["Video/Shader"].text());
} else { } else {
video->set(Video::Filter, config->video.filter == "Blur" ? Video::FilterLinear : Video::FilterNearest); video->set(Video::Filter, settings["Video/Filter"].text() == "Blur" ? Video::FilterLinear : Video::FilterNearest);
video->set(Video::Shader, (string)""); video->set(Video::Shader, (string)"");
} }
} }
auto Program::updateVideoPalette() -> void { auto Program::updateVideoPalette() -> void {
if(!emulator) return; if(!emulator) return;
emulator->paletteUpdate(config->video.colorEmulation emulator->paletteUpdate(settings["Video/ColorEmulation"].boolean()
? Emulator::Interface::PaletteMode::Emulation ? Emulator::Interface::PaletteMode::Emulation
: Emulator::Interface::PaletteMode::Standard : Emulator::Interface::PaletteMode::Standard
); );
@ -56,23 +59,25 @@ auto Program::updateVideoPalette() -> void {
auto Program::updateAudio() -> void { auto Program::updateAudio() -> void {
if(!audio) return; if(!audio) return;
audio->clear(); audio->clear();
audio->set(Audio::Frequency, config->audio.frequency); audio->set(Audio::Frequency, settings["Audio/Frequency"].natural());
audio->set(Audio::Latency, config->audio.latency); audio->set(Audio::Latency, settings["Audio/Latency"].natural());
if(auto resampler = config->audio.resampler) { if(settings["Audio/Resampler"].text() == "Linear" ) dsp.setResampler(DSP::ResampleEngine::Linear);
if(resampler == "Linear" ) dsp.setResampler(DSP::ResampleEngine::Linear); if(settings["Audio/Resampler"].text() == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite);
if(resampler == "Hermite") dsp.setResampler(DSP::ResampleEngine::Hermite); if(settings["Audio/Resampler"].text() == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc);
if(resampler == "Sinc" ) dsp.setResampler(DSP::ResampleEngine::Sinc); dsp.setResamplerFrequency(settings["Audio/Frequency"].natural());
} updateAudioVolume();
dsp.setResamplerFrequency(config->audio.frequency);
dsp.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01);
updateDSP(); updateDSP();
} }
auto Program::updateAudioVolume() -> void {
dsp.setVolume(settings["Audio/Mute"].boolean() ? 0.0 : settings["Audio/Volume"].natural() * 0.01);
}
auto Program::updateDSP() -> void { auto Program::updateDSP() -> void {
if(!emulator) return; if(!emulator) return;
if(!config->video.synchronize) return dsp.setFrequency(emulator->audioFrequency()); if(!settings["Video/Synchronize"].boolean()) return dsp.setFrequency(emulator->audioFrequency());
double inputRatio = emulator->audioFrequency() / emulator->videoFrequency(); double inputRatio = emulator->audioFrequency() / emulator->videoFrequency();
double outputRatio = config->timing.audio / config->timing.video; double outputRatio = settings["Timing/Audio"].real() / settings["Timing/Video"].real();
dsp.setFrequency(inputRatio / outputRatio * config->audio.frequency); dsp.setFrequency(inputRatio / outputRatio * settings["Audio/Frequency"].natural());
} }

View File

@ -6,36 +6,41 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
driverLabel.setText("Driver Selection").setFont(Font().setBold()); driverLabel.setText("Driver Selection").setFont(Font().setBold());
videoLabel.setText("Video:"); videoLabel.setText("Video:");
videoDriver.onChange([&] { config->video.driver = videoDriver.selected()->text(); }); videoDriver.onChange([&] { settings["Video/Driver"].setValue(videoDriver.selected().text()); });
for(auto& driver : Video::availableDrivers()) { for(auto& driver : Video::availableDrivers()) {
ComboButtonItem item; ComboButtonItem item;
item.setText(driver); item.setText(driver);
videoDriver.append(item); videoDriver.append(item);
if(config->video.driver == driver) item.setSelected(); if(settings["Video/Driver"].text() == driver) item.setSelected();
} }
audioLabel.setText("Audio:"); audioLabel.setText("Audio:");
audioDriver.onChange([&] { config->audio.driver = audioDriver.selected()->text(); }); audioDriver.onChange([&] { settings["Audio/Driver"].setValue(audioDriver.selected().text()); });
for(auto& driver : Audio::availableDrivers()) { for(auto& driver : Audio::availableDrivers()) {
ComboButtonItem item; ComboButtonItem item;
item.setText(driver); item.setText(driver);
audioDriver.append(item); audioDriver.append(item);
if(config->audio.driver == driver) item.setSelected(); if(settings["Audio/Driver"].text() == driver) item.setSelected();
} }
inputLabel.setText("Input:"); inputLabel.setText("Input:");
inputDriver.onChange([&] { config->input.driver = inputDriver.selected()->text(); }); inputDriver.onChange([&] { settings["Input/Driver"].setValue(inputDriver.selected().text()); });
for(auto& driver : Input::availableDrivers()) { for(auto& driver : Input::availableDrivers()) {
ComboButtonItem item; ComboButtonItem item;
item.setText(driver); item.setText(driver);
inputDriver.append(item); inputDriver.append(item);
if(config->input.driver == driver) item.setSelected(); if(settings["Input/Driver"].text() == driver) item.setSelected();
} }
libraryLabel.setText("Game Library").setFont(Font().setBold()); libraryLabel.setText("Game Library").setFont(Font().setBold());
libraryPrefix.setText("Location:"); libraryPrefix.setText("Location:");
libraryLocation.setEditable(false).setText(config->library.location); libraryLocation.setEditable(false).setText(settings["Library/Location"].text());
libraryChange.setText("Change ...").onActivate([&] { libraryChange.setText("Change ...").onActivate([&] {
if(auto location = BrowserDialog().setTitle("Select Library Location").selectFolder()) { if(auto location = BrowserDialog().setTitle("Select Library Location").selectFolder()) {
libraryLocation.setText(config->library.location = location); settings["Library/Location"].setValue(location);
libraryLocation.setText(location);
} }
}); });
ignoreManifests.setText("Ignore Manifests").setChecked(settings["Library/IgnoreManifests"].boolean()).onToggle([&] {
settings["Library/IgnoreManifests"].setValue(ignoreManifests.checked());
});
} }

View File

@ -9,7 +9,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
frequencyCombo.append(ComboButtonItem().setText("44100hz")); frequencyCombo.append(ComboButtonItem().setText("44100hz"));
frequencyCombo.append(ComboButtonItem().setText("48000hz")); frequencyCombo.append(ComboButtonItem().setText("48000hz"));
frequencyCombo.append(ComboButtonItem().setText("96000hz")); frequencyCombo.append(ComboButtonItem().setText("96000hz"));
switch(config->audio.frequency) { switch(settings["Audio/Frequency"].natural()) {
case 32000: frequencyCombo.item(0)->setSelected(); break; case 32000: frequencyCombo.item(0)->setSelected(); break;
case 44100: frequencyCombo.item(1)->setSelected(); break; case 44100: frequencyCombo.item(1)->setSelected(); break;
case 48000: frequencyCombo.item(2)->setSelected(); break; case 48000: frequencyCombo.item(2)->setSelected(); break;
@ -23,7 +23,7 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
latencyCombo.append(ComboButtonItem().setText("60ms")); latencyCombo.append(ComboButtonItem().setText("60ms"));
latencyCombo.append(ComboButtonItem().setText("80ms")); latencyCombo.append(ComboButtonItem().setText("80ms"));
latencyCombo.append(ComboButtonItem().setText("100ms")); latencyCombo.append(ComboButtonItem().setText("100ms"));
switch(config->audio.latency) { switch(settings["Audio/Latency"].natural()) {
case 20: latencyCombo.item(0)->setSelected(); break; case 20: latencyCombo.item(0)->setSelected(); break;
case 40: latencyCombo.item(1)->setSelected(); break; case 40: latencyCombo.item(1)->setSelected(); break;
case 60: latencyCombo.item(2)->setSelected(); break; case 60: latencyCombo.item(2)->setSelected(); break;
@ -36,42 +36,48 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
resamplerCombo.append(ComboButtonItem().setText("Linear")); resamplerCombo.append(ComboButtonItem().setText("Linear"));
resamplerCombo.append(ComboButtonItem().setText("Hermite")); resamplerCombo.append(ComboButtonItem().setText("Hermite"));
resamplerCombo.append(ComboButtonItem().setText("Sinc")); resamplerCombo.append(ComboButtonItem().setText("Sinc"));
if(config->audio.resampler == "Linear" ) resamplerCombo.item(0)->setSelected(); if(settings["Audio/Resampler"].text() == "Linear" ) resamplerCombo.item(0)->setSelected();
if(config->audio.resampler == "Hermite") resamplerCombo.item(1)->setSelected(); if(settings["Audio/Resampler"].text() == "Hermite") resamplerCombo.item(1)->setSelected();
if(config->audio.resampler == "Sinc" ) resamplerCombo.item(2)->setSelected(); if(settings["Audio/Resampler"].text() == "Sinc" ) resamplerCombo.item(2)->setSelected();
resamplerCombo.onChange([&] { update(); }); resamplerCombo.onChange([&] { update(); });
volumeLabel.setText("Volume:"); volumeLabel.setText("Volume:");
volumeSlider.setLength(201).setPosition(config->audio.volume).onChange([&] { updateVolume(); }); volumeSlider.setLength(201).setPosition(settings["Audio/Volume"].natural()).onChange([&] { updateVolume(); });
update(); update();
} }
auto AudioSettings::update() -> void { auto AudioSettings::update() -> void {
if(auto item = frequencyCombo.selected()) { if(auto item = frequencyCombo.selected()) {
if(item->offset() == 0) config->audio.frequency = 32000; uint frequency = 48000;
if(item->offset() == 1) config->audio.frequency = 44100; if(item->offset() == 0) frequency = 32000;
if(item->offset() == 2) config->audio.frequency = 48000; if(item->offset() == 1) frequency = 44100;
if(item->offset() == 3) config->audio.frequency = 96000; if(item->offset() == 2) frequency = 48000;
if(item->offset() == 3) frequency = 96000;
settings["Audio/Frequency"].setValue(frequency);
} }
if(auto item = latencyCombo.selected()) { if(auto item = latencyCombo.selected()) {
if(item->offset() == 0) config->audio.latency = 20; uint latency = 60;
if(item->offset() == 1) config->audio.latency = 40; if(item->offset() == 0) latency = 20;
if(item->offset() == 2) config->audio.latency = 60; if(item->offset() == 1) latency = 40;
if(item->offset() == 3) config->audio.latency = 80; if(item->offset() == 2) latency = 60;
if(item->offset() == 4) config->audio.latency = 100; if(item->offset() == 3) latency = 80;
if(item->offset() == 4) latency = 100;
settings["Audio/Latency"].setValue(latency);
} }
if(auto item = resamplerCombo.selected()) { if(auto item = resamplerCombo.selected()) {
if(item->offset() == 0) config->audio.resampler = "Linear"; string resampler = "Sinc";
if(item->offset() == 1) config->audio.resampler = "Hermite"; if(item->offset() == 0) resampler = "Linear";
if(item->offset() == 2) config->audio.resampler = "Sinc"; if(item->offset() == 1) resampler = "Hermite";
if(item->offset() == 2) resampler = "Sinc";
settings["Audio/Resampler"].setValue(resampler);
} }
updateVolume(); updateVolume();
program->updateAudio(); program->updateAudio();
} }
auto AudioSettings::updateVolume() -> void { auto AudioSettings::updateVolume() -> void {
config->audio.volume = volumeSlider.position(); settings["Audio/Volume"].setValue(volumeSlider.position());
volumeValue.setText({config->audio.volume, "%"}); volumeValue.setText({volumeSlider.position(), "%"});
program->dsp.setVolume(config->audio.mute ? 0.0 : config->audio.volume * 0.01); program->updateAudioVolume();
} }

View File

@ -129,6 +129,7 @@ struct AdvancedSettings : TabFrameItem {
Label libraryPrefix{&libraryLayout, Size{0, 0}}; Label libraryPrefix{&libraryLayout, Size{0, 0}};
LineEdit libraryLocation{&libraryLayout, Size{~0, 0}}; LineEdit libraryLocation{&libraryLayout, Size{~0, 0}};
Button libraryChange{&libraryLayout, Size{0, 0}}; Button libraryChange{&libraryLayout, Size{0, 0}};
CheckLabel ignoreManifests{&layout, Size{~0, 0}};
}; };
struct SettingsManager : Window { struct SettingsManager : Window {

View File

@ -4,15 +4,15 @@ TimingSettings::TimingSettings(TabFrame* parent) : TabFrameItem(parent) {
layout.setMargin(5); layout.setMargin(5);
videoLabel.setText("Video:"); videoLabel.setText("Video:");
videoValue.setText(config->timing.video).onActivate([&] { update(); }); videoValue.setText(settings["Timing/Video"].real()).onActivate([&] { update(); });
videoAssign.setText("Assign").onActivate([&] { update(); }); videoAssign.setText("Assign").onActivate([&] { update(); });
audioLabel.setText("Audio:"); audioLabel.setText("Audio:");
audioValue.setText(config->timing.audio).onActivate([&] { update(); }); audioValue.setText(settings["Timing/Audio"].real()).onActivate([&] { update(); });
audioAssign.setText("Assign").onActivate([&] { update(); }); audioAssign.setText("Assign").onActivate([&] { update(); });
} }
auto TimingSettings::update() -> void { auto TimingSettings::update() -> void {
config->timing.video = real(videoValue.text()); settings["Timing/Video"].setValue(videoValue.text().real());
config->timing.audio = real(audioValue.text()); settings["Timing/Audio"].setValue(audioValue.text().real());
program->updateDSP(); program->updateDSP();
} }

View File

@ -6,31 +6,31 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment"); colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment");
saturationLabel.setText("Saturation:"); saturationLabel.setText("Saturation:");
saturationSlider.setLength(201).setPosition(config->video.saturation).onChange([&] { update(); }); saturationSlider.setLength(201).setPosition(settings["Video/Saturation"].natural()).onChange([&] { update(); });
gammaLabel.setText("Gamma:"); gammaLabel.setText("Gamma:");
gammaSlider.setLength(101).setPosition(config->video.gamma - 100).onChange([&] { update(); }); gammaSlider.setLength(101).setPosition(settings["Video/Gamma"].natural() - 100).onChange([&] { update(); });
luminanceLabel.setText("Luminance:"); luminanceLabel.setText("Luminance:");
luminanceSlider.setLength(101).setPosition(config->video.luminance).onChange([&] { update(); }); luminanceSlider.setLength(101).setPosition(settings["Video/Luminance"].natural()).onChange([&] { update(); });
overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask"); overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask");
horizontalMaskLabel.setText("Horizontal:"); horizontalMaskLabel.setText("Horizontal:");
horizontalMaskSlider.setLength(17).setPosition(config->video.overscan.horizontal).onChange([&] { update(); }); horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { update(); });
verticalMaskLabel.setText("Vertical:"); verticalMaskLabel.setText("Vertical:");
verticalMaskSlider.setLength(17).setPosition(config->video.overscan.vertical).onChange([&] { update(); }); verticalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { update(); });
update(); update();
} }
auto VideoSettings::update() -> void { auto VideoSettings::update() -> void {
config->video.saturation = saturationSlider.position(); settings["Video/Saturation"].setValue(saturationSlider.position());
config->video.gamma = 100 + gammaSlider.position(); settings["Video/Gamma"].setValue(100 + gammaSlider.position());
config->video.luminance = luminanceSlider.position(); settings["Video/Luminance"].setValue(luminanceSlider.position());
config->video.overscan.horizontal = horizontalMaskSlider.position(); settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position());
config->video.overscan.vertical = verticalMaskSlider.position(); settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position());
saturationValue.setText({config->video.saturation, "%"}); saturationValue.setText({saturationSlider.position(), "%"});
gammaValue.setText({config->video.gamma, "%"}); gammaValue.setText({100 + gammaSlider.position(), "%"});
luminanceValue.setText({config->video.luminance, "%"}); luminanceValue.setText({luminanceSlider.position(), "%"});
horizontalMaskValue.setText({config->video.overscan.horizontal, "px"}); horizontalMaskValue.setText({horizontalMaskSlider.position(), "px"});
verticalMaskValue.setText({config->video.overscan.vertical, "px"}); verticalMaskValue.setText({verticalMaskSlider.position(), "px"});
program->updateVideoPalette(); program->updateVideoPalette();
} }