mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-21 13:31:46 +02:00
Compare commits
75 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ba081d309e | ||
|
1bf9265b7c | ||
|
f947d84309 | ||
|
0bd21185b8 | ||
|
6227974bf6 | ||
|
ea95eaca3c | ||
|
ad0805b168 | ||
|
2cc077e12b | ||
|
ae6c3c377d | ||
|
01750e9c83 | ||
|
891f1ab7af | ||
|
bf78e66027 | ||
|
483f9f8f20 | ||
|
f3feaa3e86 | ||
|
aaffd000a4 | ||
|
118a393c4c | ||
|
6b708de893 | ||
|
db5e2107b4 | ||
|
13ac6104e3 | ||
|
7fa8ad755d | ||
|
ef85f7ccb0 | ||
|
b8d607d16b | ||
|
4c47cc203f | ||
|
4cbaf4e4ec | ||
|
21f9fe4cd5 | ||
|
b629a46779 | ||
|
ba2e6b5789 | ||
|
7115047d85 | ||
|
e8b1af0917 | ||
|
1d4f778176 | ||
|
875ed46d79 | ||
|
046e478d86 | ||
|
82a17ac0f5 | ||
|
979aa640af | ||
|
98ec338285 | ||
|
5b4dcbfdfe | ||
|
101c9507b1 | ||
|
69ed35db99 | ||
|
5c2d16828c | ||
|
382ae1e61e | ||
|
7619805266 | ||
|
e3c7bbfb63 | ||
|
5f099b8ad0 | ||
|
cb3460a673 | ||
|
278cf8462c | ||
|
7f4381b505 | ||
|
c668d10ac7 | ||
|
7fc78dae07 | ||
|
4ca051a22f | ||
|
8618334356 | ||
|
ec7e4087fb | ||
|
496708cffe | ||
|
a86c5ee59d | ||
|
d8f9204e18 | ||
|
e8775319c8 | ||
|
095181af62 | ||
|
b28c54770c | ||
|
71763f2d98 | ||
|
423d9ba00d | ||
|
064ca4c626 | ||
|
10906d8418 | ||
|
e88ab60663 | ||
|
564e38ea9f | ||
|
0c3f0834ab | ||
|
f38af85e0a | ||
|
8276700381 | ||
|
ec69109c0b | ||
|
8ae6444af7 | ||
|
5fc86eae6d | ||
|
927c97eb06 | ||
|
cf09d41669 | ||
|
724747ac9e | ||
|
e1e275eb38 | ||
|
e30fcade43 | ||
|
42dbf73d18 |
@@ -1,41 +1,46 @@
|
|||||||
include nall/Makefile
|
include nall/Makefile
|
||||||
|
|
||||||
|
nes := nes
|
||||||
snes := snes
|
snes := snes
|
||||||
gameboy := gameboy
|
gameboy := gameboy
|
||||||
profile := accuracy
|
profile := accuracy
|
||||||
ui := ui
|
ui := ui
|
||||||
|
|
||||||
# debugger
|
# options += console
|
||||||
options :=
|
# options += debugger
|
||||||
|
|
||||||
# compiler
|
# compiler
|
||||||
c := $(compiler) -std=gnu99
|
c := $(compiler) -std=gnu99
|
||||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||||
flags := -O3 -fomit-frame-pointer -I.
|
flags := -I. -O3 -fomit-frame-pointer
|
||||||
link :=
|
link :=
|
||||||
objects := libco
|
objects := libco
|
||||||
|
|
||||||
# profile-guided instrumentation
|
# profile-guided optimization mode
|
||||||
# flags += -fprofile-generate
|
# pgo := instrument
|
||||||
# link += -lgcov
|
# pgo := optimize
|
||||||
|
|
||||||
# profile-guided optimization
|
ifeq ($(pgo),instrument)
|
||||||
# flags += -fprofile-use
|
flags += -fprofile-generate
|
||||||
|
link += -lgcov
|
||||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
else ifeq ($(pgo),optimize)
|
||||||
|
flags += -fprofile-use
|
||||||
|
endif
|
||||||
|
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),x)
|
ifeq ($(platform),x)
|
||||||
link += -s -ldl -lX11 -lXext
|
link += -s -ldl -lX11 -lXext
|
||||||
else ifeq ($(platform),osx)
|
else ifeq ($(platform),osx)
|
||||||
else ifeq ($(platform),win)
|
else ifeq ($(platform),win)
|
||||||
link += -mwindows
|
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
||||||
# link += -mconsole
|
|
||||||
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||||
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||||
else
|
else
|
||||||
unknown_platform: help;
|
unknown_platform: help;
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||||
|
|
||||||
# implicit rules
|
# implicit rules
|
||||||
compile = \
|
compile = \
|
||||||
$(strip \
|
$(strip \
|
||||||
@@ -69,7 +74,23 @@ clean:
|
|||||||
-@$(call delete,*.pdb)
|
-@$(call delete,*.pdb)
|
||||||
-@$(call delete,*.manifest)
|
-@$(call delete,*.manifest)
|
||||||
|
|
||||||
|
sync:
|
||||||
|
if [ -d ./libco ]; then rm -r ./libco; fi
|
||||||
|
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||||
|
if [ -d ./ruby ]; then rm -r ./ruby; fi
|
||||||
|
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||||
|
cp -r ../libco ./libco
|
||||||
|
cp -r ../nall ./nall
|
||||||
|
cp -r ../ruby ./ruby
|
||||||
|
cp -r ../phoenix ./phoenix
|
||||||
|
rm -r libco/doc
|
||||||
|
rm -r libco/test
|
||||||
|
rm -r nall/test
|
||||||
|
rm -r ruby/_test
|
||||||
|
rm -r phoenix/nall
|
||||||
|
rm -r phoenix/test
|
||||||
|
|
||||||
archive-all:
|
archive-all:
|
||||||
tar -cjf bsnes.tar.bz2 data gameboy libco nall obj out phoenix ruby snes ui ui-gameboy ui-libsnes Makefile cc.bat clean.bat sync.sh
|
tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes Makefile cc.bat clean.bat
|
||||||
|
|
||||||
help:;
|
help:;
|
||||||
|
170505
bsnes/data/cheats.xml
170505
bsnes/data/cheats.xml
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
|||||||
gameboy_objects := gameboy-system gameboy-scheduler
|
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
|
||||||
gameboy_objects += gameboy-memory gameboy-cartridge
|
gameboy_objects += gameboy-memory gameboy-cartridge
|
||||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
||||||
|
gameboy_objects += gameboy-cheat gameboy-video
|
||||||
objects += $(gameboy_objects)
|
objects += $(gameboy_objects)
|
||||||
|
|
||||||
|
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
|
||||||
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
||||||
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
||||||
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||||
@@ -10,3 +12,5 @@ obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/m
|
|||||||
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
||||||
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
|
obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/)
|
||||||
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
|
obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
|
||||||
|
obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/)
|
||||||
|
obj/gameboy-video.o: $(gameboy)/video/video.cpp $(call rwildcard,$(gameboy)/video/)
|
||||||
|
@@ -46,16 +46,18 @@ void APU::main() {
|
|||||||
noise.run();
|
noise.run();
|
||||||
master.run();
|
master.run();
|
||||||
|
|
||||||
system.interface->audio_sample(master.center, master.left, master.right);
|
interface->audioSample(master.center, master.left, master.right);
|
||||||
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
|
||||||
|
clock += 1 * cpu.frequency;
|
||||||
|
if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::power() {
|
void APU::power() {
|
||||||
create(Main, 4194304);
|
create(Main, 4 * 1024 * 1024);
|
||||||
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||||
|
|
||||||
foreach(n, mmio_data) n = 0x00;
|
for(auto &n : mmio_data) n = 0x00;
|
||||||
sequencer_base = 0;
|
sequencer_base = 0;
|
||||||
sequencer_step = 0;
|
sequencer_step = 0;
|
||||||
|
|
||||||
@@ -80,10 +82,10 @@ uint8 APU::mmio_read(uint16 addr) {
|
|||||||
|
|
||||||
if(addr == 0xff26) {
|
if(addr == 0xff26) {
|
||||||
uint8 data = master.enable << 7;
|
uint8 data = master.enable << 7;
|
||||||
if(square1.counter && square1.length) data |= 0x01;
|
if(square1.enable) data |= 0x01;
|
||||||
if(square2.counter && square2.length) data |= 0x02;
|
if(square2.enable) data |= 0x02;
|
||||||
if( wave.counter && wave.length) data |= 0x04;
|
if( wave.enable) data |= 0x04;
|
||||||
if( noise.counter && noise.length) data |= 0x08;
|
if( noise.enable) data |= 0x08;
|
||||||
return data | table[addr - 0xff10];
|
return data | table[addr - 0xff10];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,28 +8,19 @@ void APU::Master::run() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
signed sample = 0, channels;
|
signed sample = 0;
|
||||||
sample += apu.square1.output;
|
sample += apu.square1.output;
|
||||||
sample += apu.square2.output;
|
sample += apu.square2.output;
|
||||||
sample += apu.wave.output;
|
sample += apu.wave.output;
|
||||||
sample += apu.noise.output;
|
sample += apu.noise.output;
|
||||||
sample >>= 2;
|
center = (sample * 512) - 16384;
|
||||||
center = sclamp<16>(sample);
|
|
||||||
|
|
||||||
if(left_enable == false && right_enable == false) {
|
|
||||||
left = center;
|
|
||||||
right = center;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample = 0;
|
sample = 0;
|
||||||
channels = 0;
|
if(channel1_left_enable) sample += apu.square1.output;
|
||||||
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
|
if(channel2_left_enable) sample += apu.square2.output;
|
||||||
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
|
if(channel3_left_enable) sample += apu.wave.output;
|
||||||
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
|
if(channel4_left_enable) sample += apu.noise.output;
|
||||||
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
|
left = (sample * 512) - 16384;
|
||||||
if(channels) sample /= channels;
|
|
||||||
left = sclamp<16>(sample);
|
|
||||||
|
|
||||||
switch(left_volume) {
|
switch(left_volume) {
|
||||||
case 0: left >>= 3; break; // 12.5%
|
case 0: left >>= 3; break; // 12.5%
|
||||||
@@ -41,16 +32,13 @@ void APU::Master::run() {
|
|||||||
case 6: left -= (left >> 3); break; // 87.5%
|
case 6: left -= (left >> 3); break; // 87.5%
|
||||||
//case 7: break; //100.0%
|
//case 7: break; //100.0%
|
||||||
}
|
}
|
||||||
if(left_enable == false) left = 0;
|
|
||||||
|
|
||||||
sample = 0;
|
sample = 0;
|
||||||
channels = 0;
|
if(channel1_right_enable) sample += apu.square1.output;
|
||||||
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
|
if(channel2_right_enable) sample += apu.square2.output;
|
||||||
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
|
if(channel3_right_enable) sample += apu.wave.output;
|
||||||
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
|
if(channel4_right_enable) sample += apu.noise.output;
|
||||||
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
|
right = (sample * 512) - 16384;
|
||||||
if(channels) sample /= channels;
|
|
||||||
right = sclamp<16>(sample);
|
|
||||||
|
|
||||||
switch(right_volume) {
|
switch(right_volume) {
|
||||||
case 0: right >>= 3; break; // 12.5%
|
case 0: right >>= 3; break; // 12.5%
|
||||||
@@ -62,18 +50,17 @@ void APU::Master::run() {
|
|||||||
case 6: right -= (right >> 3); break; // 87.5%
|
case 6: right -= (right >> 3); break; // 87.5%
|
||||||
//case 7: break; //100.0%
|
//case 7: break; //100.0%
|
||||||
}
|
}
|
||||||
if(right_enable == false) right = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Master::write(unsigned r, uint8 data) {
|
void APU::Master::write(unsigned r, uint8 data) {
|
||||||
if(r == 0) {
|
if(r == 0) { //$ff24 NR50
|
||||||
left_enable = data & 0x80;
|
left_in_enable = data & 0x80;
|
||||||
left_volume = (data >> 4) & 7;
|
left_volume = (data >> 4) & 7;
|
||||||
right_enable = data & 0x08;
|
right_in_enable = data & 0x08;
|
||||||
right_volume = (data >> 0) & 7;
|
right_volume = (data >> 0) & 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 1) {
|
if(r == 1) { //$ff25 NR51
|
||||||
channel4_left_enable = data & 0x80;
|
channel4_left_enable = data & 0x80;
|
||||||
channel3_left_enable = data & 0x40;
|
channel3_left_enable = data & 0x40;
|
||||||
channel2_left_enable = data & 0x20;
|
channel2_left_enable = data & 0x20;
|
||||||
@@ -84,15 +71,15 @@ void APU::Master::write(unsigned r, uint8 data) {
|
|||||||
channel1_right_enable = data & 0x01;
|
channel1_right_enable = data & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 2) {
|
if(r == 2) { //$ff26 NR52
|
||||||
enable = data & 0x80;
|
enable = data & 0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Master::power() {
|
void APU::Master::power() {
|
||||||
left_enable = 0;
|
left_in_enable = 0;
|
||||||
left_volume = 0;
|
left_volume = 0;
|
||||||
right_enable = 0;
|
right_in_enable = 0;
|
||||||
right_volume = 0;
|
right_volume = 0;
|
||||||
channel4_left_enable = 0;
|
channel4_left_enable = 0;
|
||||||
channel3_left_enable = 0;
|
channel3_left_enable = 0;
|
||||||
@@ -110,9 +97,9 @@ void APU::Master::power() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Master::serialize(serializer &s) {
|
void APU::Master::serialize(serializer &s) {
|
||||||
s.integer(left_enable);
|
s.integer(left_in_enable);
|
||||||
s.integer(left_volume);
|
s.integer(left_volume);
|
||||||
s.integer(right_enable);
|
s.integer(right_in_enable);
|
||||||
s.integer(right_volume);
|
s.integer(right_volume);
|
||||||
s.integer(channel4_left_enable);
|
s.integer(channel4_left_enable);
|
||||||
s.integer(channel3_left_enable);
|
s.integer(channel3_left_enable);
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
struct Master {
|
struct Master {
|
||||||
bool left_enable;
|
bool left_in_enable;
|
||||||
unsigned left_volume;
|
uint3 left_volume;
|
||||||
bool right_enable;
|
bool right_in_enable;
|
||||||
unsigned right_volume;
|
uint3 right_volume;
|
||||||
bool channel4_left_enable;
|
bool channel4_left_enable;
|
||||||
bool channel3_left_enable;
|
bool channel3_left_enable;
|
||||||
bool channel2_left_enable;
|
bool channel2_left_enable;
|
||||||
|
@@ -1,27 +1,32 @@
|
|||||||
#ifdef APU_CPP
|
#ifdef APU_CPP
|
||||||
|
|
||||||
|
bool APU::Noise::dac_enable() {
|
||||||
|
return (envelope_volume || envelope_direction);
|
||||||
|
}
|
||||||
|
|
||||||
void APU::Noise::run() {
|
void APU::Noise::run() {
|
||||||
if(period && --period == 0) {
|
if(period && --period == 0) {
|
||||||
period = divisor << frequency;
|
period = divisor << frequency;
|
||||||
if(frequency < 14) {
|
if(frequency < 14) {
|
||||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||||
lfsr = (lfsr >> 1) ^ (bit << 14);
|
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
|
||||||
if(narrow_lfsr) lfsr |= (bit << 6);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint4 sample = (lfsr & 1) ? 0 : volume;
|
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
|
||||||
if(counter && length == 0) sample = 0;
|
if(enable == false) sample = 0;
|
||||||
|
|
||||||
output = (sample * 4369) - 32768;
|
output = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::clock_length() {
|
void APU::Noise::clock_length() {
|
||||||
if(counter && length) length--;
|
if(counter && length) {
|
||||||
|
if(--length == 0) enable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::clock_envelope() {
|
void APU::Noise::clock_envelope() {
|
||||||
if(envelope_period && --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--;
|
||||||
if(envelope_direction == 1 && volume < 15) volume++;
|
if(envelope_direction == 1 && volume < 15) volume++;
|
||||||
@@ -29,42 +34,42 @@ void APU::Noise::clock_envelope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::write(unsigned r, uint8 data) {
|
void APU::Noise::write(unsigned r, uint8 data) {
|
||||||
if(r == 1) {
|
if(r == 1) { //$ff20 NR41
|
||||||
initial_length = 64 - (data & 0x3f);
|
length = 64 - (data & 0x3f);
|
||||||
|
|
||||||
length = initial_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 2) {
|
if(r == 2) { //$ff21 NR42
|
||||||
envelope_volume = data >> 4;
|
envelope_volume = data >> 4;
|
||||||
envelope_direction = data & 0x08;
|
envelope_direction = data & 0x08;
|
||||||
envelope_frequency = data & 0x07;
|
envelope_frequency = data & 0x07;
|
||||||
|
if(dac_enable() == false) enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 3) {
|
if(r == 3) { //$ff22 NR43
|
||||||
frequency = data >> 4;
|
frequency = data >> 4;
|
||||||
narrow_lfsr = data & 0x08;
|
narrow_lfsr = data & 0x08;
|
||||||
divisor = (data & 0x07) << 4;
|
divisor = (data & 0x07) << 4;
|
||||||
if(divisor == 0) divisor = 8;
|
if(divisor == 0) divisor = 8;
|
||||||
|
|
||||||
period = divisor << frequency;
|
period = divisor << frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 4) {
|
if(r == 4) { //$ff34 NR44
|
||||||
bool initialize = data & 0x80;
|
bool initialize = data & 0x80;
|
||||||
counter = data & 0x40;
|
counter = data & 0x40;
|
||||||
|
|
||||||
if(initialize) {
|
if(initialize) {
|
||||||
|
enable = dac_enable();
|
||||||
lfsr = ~0U;
|
lfsr = ~0U;
|
||||||
length = initial_length;
|
|
||||||
envelope_period = envelope_frequency;
|
envelope_period = envelope_frequency;
|
||||||
volume = envelope_volume;
|
volume = envelope_volume;
|
||||||
|
if(length == 0) length = 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::power() {
|
void APU::Noise::power() {
|
||||||
initial_length = 0;
|
enable = 0;
|
||||||
|
|
||||||
envelope_volume = 0;
|
envelope_volume = 0;
|
||||||
envelope_direction = 0;
|
envelope_direction = 0;
|
||||||
envelope_frequency = 0;
|
envelope_frequency = 0;
|
||||||
@@ -82,7 +87,8 @@ void APU::Noise::power() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Noise::serialize(serializer &s) {
|
void APU::Noise::serialize(serializer &s) {
|
||||||
s.integer(initial_length);
|
s.integer(enable);
|
||||||
|
|
||||||
s.integer(envelope_volume);
|
s.integer(envelope_volume);
|
||||||
s.integer(envelope_direction);
|
s.integer(envelope_direction);
|
||||||
s.integer(envelope_frequency);
|
s.integer(envelope_frequency);
|
||||||
|
@@ -1,20 +1,23 @@
|
|||||||
struct Noise {
|
struct Noise {
|
||||||
unsigned initial_length;
|
bool enable;
|
||||||
unsigned envelope_volume;
|
|
||||||
|
uint4 envelope_volume;
|
||||||
bool envelope_direction;
|
bool envelope_direction;
|
||||||
unsigned envelope_frequency;
|
uint3 envelope_frequency;
|
||||||
unsigned frequency;
|
uint4 frequency;
|
||||||
bool narrow_lfsr;
|
bool narrow_lfsr;
|
||||||
unsigned divisor;
|
unsigned divisor;
|
||||||
bool counter;
|
bool counter;
|
||||||
|
|
||||||
int16 output;
|
int16 output;
|
||||||
unsigned length;
|
unsigned length;
|
||||||
unsigned envelope_period;
|
uint3 envelope_period;
|
||||||
unsigned volume;
|
uint4 volume;
|
||||||
unsigned period;
|
unsigned period;
|
||||||
uint15 lfsr;
|
uint15 lfsr;
|
||||||
|
|
||||||
|
bool dac_enable();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void clock_length();
|
void clock_length();
|
||||||
void clock_envelope();
|
void clock_envelope();
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
#ifdef APU_CPP
|
#ifdef APU_CPP
|
||||||
|
|
||||||
void APU::serialize(serializer &s) {
|
void APU::serialize(serializer &s) {
|
||||||
|
Processor::serialize(s);
|
||||||
|
|
||||||
s.array(mmio_data);
|
s.array(mmio_data);
|
||||||
s.integer(sequencer_base);
|
s.integer(sequencer_base);
|
||||||
s.integer(sequencer_step);
|
s.integer(sequencer_step);
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
#ifdef APU_CPP
|
#ifdef APU_CPP
|
||||||
|
|
||||||
|
bool APU::Square1::dac_enable() {
|
||||||
|
return (envelope_volume || envelope_direction);
|
||||||
|
}
|
||||||
|
|
||||||
void APU::Square1::run() {
|
void APU::Square1::run() {
|
||||||
if(period && --period == 0) {
|
if(period && --period == 0) {
|
||||||
period = 4 * (2048 - frequency);
|
period = 4 * (2048 - frequency);
|
||||||
phase = (phase + 1) & 7;
|
phase++;
|
||||||
switch(duty) {
|
switch(duty) {
|
||||||
case 0: duty_output = (phase == 6); break; //______-_
|
case 0: duty_output = (phase == 6); break; //______-_
|
||||||
case 1: duty_output = (phase >= 6); break; //______--
|
case 1: duty_output = (phase >= 6); break; //______--
|
||||||
@@ -12,45 +16,44 @@ void APU::Square1::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint4 sample = (duty_output ? volume : 0);
|
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||||
if(counter && length == 0) sample = 0;
|
if(enable == false) sample = 0;
|
||||||
|
|
||||||
output = (sample * 4369) - 32768;
|
output = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::sweep() {
|
void APU::Square1::sweep(bool update) {
|
||||||
if(enable == false) return;
|
if(sweep_enable == false) return;
|
||||||
|
|
||||||
signed offset = frequency_shadow >> sweep_shift;
|
sweep_negate = sweep_direction;
|
||||||
if(sweep_direction) offset = -offset;
|
unsigned delta = frequency_shadow >> sweep_shift;
|
||||||
frequency_shadow += offset;
|
signed freq = frequency_shadow + (sweep_negate ? -delta : delta);
|
||||||
|
|
||||||
if(frequency_shadow < 0) {
|
if(freq > 2047) {
|
||||||
frequency_shadow = 0;
|
|
||||||
} else if(frequency_shadow > 2047) {
|
|
||||||
frequency_shadow = 2048;
|
|
||||||
enable = false;
|
enable = false;
|
||||||
}
|
} else if(sweep_shift && update) {
|
||||||
|
frequency_shadow = freq;
|
||||||
if(frequency_shadow <= 2047 && sweep_shift) {
|
frequency = freq & 2047;
|
||||||
frequency = frequency_shadow;
|
|
||||||
period = 4 * (2048 - frequency);
|
period = 4 * (2048 - frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::clock_length() {
|
void APU::Square1::clock_length() {
|
||||||
if(counter && length) length--;
|
if(counter && length) {
|
||||||
|
if(--length == 0) enable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::clock_sweep() {
|
void APU::Square1::clock_sweep() {
|
||||||
if(sweep_frequency && sweep_period && --sweep_period == 0) {
|
if(enable && sweep_frequency && --sweep_period == 0) {
|
||||||
sweep_period = sweep_frequency;
|
sweep_period = sweep_frequency;
|
||||||
sweep();
|
sweep(1);
|
||||||
|
sweep(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::clock_envelope() {
|
void APU::Square1::clock_envelope() {
|
||||||
if(envelope_period && --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--;
|
||||||
if(envelope_direction == 1 && volume < 15) volume++;
|
if(envelope_direction == 1 && volume < 15) volume++;
|
||||||
@@ -58,39 +61,44 @@ void APU::Square1::clock_envelope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::write(unsigned r, uint8 data) {
|
void APU::Square1::write(unsigned r, uint8 data) {
|
||||||
if(r == 0) {
|
if(r == 0) { //$ff10 NR10
|
||||||
|
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
|
||||||
sweep_frequency = (data >> 4) & 7;
|
sweep_frequency = (data >> 4) & 7;
|
||||||
sweep_direction = data & 0x08;
|
sweep_direction = data & 0x08;
|
||||||
sweep_shift = data & 0x07;
|
sweep_shift = data & 0x07;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 1) {
|
if(r == 1) { //$ff11 NR11
|
||||||
duty = data >> 6;
|
duty = data >> 6;
|
||||||
length = data & 0x3f;
|
length = 64 - (data & 0x3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 2) {
|
if(r == 2) { //$ff12 NR12
|
||||||
envelope_volume = data >> 4;
|
envelope_volume = data >> 4;
|
||||||
envelope_direction = data & 0x08;
|
envelope_direction = data & 0x08;
|
||||||
envelope_frequency = data & 0x07;
|
envelope_frequency = data & 0x07;
|
||||||
|
if(dac_enable() == false) enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 3) {
|
if(r == 3) { //$ff13 NR13
|
||||||
frequency = (frequency & 0x0700) | data;
|
frequency = (frequency & 0x0700) | data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 4) {
|
if(r == 4) { //$ff14 NR14
|
||||||
bool initialize = data & 0x80;
|
bool initialize = data & 0x80;
|
||||||
counter = data & 0x40;
|
counter = data & 0x40;
|
||||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||||
|
|
||||||
if(initialize) {
|
if(initialize) {
|
||||||
|
enable = dac_enable();
|
||||||
envelope_period = envelope_frequency;
|
envelope_period = envelope_frequency;
|
||||||
volume = envelope_volume;
|
volume = envelope_volume;
|
||||||
frequency_shadow = frequency;
|
frequency_shadow = frequency;
|
||||||
sweep_period = sweep_frequency;
|
sweep_period = sweep_frequency;
|
||||||
enable = sweep_period || sweep_shift;
|
sweep_enable = sweep_period || sweep_shift;
|
||||||
if(sweep_shift) sweep();
|
sweep_negate = false;
|
||||||
|
if(sweep_shift) sweep(0);
|
||||||
|
if(length == 0) length = 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,9 +106,12 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::power() {
|
void APU::Square1::power() {
|
||||||
|
enable = 0;
|
||||||
|
|
||||||
sweep_frequency = 0;
|
sweep_frequency = 0;
|
||||||
sweep_direction = 0;
|
sweep_direction = 0;
|
||||||
sweep_shift = 0;
|
sweep_shift = 0;
|
||||||
|
sweep_negate = 0;
|
||||||
duty = 0;
|
duty = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
envelope_volume = 0;
|
envelope_volume = 0;
|
||||||
@@ -116,14 +127,17 @@ void APU::Square1::power() {
|
|||||||
envelope_period = 0;
|
envelope_period = 0;
|
||||||
sweep_period = 0;
|
sweep_period = 0;
|
||||||
frequency_shadow = 0;
|
frequency_shadow = 0;
|
||||||
enable = 0;
|
sweep_enable = 0;
|
||||||
volume = 0;
|
volume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square1::serialize(serializer &s) {
|
void APU::Square1::serialize(serializer &s) {
|
||||||
|
s.integer(enable);
|
||||||
|
|
||||||
s.integer(sweep_frequency);
|
s.integer(sweep_frequency);
|
||||||
s.integer(sweep_direction);
|
s.integer(sweep_direction);
|
||||||
s.integer(sweep_shift);
|
s.integer(sweep_shift);
|
||||||
|
s.integer(sweep_negate);
|
||||||
s.integer(duty);
|
s.integer(duty);
|
||||||
s.integer(length);
|
s.integer(length);
|
||||||
s.integer(envelope_volume);
|
s.integer(envelope_volume);
|
||||||
@@ -139,7 +153,7 @@ void APU::Square1::serialize(serializer &s) {
|
|||||||
s.integer(envelope_period);
|
s.integer(envelope_period);
|
||||||
s.integer(sweep_period);
|
s.integer(sweep_period);
|
||||||
s.integer(frequency_shadow);
|
s.integer(frequency_shadow);
|
||||||
s.integer(enable);
|
s.integer(sweep_enable);
|
||||||
s.integer(volume);
|
s.integer(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,27 +1,32 @@
|
|||||||
struct Square1 {
|
struct Square1 {
|
||||||
unsigned sweep_frequency;
|
bool enable;
|
||||||
unsigned sweep_direction;
|
|
||||||
unsigned sweep_shift;
|
uint3 sweep_frequency;
|
||||||
unsigned duty;
|
bool sweep_direction;
|
||||||
|
uint3 sweep_shift;
|
||||||
|
bool sweep_negate;
|
||||||
|
uint2 duty;
|
||||||
unsigned length;
|
unsigned length;
|
||||||
unsigned envelope_volume;
|
uint4 envelope_volume;
|
||||||
unsigned envelope_direction;
|
bool envelope_direction;
|
||||||
unsigned envelope_frequency;
|
uint3 envelope_frequency;
|
||||||
unsigned frequency;
|
uint11 frequency;
|
||||||
unsigned counter;
|
bool counter;
|
||||||
|
|
||||||
int16 output;
|
int16 output;
|
||||||
bool duty_output;
|
bool duty_output;
|
||||||
unsigned phase;
|
uint3 phase;
|
||||||
unsigned period;
|
unsigned period;
|
||||||
unsigned envelope_period;
|
uint3 envelope_period;
|
||||||
unsigned sweep_period;
|
uint3 sweep_period;
|
||||||
signed frequency_shadow;
|
signed frequency_shadow;
|
||||||
bool enable;
|
bool sweep_enable;
|
||||||
unsigned volume;
|
uint4 volume;
|
||||||
|
|
||||||
|
bool dac_enable();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void sweep();
|
void sweep(bool update);
|
||||||
void clock_length();
|
void clock_length();
|
||||||
void clock_sweep();
|
void clock_sweep();
|
||||||
void clock_envelope();
|
void clock_envelope();
|
||||||
|
@@ -1,9 +1,13 @@
|
|||||||
#ifdef APU_CPP
|
#ifdef APU_CPP
|
||||||
|
|
||||||
|
bool APU::Square2::dac_enable() {
|
||||||
|
return (envelope_volume || envelope_direction);
|
||||||
|
}
|
||||||
|
|
||||||
void APU::Square2::run() {
|
void APU::Square2::run() {
|
||||||
if(period && --period == 0) {
|
if(period && --period == 0) {
|
||||||
period = 4 * (2048 - frequency);
|
period = 4 * (2048 - frequency);
|
||||||
phase = (phase + 1) & 7;
|
phase++;
|
||||||
switch(duty) {
|
switch(duty) {
|
||||||
case 0: duty_output = (phase == 6); break; //______-_
|
case 0: duty_output = (phase == 6); break; //______-_
|
||||||
case 1: duty_output = (phase >= 6); break; //______--
|
case 1: duty_output = (phase >= 6); break; //______--
|
||||||
@@ -12,18 +16,20 @@ void APU::Square2::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint4 sample = (duty_output ? volume : 0);
|
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||||
if(counter && length == 0) sample = 0;
|
if(enable == false) sample = 0;
|
||||||
|
|
||||||
output = (sample * 4369) - 32768;
|
output = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square2::clock_length() {
|
void APU::Square2::clock_length() {
|
||||||
if(counter && length) length--;
|
if(counter && length) {
|
||||||
|
if(--length == 0) enable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square2::clock_envelope() {
|
void APU::Square2::clock_envelope() {
|
||||||
if(envelope_period && --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--;
|
||||||
if(envelope_direction == 1 && volume < 15) volume++;
|
if(envelope_direction == 1 && volume < 15) volume++;
|
||||||
@@ -31,29 +37,32 @@ void APU::Square2::clock_envelope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square2::write(unsigned r, uint8 data) {
|
void APU::Square2::write(unsigned r, uint8 data) {
|
||||||
if(r == 1) {
|
if(r == 1) { //$ff16 NR21
|
||||||
duty = data >> 6;
|
duty = data >> 6;
|
||||||
length = data & 0x3f;
|
length = 64 - (data & 0x3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 2) {
|
if(r == 2) { //$ff17 NR22
|
||||||
envelope_volume = data >> 4;
|
envelope_volume = data >> 4;
|
||||||
envelope_direction = data & 0x08;
|
envelope_direction = data & 0x08;
|
||||||
envelope_frequency = data & 0x07;
|
envelope_frequency = data & 0x07;
|
||||||
|
if(dac_enable() == false) enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 3) {
|
if(r == 3) { //$ff18 NR23
|
||||||
frequency = (frequency & 0x0700) | data;
|
frequency = (frequency & 0x0700) | data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 4) {
|
if(r == 4) { //$ff19 NR24
|
||||||
bool initialize = data & 0x80;
|
bool initialize = data & 0x80;
|
||||||
counter = data & 0x40;
|
counter = data & 0x40;
|
||||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||||
|
|
||||||
if(initialize) {
|
if(initialize) {
|
||||||
|
enable = dac_enable();
|
||||||
envelope_period = envelope_frequency;
|
envelope_period = envelope_frequency;
|
||||||
volume = envelope_volume;
|
volume = envelope_volume;
|
||||||
|
if(length == 0) length = 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +70,8 @@ void APU::Square2::write(unsigned r, uint8 data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square2::power() {
|
void APU::Square2::power() {
|
||||||
|
enable = 0;
|
||||||
|
|
||||||
duty = 0;
|
duty = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
envelope_volume = 0;
|
envelope_volume = 0;
|
||||||
@@ -78,6 +89,8 @@ void APU::Square2::power() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Square2::serialize(serializer &s) {
|
void APU::Square2::serialize(serializer &s) {
|
||||||
|
s.integer(enable);
|
||||||
|
|
||||||
s.integer(duty);
|
s.integer(duty);
|
||||||
s.integer(length);
|
s.integer(length);
|
||||||
s.integer(envelope_volume);
|
s.integer(envelope_volume);
|
||||||
|
@@ -1,18 +1,22 @@
|
|||||||
struct Square2 {
|
struct Square2 {
|
||||||
unsigned duty;
|
bool enable;
|
||||||
|
|
||||||
|
uint2 duty;
|
||||||
unsigned length;
|
unsigned length;
|
||||||
unsigned envelope_volume;
|
uint4 envelope_volume;
|
||||||
unsigned envelope_direction;
|
bool envelope_direction;
|
||||||
unsigned envelope_frequency;
|
uint3 envelope_frequency;
|
||||||
unsigned frequency;
|
uint11 frequency;
|
||||||
unsigned counter;
|
bool counter;
|
||||||
|
|
||||||
int16 output;
|
int16 output;
|
||||||
bool duty_output;
|
bool duty_output;
|
||||||
unsigned phase;
|
uint3 phase;
|
||||||
unsigned period;
|
unsigned period;
|
||||||
unsigned envelope_period;
|
uint3 envelope_period;
|
||||||
unsigned volume;
|
uint4 volume;
|
||||||
|
|
||||||
|
bool dac_enable();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void clock_length();
|
void clock_length();
|
||||||
|
@@ -3,57 +3,53 @@
|
|||||||
void APU::Wave::run() {
|
void APU::Wave::run() {
|
||||||
if(period && --period == 0) {
|
if(period && --period == 0) {
|
||||||
period = 2 * (2048 - frequency);
|
period = 2 * (2048 - frequency);
|
||||||
pattern_offset = (pattern_offset + 1) & 31;
|
pattern_sample = pattern[++pattern_offset];
|
||||||
pattern_sample = pattern[pattern_offset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint4 sample = pattern_sample;
|
uint4 sample = pattern_sample >> volume_shift;
|
||||||
if(counter && length == 0) sample = 0;
|
|
||||||
if(enable == false) sample = 0;
|
if(enable == false) sample = 0;
|
||||||
|
|
||||||
output = (sample * 4369) - 32768;
|
output = sample;
|
||||||
output >>= volume;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Wave::clock_length() {
|
void APU::Wave::clock_length() {
|
||||||
if(counter && length) length--;
|
if(counter && length) {
|
||||||
|
if(--length == 0) enable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Wave::write(unsigned r, uint8 data) {
|
void APU::Wave::write(unsigned r, uint8 data) {
|
||||||
if(r == 0) {
|
if(r == 0) { //$ff1a NR30
|
||||||
dac_enable = data & 0x80;
|
dac_enable = data & 0x80;
|
||||||
|
|
||||||
if(dac_enable == false) enable = false;
|
if(dac_enable == false) enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 1) {
|
if(r == 1) { //$ff1b NR31
|
||||||
initial_length = 256 - data;
|
length = 256 - data;
|
||||||
|
|
||||||
length = initial_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 2) {
|
if(r == 2) { //$ff1c NR32
|
||||||
switch((data >> 5) & 3) {
|
switch((data >> 5) & 3) {
|
||||||
case 0: volume = 16; break; // 0%
|
case 0: volume_shift = 4; break; // 0%
|
||||||
case 1: volume = 0; break; //100%
|
case 1: volume_shift = 0; break; //100%
|
||||||
case 2: volume = 1; break; // 50%
|
case 2: volume_shift = 1; break; // 50%
|
||||||
case 3: volume = 2; break; // 25%
|
case 3: volume_shift = 2; break; // 25%
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 3) {
|
if(r == 3) { //$ff1d NR33
|
||||||
frequency = (frequency & 0x0700) | data;
|
frequency = (frequency & 0x0700) | data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r == 4) {
|
if(r == 4) { //$ff1e NR34
|
||||||
bool initialize = data & 0x80;
|
bool initialize = data & 0x80;
|
||||||
counter = data & 0x40;
|
counter = data & 0x40;
|
||||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||||
|
|
||||||
if(initialize && dac_enable) {
|
if(initialize) {
|
||||||
enable = true;
|
enable = dac_enable;
|
||||||
pattern_offset = 0;
|
pattern_offset = 0;
|
||||||
length = initial_length;
|
if(length == 0) length = 256;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,17 +63,17 @@ void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Wave::power() {
|
void APU::Wave::power() {
|
||||||
|
enable = 0;
|
||||||
|
|
||||||
dac_enable = 0;
|
dac_enable = 0;
|
||||||
initial_length = 0;
|
volume_shift = 0;
|
||||||
volume = 0;
|
|
||||||
frequency = 0;
|
frequency = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
|
||||||
random_lfsr r;
|
random_lfsr r;
|
||||||
foreach(n, pattern) n = r() & 15;
|
for(auto &n : pattern) n = r() & 15;
|
||||||
|
|
||||||
output = 0;
|
output = 0;
|
||||||
enable = 0;
|
|
||||||
length = 0;
|
length = 0;
|
||||||
period = 0;
|
period = 0;
|
||||||
pattern_offset = 0;
|
pattern_offset = 0;
|
||||||
@@ -85,15 +81,15 @@ void APU::Wave::power() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void APU::Wave::serialize(serializer &s) {
|
void APU::Wave::serialize(serializer &s) {
|
||||||
|
s.integer(enable);
|
||||||
|
|
||||||
s.integer(dac_enable);
|
s.integer(dac_enable);
|
||||||
s.integer(initial_length);
|
s.integer(volume_shift);
|
||||||
s.integer(volume);
|
|
||||||
s.integer(frequency);
|
s.integer(frequency);
|
||||||
s.integer(counter);
|
s.integer(counter);
|
||||||
s.array(pattern);
|
s.array(pattern);
|
||||||
|
|
||||||
s.integer(output);
|
s.integer(output);
|
||||||
s.integer(enable);
|
|
||||||
s.integer(length);
|
s.integer(length);
|
||||||
s.integer(period);
|
s.integer(period);
|
||||||
s.integer(pattern_offset);
|
s.integer(pattern_offset);
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
struct Wave {
|
struct Wave {
|
||||||
|
bool enable;
|
||||||
|
|
||||||
bool dac_enable;
|
bool dac_enable;
|
||||||
unsigned initial_length;
|
unsigned volume_shift;
|
||||||
unsigned volume;
|
uint11 frequency;
|
||||||
unsigned frequency;
|
|
||||||
bool counter;
|
bool counter;
|
||||||
uint8 pattern[32];
|
uint8 pattern[32];
|
||||||
|
|
||||||
int16 output;
|
int16 output;
|
||||||
bool enable;
|
|
||||||
unsigned length;
|
unsigned length;
|
||||||
unsigned period;
|
unsigned period;
|
||||||
unsigned pattern_offset;
|
uint5 pattern_offset;
|
||||||
unsigned pattern_sample;
|
uint4 pattern_sample;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void clock_length();
|
void clock_length();
|
||||||
|
@@ -16,14 +16,11 @@ namespace GameBoy {
|
|||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
|
|
||||||
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
|
||||||
if(size == 0) size = 32768;
|
if(size == 0) size = 32768;
|
||||||
romdata = allocate<uint8>(romsize = size, 0xff);
|
romdata = allocate<uint8>(romsize = size, 0xff);
|
||||||
if(data) memcpy(romdata, data, size);
|
if(data) memcpy(romdata, data, size);
|
||||||
|
|
||||||
//uint32_t crc = crc32_calculate(data, size);
|
|
||||||
//print("CRC32 = ", hex<4>(crc), "\n");
|
|
||||||
|
|
||||||
info.mapper = Mapper::Unknown;
|
info.mapper = Mapper::Unknown;
|
||||||
info.ram = false;
|
info.ram = false;
|
||||||
info.battery = false;
|
info.battery = false;
|
||||||
@@ -33,42 +30,24 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
|||||||
info.romsize = 0;
|
info.romsize = 0;
|
||||||
info.ramsize = 0;
|
info.ramsize = 0;
|
||||||
|
|
||||||
xml_element document = xml_parse(xml);
|
XML::Document document(markup);
|
||||||
foreach(head, document.element) {
|
|
||||||
if(head.name == "cartridge") {
|
|
||||||
foreach(attr, head.attribute) {
|
|
||||||
if(attr.name == "mapper") {
|
|
||||||
if(attr.content == "none") info.mapper = Mapper::MBC0;
|
|
||||||
if(attr.content == "MBC1") info.mapper = Mapper::MBC1;
|
|
||||||
if(attr.content == "MBC2") info.mapper = Mapper::MBC2;
|
|
||||||
if(attr.content == "MBC3") info.mapper = Mapper::MBC3;
|
|
||||||
if(attr.content == "MBC5") info.mapper = Mapper::MBC5;
|
|
||||||
if(attr.content == "MMM01") info.mapper = Mapper::MMM01;
|
|
||||||
if(attr.content == "HuC1") info.mapper = Mapper::HuC1;
|
|
||||||
if(attr.content == "HuC3") info.mapper = Mapper::HuC3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(attr.name == "rtc") info.rtc = (attr.content == "true" ? true : false);
|
auto &mapperid = document["cartridge"]["mapper"].data;
|
||||||
if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false);
|
if(mapperid == "none" ) info.mapper = Mapper::MBC0;
|
||||||
}
|
if(mapperid == "MBC1" ) info.mapper = Mapper::MBC1;
|
||||||
|
if(mapperid == "MBC2" ) info.mapper = Mapper::MBC2;
|
||||||
|
if(mapperid == "MBC3" ) info.mapper = Mapper::MBC3;
|
||||||
|
if(mapperid == "MBC5" ) info.mapper = Mapper::MBC5;
|
||||||
|
if(mapperid == "MMM01") info.mapper = Mapper::MMM01;
|
||||||
|
if(mapperid == "HuC1" ) info.mapper = Mapper::HuC1;
|
||||||
|
if(mapperid == "HuC3" ) info.mapper = Mapper::HuC3;
|
||||||
|
|
||||||
foreach(elem, head.element) {
|
info.rtc = document["cartridge"]["rtc"].data == "true";
|
||||||
if(elem.name == "rom") {
|
info.rumble = document["cartridge"]["rumble"].data == "true";
|
||||||
foreach(attr, elem.attribute) {
|
|
||||||
if(attr.name == "size") info.romsize = hex(attr.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(elem.name == "ram") {
|
info.romsize = hex(document["cartridge"]["rom"]["size"].data);
|
||||||
info.ram = true;
|
info.ramsize = hex(document["cartridge"]["ram"]["size"].data);
|
||||||
foreach(attr, elem.attribute) {
|
info.battery = document["cartridge"]["ram"]["battery"].data == "true";
|
||||||
if(attr.name == "size") info.ramsize = hex(attr.content);
|
|
||||||
if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(info.mapper) { default:
|
switch(info.mapper) { default:
|
||||||
case Mapper::MBC0: mapper = &mbc0; break;
|
case Mapper::MBC0: mapper = &mbc0; break;
|
||||||
@@ -82,8 +61,10 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ramdata = new uint8_t[ramsize = info.ramsize]();
|
ramdata = new uint8_t[ramsize = info.ramsize]();
|
||||||
system.load();
|
system.load(revision);
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
sha256 = nall::sha256(romdata, romsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::unload() {
|
void Cartridge::unload() {
|
||||||
@@ -117,12 +98,28 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 Cartridge::mmio_read(uint16 addr) {
|
uint8 Cartridge::mmio_read(uint16 addr) {
|
||||||
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
|
if(addr == 0xff50) return 0x00;
|
||||||
|
|
||||||
|
if(bootrom_enable) {
|
||||||
|
const uint8 *data = nullptr;
|
||||||
|
switch(system.revision()) { default:
|
||||||
|
case System::Revision::GameBoy: data = System::BootROM::dmg; break;
|
||||||
|
case System::Revision::SuperGameBoy: data = System::BootROM::sgb; break;
|
||||||
|
case System::Revision::GameBoyColor: data = System::BootROM::cgb; break;
|
||||||
|
}
|
||||||
|
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr];
|
||||||
|
if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256];
|
||||||
|
}
|
||||||
|
|
||||||
return mapper->mmio_read(addr);
|
return mapper->mmio_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
|
if(bootrom_enable && addr == 0xff50) {
|
||||||
|
bootrom_enable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mapper->mmio_write(addr, data);
|
mapper->mmio_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||||||
} info;
|
} info;
|
||||||
|
|
||||||
readonly<bool> loaded;
|
readonly<bool> loaded;
|
||||||
|
readonly<string> sha256;
|
||||||
|
|
||||||
uint8_t *romdata;
|
uint8_t *romdata;
|
||||||
unsigned romsize;
|
unsigned romsize;
|
||||||
@@ -44,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||||||
MMIO *mapper;
|
MMIO *mapper;
|
||||||
bool bootrom_enable;
|
bool bootrom_enable;
|
||||||
|
|
||||||
void load(const string &xml, const uint8_t *data, unsigned size);
|
void load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
|
||||||
void unload();
|
void unload();
|
||||||
|
|
||||||
uint8 rom_read(unsigned addr);
|
uint8 rom_read(unsigned addr);
|
||||||
|
@@ -1,53 +1,54 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
|
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
||||||
return 0x00;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
ram_writable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
rom_select = data;
|
rom_select = data;
|
||||||
|
if(rom_select == 0) rom_select = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
ram_select = data;
|
ram_select = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
//unknown purpose
|
model = data & 0x01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
if(ram_writable == false) return;
|
||||||
return;
|
return cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::HuC1::power() {
|
void Cartridge::HuC1::power() {
|
||||||
ram_enable = false;
|
ram_writable = false;
|
||||||
rom_select = 0x01;
|
rom_select = 0x01;
|
||||||
ram_select = 0x00;
|
ram_select = 0x00;
|
||||||
|
model = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
struct HuC1 : MMIO {
|
struct HuC1 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_writable; //$0000-1fff
|
||||||
uint8 rom_select; //2000-3fff
|
uint8 rom_select; //$2000-3fff
|
||||||
uint8 ram_select; //4000-5fff
|
uint8 ram_select; //$4000-5fff
|
||||||
|
bool model; //$6000-7fff
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
|
uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
@@ -18,27 +18,27 @@ uint8 Cartridge::HuC3::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
ram_enable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
rom_select = data;
|
rom_select = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
ram_select = data;
|
ram_select = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
//unknown purpose
|
//unknown purpose
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
struct HuC3 : MMIO {
|
struct HuC3 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_enable; //$0000-1fff
|
||||||
uint8 rom_select; //2000-3fff
|
uint8 rom_select; //$2000-3fff
|
||||||
uint8 ram_select; //4000-5fff
|
uint8 ram_select; //$4000-5fff
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
|
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x7fff>(addr)) {
|
if((addr & 0x8000) == 0x0000) { //$0000-7fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
return cartridge.ram_read(addr & 0x1fff);
|
return cartridge.ram_read(addr & 0x1fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
cartridge.ram_write(addr & 0x1fff, data);
|
cartridge.ram_write(addr & 0x1fff, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
|
uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
if(mode_select == 0) {
|
if(mode_select == 0) {
|
||||||
return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff));
|
||||||
} else {
|
} else {
|
||||||
@@ -13,7 +13,7 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) {
|
if(ram_enable) {
|
||||||
if(mode_select == 0) {
|
if(mode_select == 0) {
|
||||||
return cartridge.ram_read(addr & 0x1fff);
|
return cartridge.ram_read(addr & 0x1fff);
|
||||||
@@ -28,27 +28,27 @@ uint8 Cartridge::MBC1::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
ram_enable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
rom_select = (data & 0x1f) + ((data & 0x1f) == 0);
|
rom_select = (data & 0x1f) + ((data & 0x1f) == 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
ram_select = data & 0x03;
|
ram_select = data & 0x03;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
mode_select = data & 0x01;
|
mode_select = data & 0x01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) {
|
if(ram_enable) {
|
||||||
if(mode_select == 0) {
|
if(mode_select == 0) {
|
||||||
cartridge.ram_write(addr & 0x1fff, data);
|
cartridge.ram_write(addr & 0x1fff, data);
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
struct MBC1 : MMIO {
|
struct MBC1 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_enable; //$0000-1fff
|
||||||
uint8 rom_select; //2000-3fff
|
uint8 rom_select; //$2000-3fff
|
||||||
uint8 ram_select; //4000-5fff
|
uint8 ram_select; //$4000-5fff
|
||||||
bool mode_select; //6000-7fff
|
bool mode_select; //$6000-7fff
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
|
uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xa1ff>(addr)) {
|
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
|
||||||
if(ram_enable) return cartridge.ram_read(addr & 0x1ff);
|
if(ram_enable) return cartridge.ram_read(addr & 0x1ff);
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
@@ -18,17 +18,17 @@ uint8 Cartridge::MBC2::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a;
|
if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
|
if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xa1ff>(addr)) {
|
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
|
||||||
if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f);
|
if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
struct MBC2 : MMIO {
|
struct MBC2 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_enable; //$0000-1fff
|
||||||
uint8 rom_select; //2000-3fff
|
uint8 rom_select; //$2000-3fff
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -19,15 +19,15 @@ void Cartridge::MBC3::second() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
|
uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) {
|
if(ram_enable) {
|
||||||
if(ram_select >= 0x00 && ram_select <= 0x03) {
|
if(ram_select >= 0x00 && ram_select <= 0x03) {
|
||||||
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
||||||
@@ -45,22 +45,22 @@ uint8 Cartridge::MBC3::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
ram_enable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
rom_select = (data & 0x7f) + ((data & 0x7f) == 0);
|
rom_select = (data & 0x7f) + ((data & 0x7f) == 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
ram_select = data;
|
ram_select = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
if(rtc_latch == 0 && data == 1) {
|
if(rtc_latch == 0 && data == 1) {
|
||||||
rtc_latch_second = rtc_second;
|
rtc_latch_second = rtc_second;
|
||||||
rtc_latch_minute = rtc_minute;
|
rtc_latch_minute = rtc_minute;
|
||||||
@@ -72,7 +72,7 @@ void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) {
|
if(ram_enable) {
|
||||||
if(ram_select >= 0x00 && ram_select <= 0x03) {
|
if(ram_select >= 0x00 && ram_select <= 0x03) {
|
||||||
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
struct MBC3 : MMIO {
|
struct MBC3 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_enable; //$0000-1fff
|
||||||
uint8 rom_select; //2000-3fff
|
uint8 rom_select; //$2000-3fff
|
||||||
uint8 ram_select; //4000-5fff
|
uint8 ram_select; //$4000-5fff
|
||||||
bool rtc_latch; //6000-7fff
|
bool rtc_latch; //$6000-7fff
|
||||||
|
|
||||||
bool rtc_halt;
|
bool rtc_halt;
|
||||||
unsigned rtc_second;
|
unsigned rtc_second;
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
|
uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(addr);
|
return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
@@ -18,27 +18,27 @@ uint8 Cartridge::MBC5::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
ram_enable = (data & 0x0f) == 0x0a;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x2fff>(addr)) {
|
if((addr & 0xf000) == 0x2000) { //$2000-2fff
|
||||||
rom_select = (rom_select & 0x0100) | data;
|
rom_select = (rom_select & 0x0100) | data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x3000, 0x3fff>(addr)) {
|
if((addr & 0xf000) == 0x3000) { //$3000-3fff
|
||||||
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
|
rom_select = ((data & 1) << 8) | (rom_select & 0x00ff);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
ram_select = data & 0x0f;
|
ram_select = data & 0x0f;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
struct MBC5 : MMIO {
|
struct MBC5 : MMIO {
|
||||||
bool ram_enable; //0000-1fff
|
bool ram_enable; //$0000-1fff
|
||||||
uint16 rom_select; //2000-2fff + 3000-3fff
|
uint16 rom_select; //$2000-2fff + $3000-3fff
|
||||||
uint8 ram_select; //4000-5fff
|
uint8 ram_select; //$4000-5fff
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
|
uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
|
||||||
if(within<0x0000, 0x7fff>(addr)) {
|
if((addr & 0x8000) == 0x0000) { //$0000-7fff
|
||||||
if(rom_mode == 0) return cartridge.rom_read(addr);
|
if(rom_mode == 0) return cartridge.rom_read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff));
|
return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff));
|
return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff));
|
if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff));
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ uint8 Cartridge::MMM01::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
|
void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
if(rom_mode == 0) {
|
if(rom_mode == 0) {
|
||||||
rom_mode = 1;
|
rom_mode = 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -30,7 +30,7 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
if(rom_mode == 0) {
|
if(rom_mode == 0) {
|
||||||
rom_base = data & 0x3f;
|
rom_base = data & 0x3f;
|
||||||
} else {
|
} else {
|
||||||
@@ -38,24 +38,24 @@ void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
if(rom_mode == 1) {
|
if(rom_mode == 1) {
|
||||||
ram_select = data;
|
ram_select = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
//unknown purpose
|
//unknown purpose
|
||||||
}
|
}
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data);
|
if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::MMM01::power() {
|
void Cartridge::MMM01::power() {
|
||||||
rom_mode = 0;
|
rom_mode = 0;
|
||||||
rom_base = 0x00;
|
rom_base = 0;
|
||||||
|
|
||||||
ram_enable = false;
|
ram_enable = false;
|
||||||
rom_select = 0x01;
|
rom_select = 0x01;
|
||||||
|
@@ -41,9 +41,10 @@ void Cartridge::serialize(serializer &s) {
|
|||||||
s.integer(mmm01.rom_select);
|
s.integer(mmm01.rom_select);
|
||||||
s.integer(mmm01.ram_select);
|
s.integer(mmm01.ram_select);
|
||||||
|
|
||||||
s.integer(huc1.ram_enable);
|
s.integer(huc1.ram_writable);
|
||||||
s.integer(huc1.rom_select);
|
s.integer(huc1.rom_select);
|
||||||
s.integer(huc1.ram_select);
|
s.integer(huc1.ram_select);
|
||||||
|
s.integer(huc1.model);
|
||||||
|
|
||||||
s.integer(huc3.ram_enable);
|
s.integer(huc3.ram_enable);
|
||||||
s.integer(huc3.rom_select);
|
s.integer(huc3.rom_select);
|
||||||
|
91
bsnes/gameboy/cheat/cheat.cpp
Executable file
91
bsnes/gameboy/cheat/cheat.cpp
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
#include <gameboy/gameboy.hpp>
|
||||||
|
|
||||||
|
namespace GameBoy {
|
||||||
|
|
||||||
|
Cheat cheat;
|
||||||
|
|
||||||
|
bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) {
|
||||||
|
static bool initialize = false;
|
||||||
|
static uint8 mapProActionReplay[256], mapGameGenie[256];
|
||||||
|
|
||||||
|
if(initialize == false) {
|
||||||
|
initialize = true;
|
||||||
|
|
||||||
|
for(auto &n : mapProActionReplay) n = ~0;
|
||||||
|
mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3;
|
||||||
|
mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7;
|
||||||
|
mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11;
|
||||||
|
mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15;
|
||||||
|
|
||||||
|
for(auto &n : mapGameGenie) n = ~0;
|
||||||
|
mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3;
|
||||||
|
mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7;
|
||||||
|
mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11;
|
||||||
|
mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
string code = code_;
|
||||||
|
code.upper();
|
||||||
|
unsigned length = code.length(), bits = 0;
|
||||||
|
|
||||||
|
if(code.wildcard("????:??")) {
|
||||||
|
code = { substr(code, 0, 4), substr(code, 5, 2) };
|
||||||
|
for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||||
|
bits = hex(code);
|
||||||
|
addr = (bits >> 8) & 0xffff;
|
||||||
|
data = (bits >> 0) & 0xff;
|
||||||
|
comp = ~0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(code.wildcard("????:??:??")) {
|
||||||
|
code = { substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) };
|
||||||
|
for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false;
|
||||||
|
bits = hex(code);
|
||||||
|
addr = (bits >> 16) & 0xffff;
|
||||||
|
data = (bits >> 8) & 0xff;
|
||||||
|
comp = (bits >> 0) & 0xff;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(code.wildcard("???" "-" "???")) {
|
||||||
|
code = { substr(code, 0, 3), substr(code, 4, 3) };
|
||||||
|
for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||||
|
for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4);
|
||||||
|
|
||||||
|
addr = (bits >> 0) & 0xffff;
|
||||||
|
data = (bits >> 16) & 0xff;
|
||||||
|
comp = ~0;
|
||||||
|
|
||||||
|
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(code.wildcard("???" "-" "???" "-" "???")) {
|
||||||
|
code = { substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) };
|
||||||
|
for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false;
|
||||||
|
for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4);
|
||||||
|
|
||||||
|
addr = (bits >> 8) & 0xffff;
|
||||||
|
data = (bits >> 24) & 0xff;
|
||||||
|
comp = (bits >> 0) & 0xff;
|
||||||
|
|
||||||
|
addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000;
|
||||||
|
comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cheat::synchronize() {
|
||||||
|
for(auto &n : override) n = false;
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
|
override[operator[](n).addr] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
bsnes/gameboy/cheat/cheat.hpp
Executable file
14
bsnes/gameboy/cheat/cheat.hpp
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
struct CheatCode {
|
||||||
|
unsigned addr;
|
||||||
|
unsigned data;
|
||||||
|
unsigned comp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cheat : public linear_vector<CheatCode> {
|
||||||
|
static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp);
|
||||||
|
|
||||||
|
void synchronize();
|
||||||
|
bool override[65536];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Cheat cheat;
|
@@ -571,6 +571,13 @@ void CPU::op_halt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::op_stop() {
|
void CPU::op_stop() {
|
||||||
|
if(status.speed_switch) {
|
||||||
|
status.speed_switch = 0;
|
||||||
|
status.speed_double ^= 1;
|
||||||
|
frequency = 4 * 1024 * 1024;
|
||||||
|
if(status.speed_double) frequency *= 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
status.stop = true;
|
status.stop = true;
|
||||||
while(status.stop == true) op_io();
|
while(status.stop == true) op_io();
|
||||||
}
|
}
|
||||||
@@ -580,7 +587,8 @@ void CPU::op_di() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::op_ei() {
|
void CPU::op_ei() {
|
||||||
status.ime = 1;
|
status.ei = true;
|
||||||
|
//status.ime = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//jump commands
|
//jump commands
|
||||||
|
@@ -94,15 +94,43 @@ void CPU::interrupt_exec(uint16 pc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::power() {
|
void CPU::power() {
|
||||||
create(Main, 4194304);
|
create(Main, 4 * 1024 * 1024);
|
||||||
|
|
||||||
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
|
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
|
||||||
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
|
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
|
||||||
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
|
for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
|
||||||
for(unsigned n = 0xff80; n <= 0xffff; n++) bus.mmio[n] = this; //HRAM+IE
|
|
||||||
|
|
||||||
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
|
bus.mmio[0xff00] = this; //JOYP
|
||||||
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
|
bus.mmio[0xff01] = this; //SB
|
||||||
|
bus.mmio[0xff02] = this; //SC
|
||||||
|
bus.mmio[0xff04] = this; //DIV
|
||||||
|
bus.mmio[0xff05] = this; //TIMA
|
||||||
|
bus.mmio[0xff06] = this; //TMA
|
||||||
|
bus.mmio[0xff07] = this; //TAC
|
||||||
|
bus.mmio[0xff0f] = this; //IF
|
||||||
|
bus.mmio[0xff46] = this; //DMA
|
||||||
|
bus.mmio[0xffff] = this; //IE
|
||||||
|
|
||||||
|
if(system.cgb()) {
|
||||||
|
bus.mmio[0xff4d] = this; //KEY1
|
||||||
|
bus.mmio[0xff51] = this; //HDMA1
|
||||||
|
bus.mmio[0xff52] = this; //HDMA2
|
||||||
|
bus.mmio[0xff53] = this; //HDMA3
|
||||||
|
bus.mmio[0xff54] = this; //HDMA4
|
||||||
|
bus.mmio[0xff55] = this; //HDMA5
|
||||||
|
bus.mmio[0xff56] = this; //RP
|
||||||
|
bus.mmio[0xff6c] = this; //???
|
||||||
|
bus.mmio[0xff70] = this; //SVBK
|
||||||
|
bus.mmio[0xff72] = this; //???
|
||||||
|
bus.mmio[0xff73] = this; //???
|
||||||
|
bus.mmio[0xff74] = this; //???
|
||||||
|
bus.mmio[0xff75] = this; //???
|
||||||
|
bus.mmio[0xff76] = this; //???
|
||||||
|
bus.mmio[0xff77] = this; //???
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto &n : wram) n = 0x00;
|
||||||
|
for(auto &n : hram) n = 0x00;
|
||||||
|
|
||||||
r[PC] = 0x0000;
|
r[PC] = 0x0000;
|
||||||
r[SP] = 0x0000;
|
r[SP] = 0x0000;
|
||||||
@@ -114,6 +142,7 @@ void CPU::power() {
|
|||||||
status.clock = 0;
|
status.clock = 0;
|
||||||
status.halt = false;
|
status.halt = false;
|
||||||
status.stop = false;
|
status.stop = false;
|
||||||
|
status.ei = false;
|
||||||
status.ime = 0;
|
status.ime = 0;
|
||||||
|
|
||||||
status.p15 = 0;
|
status.p15 = 0;
|
||||||
@@ -142,6 +171,23 @@ void CPU::power() {
|
|||||||
status.interrupt_request_stat = 0;
|
status.interrupt_request_stat = 0;
|
||||||
status.interrupt_request_vblank = 0;
|
status.interrupt_request_vblank = 0;
|
||||||
|
|
||||||
|
status.speed_double = 0;
|
||||||
|
status.speed_switch = 0;
|
||||||
|
|
||||||
|
status.dma_source = 0;
|
||||||
|
status.dma_target = 0;
|
||||||
|
|
||||||
|
status.dma_mode = 0;
|
||||||
|
status.dma_length = 0;
|
||||||
|
|
||||||
|
status.ff6c = 0;
|
||||||
|
status.ff72 = 0;
|
||||||
|
status.ff73 = 0;
|
||||||
|
status.ff74 = 0;
|
||||||
|
status.ff75 = 0;
|
||||||
|
|
||||||
|
status.wram_bank = 1;
|
||||||
|
|
||||||
status.interrupt_enable_joypad = 0;
|
status.interrupt_enable_joypad = 0;
|
||||||
status.interrupt_enable_serial = 0;
|
status.interrupt_enable_serial = 0;
|
||||||
status.interrupt_enable_timer = 0;
|
status.interrupt_enable_timer = 0;
|
||||||
|
@@ -17,6 +17,7 @@ struct CPU : Processor, MMIO {
|
|||||||
unsigned clock;
|
unsigned clock;
|
||||||
bool halt;
|
bool halt;
|
||||||
bool stop;
|
bool stop;
|
||||||
|
bool ei;
|
||||||
bool ime;
|
bool ime;
|
||||||
|
|
||||||
//$ff00 JOYP
|
//$ff00 JOYP
|
||||||
@@ -53,6 +54,32 @@ struct CPU : Processor, MMIO {
|
|||||||
bool interrupt_request_stat;
|
bool interrupt_request_stat;
|
||||||
bool interrupt_request_vblank;
|
bool interrupt_request_vblank;
|
||||||
|
|
||||||
|
//$ff4d KEY1
|
||||||
|
bool speed_double;
|
||||||
|
bool speed_switch;
|
||||||
|
|
||||||
|
//$ff51,$ff52 HDMA1,HDMA2
|
||||||
|
uint16 dma_source;
|
||||||
|
|
||||||
|
//$ff53,$ff54 HDMA3,HDMA4
|
||||||
|
uint16 dma_target;
|
||||||
|
|
||||||
|
//$ff55 HDMA5
|
||||||
|
bool dma_mode;
|
||||||
|
uint16 dma_length;
|
||||||
|
|
||||||
|
//$ff6c ???
|
||||||
|
uint8 ff6c;
|
||||||
|
|
||||||
|
//$ff70 SVBK
|
||||||
|
uint3 wram_bank;
|
||||||
|
|
||||||
|
//$ff72-$ff75 ???
|
||||||
|
uint8 ff72;
|
||||||
|
uint8 ff73;
|
||||||
|
uint8 ff74;
|
||||||
|
uint8 ff75;
|
||||||
|
|
||||||
//$ffff IE
|
//$ffff IE
|
||||||
bool interrupt_enable_joypad;
|
bool interrupt_enable_joypad;
|
||||||
bool interrupt_enable_serial;
|
bool interrupt_enable_serial;
|
||||||
@@ -61,7 +88,7 @@ struct CPU : Processor, MMIO {
|
|||||||
bool interrupt_enable_vblank;
|
bool interrupt_enable_vblank;
|
||||||
} status;
|
} status;
|
||||||
|
|
||||||
uint8 wram[8192];
|
uint8 wram[32768]; //GB=8192, GBC=32768
|
||||||
uint8 hram[128];
|
uint8 hram[128];
|
||||||
|
|
||||||
static void Main();
|
static void Main();
|
||||||
|
@@ -1,17 +1,24 @@
|
|||||||
#ifdef CPU_CPP
|
#ifdef CPU_CPP
|
||||||
|
|
||||||
|
unsigned CPU::wram_addr(uint16 addr) const {
|
||||||
|
addr &= 0x1fff;
|
||||||
|
if(addr < 0x1000) return addr;
|
||||||
|
auto bank = status.wram_bank + (status.wram_bank == 0);
|
||||||
|
return (bank * 0x1000) + (addr & 0x0fff);
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::mmio_joyp_poll() {
|
void CPU::mmio_joyp_poll() {
|
||||||
unsigned button = 0, dpad = 0;
|
unsigned button = 0, dpad = 0;
|
||||||
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
|
button |= interface->inputPoll((unsigned)Input::Start) << 3;
|
||||||
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
|
button |= interface->inputPoll((unsigned)Input::Select) << 2;
|
||||||
button |= system.interface->input_poll((unsigned)Input::B) << 1;
|
button |= interface->inputPoll((unsigned)Input::B) << 1;
|
||||||
button |= system.interface->input_poll((unsigned)Input::A) << 0;
|
button |= interface->inputPoll((unsigned)Input::A) << 0;
|
||||||
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
|
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
|
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
|
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
|
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
|
||||||
|
|
||||||
status.joyp = 0x0f;
|
status.joyp = 0x0f;
|
||||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||||
@@ -21,8 +28,7 @@ void CPU::mmio_joyp_poll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::mmio_read(uint16 addr) {
|
uint8 CPU::mmio_read(uint16 addr) {
|
||||||
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
|
if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)];
|
||||||
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
|
|
||||||
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
||||||
|
|
||||||
if(addr == 0xff00) { //JOYP
|
if(addr == 0xff00) { //JOYP
|
||||||
@@ -65,6 +71,50 @@ uint8 CPU::mmio_read(uint16 addr) {
|
|||||||
| (status.interrupt_request_vblank << 0);
|
| (status.interrupt_request_vblank << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff4d) { //KEY1
|
||||||
|
return (status.speed_double << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff55) { //HDMA5
|
||||||
|
return (status.dma_length / 16) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff56) { //RP
|
||||||
|
return 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff6c) { //???
|
||||||
|
return 0xfe | status.ff6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff70) { //SVBK
|
||||||
|
return status.wram_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff72) { //???
|
||||||
|
return status.ff72;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff73) { //???
|
||||||
|
return status.ff73;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff74) { //???
|
||||||
|
return status.ff74;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff75) { //???
|
||||||
|
return 0x8f | status.ff75;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff76) { //???
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff77) { //???
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
if(addr == 0xffff) { //IE
|
if(addr == 0xffff) { //IE
|
||||||
return (status.interrupt_enable_joypad << 4)
|
return (status.interrupt_enable_joypad << 4)
|
||||||
| (status.interrupt_enable_serial << 3)
|
| (status.interrupt_enable_serial << 3)
|
||||||
@@ -77,14 +127,13 @@ uint8 CPU::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::mmio_write(uint16 addr, uint8 data) {
|
void CPU::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
|
if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; }
|
||||||
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
|
|
||||||
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
|
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
|
||||||
|
|
||||||
if(addr == 0xff00) { //JOYP
|
if(addr == 0xff00) { //JOYP
|
||||||
status.p15 = data & 0x20;
|
status.p15 = data & 0x20;
|
||||||
status.p14 = data & 0x10;
|
status.p14 = data & 0x10;
|
||||||
system.interface->joyp_write(status.p15, status.p14);
|
interface->joypWrite(status.p15, status.p14);
|
||||||
mmio_joyp_poll();
|
mmio_joyp_poll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -131,6 +180,84 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff46) { //DMA
|
||||||
|
for(unsigned n = 0x00; n <= 0x9f; n++) {
|
||||||
|
bus.write(0xfe00 + n, bus.read((data << 8) + n));
|
||||||
|
add_clocks(4);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff4d) { //KEY1
|
||||||
|
status.speed_switch = data & 0x01;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff51) { //HDMA1
|
||||||
|
status.dma_source = (status.dma_source & 0x00ff) | (data << 8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff52) { //HDMA2
|
||||||
|
status.dma_source = (status.dma_source & 0xff00) | (data << 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff53) { //HDMA3
|
||||||
|
status.dma_target = (status.dma_target & 0x00ff) | (data << 8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff54) { //HDMA4
|
||||||
|
status.dma_target = (status.dma_target & 0xff00) | (data << 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff55) { //HDMA5
|
||||||
|
status.dma_mode = data & 0x80;
|
||||||
|
status.dma_length = ((data & 0x7f) + 1) * 16;
|
||||||
|
|
||||||
|
if(status.dma_mode == 0) do {
|
||||||
|
bus.write(status.dma_target++, bus.read(status.dma_source++));
|
||||||
|
add_clocks(4 << status.speed_double);
|
||||||
|
} while(--status.dma_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff56) { //RP
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff6c) { //???
|
||||||
|
status.ff6c = data & 0x01;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff72) { //???
|
||||||
|
status.ff72 = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff73) { //???
|
||||||
|
status.ff73 = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff74) { //???
|
||||||
|
status.ff74 = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff75) { //???
|
||||||
|
status.ff75 = data & 0x70;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff70) { //SVBK
|
||||||
|
status.wram_bank = data & 0x07;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(addr == 0xffff) { //IE
|
if(addr == 0xffff) { //IE
|
||||||
status.interrupt_enable_joypad = data & 0x10;
|
status.interrupt_enable_joypad = data & 0x10;
|
||||||
status.interrupt_enable_serial = data & 0x08;
|
status.interrupt_enable_serial = data & 0x08;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
unsigned wram_addr(uint16 addr) const;
|
||||||
void mmio_joyp_poll();
|
void mmio_joyp_poll();
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
#ifdef CPU_CPP
|
#ifdef CPU_CPP
|
||||||
|
|
||||||
void CPU::serialize(serializer &s) {
|
void CPU::serialize(serializer &s) {
|
||||||
|
Processor::serialize(s);
|
||||||
|
|
||||||
s.array(wram);
|
s.array(wram);
|
||||||
s.array(hram);
|
s.array(hram);
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ void CPU::serialize(serializer &s) {
|
|||||||
s.integer(status.clock);
|
s.integer(status.clock);
|
||||||
s.integer(status.halt);
|
s.integer(status.halt);
|
||||||
s.integer(status.stop);
|
s.integer(status.stop);
|
||||||
|
s.integer(status.ei);
|
||||||
s.integer(status.ime);
|
s.integer(status.ime);
|
||||||
|
|
||||||
s.integer(status.p15);
|
s.integer(status.p15);
|
||||||
@@ -46,6 +49,23 @@ void CPU::serialize(serializer &s) {
|
|||||||
s.integer(status.interrupt_request_stat);
|
s.integer(status.interrupt_request_stat);
|
||||||
s.integer(status.interrupt_request_vblank);
|
s.integer(status.interrupt_request_vblank);
|
||||||
|
|
||||||
|
s.integer(status.speed_double);
|
||||||
|
s.integer(status.speed_switch);
|
||||||
|
|
||||||
|
s.integer(status.dma_source);
|
||||||
|
s.integer(status.dma_target);
|
||||||
|
s.integer(status.dma_mode);
|
||||||
|
s.integer(status.dma_length);
|
||||||
|
|
||||||
|
s.integer(status.ff6c);
|
||||||
|
|
||||||
|
s.integer(status.wram_bank);
|
||||||
|
|
||||||
|
s.integer(status.ff72);
|
||||||
|
s.integer(status.ff73);
|
||||||
|
s.integer(status.ff74);
|
||||||
|
s.integer(status.ff75);
|
||||||
|
|
||||||
s.integer(status.interrupt_enable_joypad);
|
s.integer(status.interrupt_enable_joypad);
|
||||||
s.integer(status.interrupt_enable_serial);
|
s.integer(status.interrupt_enable_serial);
|
||||||
s.integer(status.interrupt_enable_timer);
|
s.integer(status.interrupt_enable_timer);
|
||||||
|
@@ -1,18 +1,28 @@
|
|||||||
#ifdef CPU_CPP
|
#ifdef CPU_CPP
|
||||||
|
|
||||||
void CPU::op_io() {
|
void CPU::op_io() {
|
||||||
|
cycle_edge();
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::op_read(uint16 addr) {
|
uint8 CPU::op_read(uint16 addr) {
|
||||||
|
cycle_edge();
|
||||||
uint8 r = bus.read(addr);
|
uint8 r = bus.read(addr);
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::op_write(uint16 addr, uint8 data) {
|
void CPU::op_write(uint16 addr, uint8 data) {
|
||||||
|
cycle_edge();
|
||||||
bus.write(addr, data);
|
bus.write(addr, data);
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::cycle_edge() {
|
||||||
|
if(status.ei) {
|
||||||
|
status.ei = false;
|
||||||
|
status.ime = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
//4194304hz (4 * 1024 * 1024)
|
|
||||||
|
|
||||||
//70224 clocks/frame
|
//70224 clocks/frame
|
||||||
// 456 clocks/scanline
|
// 456 clocks/scanline
|
||||||
// 154 scanlines/frame
|
// 154 scanlines/frame
|
||||||
@@ -10,25 +8,25 @@
|
|||||||
|
|
||||||
void CPU::add_clocks(unsigned clocks) {
|
void CPU::add_clocks(unsigned clocks) {
|
||||||
system.clocks_executed += clocks;
|
system.clocks_executed += clocks;
|
||||||
scheduler.exit(Scheduler::ExitReason::StepEvent);
|
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
|
||||||
|
|
||||||
status.clock += clocks;
|
status.clock += clocks;
|
||||||
if(status.clock >= 4194304) {
|
if(status.clock >= 4 * 1024 * 1024) {
|
||||||
status.clock -= 4194304;
|
status.clock -= 4 * 1024 * 1024;
|
||||||
cartridge.mbc3.second();
|
cartridge.mbc3.second();
|
||||||
}
|
}
|
||||||
|
|
||||||
//4194304 / N(hz) - 1 = mask
|
//4MHz / N(hz) - 1 = mask
|
||||||
if((status.clock & 15) == 0) timer_262144hz();
|
if((status.clock & 15) == 0) timer_262144hz();
|
||||||
if((status.clock & 63) == 0) timer_65536hz();
|
if((status.clock & 63) == 0) timer_65536hz();
|
||||||
if((status.clock & 255) == 0) timer_16384hz();
|
if((status.clock & 255) == 0) timer_16384hz();
|
||||||
if((status.clock & 511) == 0) timer_8192hz();
|
if((status.clock & 511) == 0) timer_8192hz();
|
||||||
if((status.clock & 1023) == 0) timer_4096hz();
|
if((status.clock & 1023) == 0) timer_4096hz();
|
||||||
|
|
||||||
lcd.clock -= clocks;
|
lcd.clock -= clocks * lcd.frequency;
|
||||||
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
|
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
|
||||||
|
|
||||||
apu.clock -= clocks;
|
apu.clock -= clocks * apu.frequency;
|
||||||
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
|
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,4 +77,14 @@ void CPU::timer_4096hz() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::hblank() {
|
||||||
|
if(status.dma_mode == 1 && status.dma_length) {
|
||||||
|
for(unsigned n = 0; n < 16; n++) {
|
||||||
|
bus.write(status.dma_target++, bus.read(status.dma_source++));
|
||||||
|
add_clocks(4);
|
||||||
|
}
|
||||||
|
status.dma_length -= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,8 +4,10 @@ void timer_65536hz();
|
|||||||
void timer_16384hz();
|
void timer_16384hz();
|
||||||
void timer_8192hz();
|
void timer_8192hz();
|
||||||
void timer_4096hz();
|
void timer_4096hz();
|
||||||
|
void hblank();
|
||||||
|
|
||||||
//opcode.cpp
|
//opcode.cpp
|
||||||
void op_io();
|
void op_io();
|
||||||
uint8 op_read(uint16 addr);
|
uint8 op_read(uint16 addr);
|
||||||
void op_write(uint16 addr, uint8 data);
|
void op_write(uint16 addr, uint8 data);
|
||||||
|
void cycle_edge();
|
||||||
|
@@ -1,18 +1,22 @@
|
|||||||
//bgameboy
|
#ifndef GAMEBOY_HPP
|
||||||
//author: byuu
|
#define GAMEBOY_HPP
|
||||||
//project started: 2010-12-27
|
|
||||||
|
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bgameboy";
|
static const char Name[] = "bgameboy";
|
||||||
static const char Version[] = "000.19";
|
static const unsigned SerializerVersion = 3;
|
||||||
static unsigned SerializerVersion = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator
|
||||||
|
author: byuu
|
||||||
|
license: GPLv3
|
||||||
|
project started: 2010-12-27
|
||||||
|
*/
|
||||||
|
|
||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
|
|
||||||
#include <nall/foreach.hpp>
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/property.hpp>
|
#include <nall/property.hpp>
|
||||||
#include <nall/random.hpp>
|
#include <nall/random.hpp>
|
||||||
@@ -65,25 +69,29 @@ namespace GameBoy {
|
|||||||
typedef uint_t<30> uint30;
|
typedef uint_t<30> uint30;
|
||||||
typedef uint_t<31> uint31;
|
typedef uint_t<31> uint31;
|
||||||
|
|
||||||
template<uint16 lo, uint16 hi>
|
|
||||||
alwaysinline bool within(uint16 addr) {
|
|
||||||
static const uint16 mask = ~(hi ^ lo);
|
|
||||||
return (addr & mask) == lo;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Processor {
|
struct Processor {
|
||||||
cothread_t thread;
|
cothread_t thread;
|
||||||
unsigned frequency;
|
unsigned frequency;
|
||||||
int64 clock;
|
int64 clock;
|
||||||
|
|
||||||
inline void create(void (*entrypoint_)(), unsigned frequency_) {
|
inline void create(void (*entrypoint)(), unsigned frequency) {
|
||||||
if(thread) co_delete(thread);
|
if(thread) co_delete(thread);
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint_);
|
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||||
frequency = frequency_;
|
this->frequency = frequency;
|
||||||
clock = 0;
|
clock = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Processor() : thread(0) {}
|
inline void serialize(serializer &s) {
|
||||||
|
s.integer(frequency);
|
||||||
|
s.integer(clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Processor() : thread(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~Processor() {
|
||||||
|
if(thread) co_delete(thread);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <gameboy/memory/memory.hpp>
|
#include <gameboy/memory/memory.hpp>
|
||||||
@@ -93,4 +101,8 @@ namespace GameBoy {
|
|||||||
#include <gameboy/cpu/cpu.hpp>
|
#include <gameboy/cpu/cpu.hpp>
|
||||||
#include <gameboy/apu/apu.hpp>
|
#include <gameboy/apu/apu.hpp>
|
||||||
#include <gameboy/lcd/lcd.hpp>
|
#include <gameboy/lcd/lcd.hpp>
|
||||||
|
#include <gameboy/cheat/cheat.hpp>
|
||||||
|
#include <gameboy/video/video.hpp>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
27
bsnes/gameboy/interface/interface.cpp
Executable file
27
bsnes/gameboy/interface/interface.cpp
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <gameboy/gameboy.hpp>
|
||||||
|
|
||||||
|
namespace GameBoy {
|
||||||
|
|
||||||
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
|
void Interface::lcdScanline() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::joypWrite(bool p15, bool p14) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::videoRefresh(const uint16_t *data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interface::inputPoll(unsigned id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface::message(const string &text) {
|
||||||
|
print(text, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
class Interface {
|
struct Interface {
|
||||||
public:
|
virtual void lcdScanline();
|
||||||
virtual void lcd_scanline() {}
|
virtual void joypWrite(bool p15, bool p14);
|
||||||
virtual void joyp_write(bool p15, bool p14) {}
|
|
||||||
|
|
||||||
virtual void video_refresh(const uint8_t *data) {}
|
virtual void videoRefresh(const uint16_t *data);
|
||||||
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
|
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
virtual void input_poll() {}
|
virtual bool inputPoll(unsigned id);
|
||||||
virtual bool input_poll(unsigned id) {}
|
|
||||||
|
|
||||||
virtual void message(const string &text) { print(text, "\n"); }
|
virtual void message(const string &text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern Interface *interface;
|
||||||
|
185
bsnes/gameboy/lcd/cgb.cpp
Executable file
185
bsnes/gameboy/lcd/cgb.cpp
Executable file
@@ -0,0 +1,185 @@
|
|||||||
|
#ifdef LCD_CPP
|
||||||
|
|
||||||
|
void LCD::cgb_render() {
|
||||||
|
for(unsigned n = 0; n < 160; n++) {
|
||||||
|
line[n] = 0x7fff;
|
||||||
|
origin[n] = Origin::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status.display_enable) {
|
||||||
|
cgb_render_bg();
|
||||||
|
if(status.window_display_enable) cgb_render_window();
|
||||||
|
if(status.ob_enable) cgb_render_ob();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 *output = screen + status.ly * 160;
|
||||||
|
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
||||||
|
interface->lcdScanline();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attributes:
|
||||||
|
//0x80: 0 = OAM priority, 1 = BG priority
|
||||||
|
//0x40: vertical flip
|
||||||
|
//0x20: horizontal flip
|
||||||
|
//0x08: VRAM bank#
|
||||||
|
//0x07: palette#
|
||||||
|
void LCD::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data) {
|
||||||
|
unsigned tmaddr = 0x1800 + (select << 10);
|
||||||
|
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||||
|
|
||||||
|
tile = vram[0x0000 + tmaddr];
|
||||||
|
attr = vram[0x2000 + tmaddr];
|
||||||
|
|
||||||
|
unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000;
|
||||||
|
if(status.bg_tiledata_select == 0) {
|
||||||
|
tdaddr += 0x1000 + ((int8)tile << 4);
|
||||||
|
} else {
|
||||||
|
tdaddr += 0x0000 + (tile << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
y &= 7;
|
||||||
|
if(attr & 0x40) y ^= 7;
|
||||||
|
tdaddr += y << 1;
|
||||||
|
|
||||||
|
data = vram[tdaddr++] << 0;
|
||||||
|
data |= vram[tdaddr++] << 8;
|
||||||
|
if(attr & 0x20) data = hflip(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::cgb_render_bg() {
|
||||||
|
unsigned iy = (status.ly + status.scy) & 255;
|
||||||
|
unsigned ix = status.scx, tx = ix & 7;
|
||||||
|
|
||||||
|
unsigned tile, attr, data;
|
||||||
|
cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
|
||||||
|
|
||||||
|
for(unsigned ox = 0; ox < 160; ox++) {
|
||||||
|
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||||
|
unsigned palette = 0;
|
||||||
|
palette |= bgpd[palette_index++] << 0;
|
||||||
|
palette |= bgpd[palette_index++] << 8;
|
||||||
|
palette &= 0x7fff;
|
||||||
|
|
||||||
|
line[ox] = palette;
|
||||||
|
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
|
||||||
|
|
||||||
|
ix = (ix + 1) & 255;
|
||||||
|
tx = (tx + 1) & 7;
|
||||||
|
if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::cgb_render_window() {
|
||||||
|
if(status.ly - status.wy >= 144u) return;
|
||||||
|
if(status.wx >= 167u) return;
|
||||||
|
unsigned iy = status.wyc++;
|
||||||
|
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||||
|
|
||||||
|
unsigned tile, attr, data;
|
||||||
|
cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
|
||||||
|
|
||||||
|
for(unsigned ox = 0; ox < 160; ox++) {
|
||||||
|
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||||
|
unsigned palette = 0;
|
||||||
|
palette |= bgpd[palette_index++] << 0;
|
||||||
|
palette |= bgpd[palette_index++] << 8;
|
||||||
|
palette &= 0x7fff;
|
||||||
|
|
||||||
|
if(ox - (status.wx - 7) < 160u) {
|
||||||
|
line[ox] = palette;
|
||||||
|
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
|
||||||
|
}
|
||||||
|
|
||||||
|
ix = (ix + 1) & 255;
|
||||||
|
tx = (tx + 1) & 7;
|
||||||
|
if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attributes:
|
||||||
|
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||||
|
//0x40: vertical flip
|
||||||
|
//0x20: horizontal flip
|
||||||
|
//0x08: VRAM bank#
|
||||||
|
//0x07: palette#
|
||||||
|
void LCD::cgb_render_ob() {
|
||||||
|
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
|
||||||
|
unsigned sprite[10], sprites = 0;
|
||||||
|
|
||||||
|
//find first ten sprites on this scanline
|
||||||
|
for(unsigned s = 0; s < 40; s++) {
|
||||||
|
unsigned sy = oam[(s << 2) + 0] - 16;
|
||||||
|
unsigned sx = oam[(s << 2) + 1] - 8;
|
||||||
|
|
||||||
|
sy = status.ly - sy;
|
||||||
|
if(sy >= Height) continue;
|
||||||
|
|
||||||
|
sprite[sprites++] = s;
|
||||||
|
if(sprites == 10) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort by X-coordinate, when equal, lower address comes first
|
||||||
|
for(unsigned x = 0; x < sprites; x++) {
|
||||||
|
for(unsigned y = x + 1; y < sprites; y++) {
|
||||||
|
signed sx = oam[(sprite[x] << 2) + 1] - 8;
|
||||||
|
signed sy = oam[(sprite[y] << 2) + 1] - 8;
|
||||||
|
if(sy < sx) {
|
||||||
|
sprite[x] ^= sprite[y];
|
||||||
|
sprite[y] ^= sprite[x];
|
||||||
|
sprite[x] ^= sprite[y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//render backwards, so that first sprite has highest priority
|
||||||
|
for(signed s = sprites - 1; s >= 0; s--) {
|
||||||
|
unsigned n = sprite[s] << 2;
|
||||||
|
unsigned sy = oam[n + 0] - 16;
|
||||||
|
unsigned sx = oam[n + 1] - 8;
|
||||||
|
unsigned tile = oam[n + 2] & ~status.ob_size;
|
||||||
|
unsigned attr = oam[n + 3];
|
||||||
|
|
||||||
|
sy = status.ly - sy;
|
||||||
|
if(sy >= Height) continue;
|
||||||
|
if(attr & 0x40) sy ^= (Height - 1);
|
||||||
|
|
||||||
|
unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0;
|
||||||
|
data |= vram[tdaddr++] << 0;
|
||||||
|
data |= vram[tdaddr++] << 8;
|
||||||
|
if(attr & 0x20) data = hflip(data);
|
||||||
|
|
||||||
|
for(unsigned tx = 0; tx < 8; tx++) {
|
||||||
|
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
if(index == 0) continue;
|
||||||
|
|
||||||
|
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
|
||||||
|
unsigned palette = 0;
|
||||||
|
palette |= obpd[palette_index++] << 0;
|
||||||
|
palette |= obpd[palette_index++] << 8;
|
||||||
|
palette &= 0x7fff;
|
||||||
|
|
||||||
|
unsigned ox = sx + tx;
|
||||||
|
|
||||||
|
if(ox < 160) {
|
||||||
|
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
|
||||||
|
if(status.bg_enable) {
|
||||||
|
if(origin[ox] == Origin::BGP) continue;
|
||||||
|
if(attr & 0x80) {
|
||||||
|
if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) {
|
||||||
|
if(line[ox] > 0) continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line[ox] = palette;
|
||||||
|
origin[ox] = Origin::OB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
145
bsnes/gameboy/lcd/dmg.cpp
Executable file
145
bsnes/gameboy/lcd/dmg.cpp
Executable file
@@ -0,0 +1,145 @@
|
|||||||
|
#ifdef LCD_CPP
|
||||||
|
|
||||||
|
void LCD::dmg_render() {
|
||||||
|
for(unsigned n = 0; n < 160; n++) {
|
||||||
|
line[n] = 0x00;
|
||||||
|
origin[n] = Origin::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status.display_enable) {
|
||||||
|
if(status.bg_enable) dmg_render_bg();
|
||||||
|
if(status.window_display_enable) dmg_render_window();
|
||||||
|
if(status.ob_enable) dmg_render_ob();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 *output = screen + status.ly * 160;
|
||||||
|
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
||||||
|
interface->lcdScanline();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 LCD::dmg_read_tile(bool select, unsigned x, unsigned y) {
|
||||||
|
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
|
||||||
|
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||||
|
if(status.bg_tiledata_select == 0) {
|
||||||
|
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
|
||||||
|
} else {
|
||||||
|
tdaddr = 0x0000 + (vram[tmaddr] << 4);
|
||||||
|
}
|
||||||
|
tdaddr += (y & 7) << 1;
|
||||||
|
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::dmg_render_bg() {
|
||||||
|
unsigned iy = (status.ly + status.scy) & 255;
|
||||||
|
unsigned ix = status.scx, tx = ix & 7;
|
||||||
|
unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||||
|
|
||||||
|
for(unsigned ox = 0; ox < 160; ox++) {
|
||||||
|
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
|
||||||
|
line[ox] = bgp[palette];
|
||||||
|
origin[ox] = Origin::BG;
|
||||||
|
|
||||||
|
ix = (ix + 1) & 255;
|
||||||
|
tx = (tx + 1) & 7;
|
||||||
|
|
||||||
|
if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::dmg_render_window() {
|
||||||
|
if(status.ly - status.wy >= 144u) return;
|
||||||
|
if(status.wx >= 167u) return;
|
||||||
|
unsigned iy = status.wyc++;
|
||||||
|
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||||
|
unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||||
|
|
||||||
|
for(unsigned ox = 0; ox < 160; ox++) {
|
||||||
|
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
if(ox - (status.wx - 7) < 160u) {
|
||||||
|
line[ox] = bgp[palette];
|
||||||
|
origin[ox] = Origin::BG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ix = (ix + 1) & 255;
|
||||||
|
tx = (tx + 1) & 7;
|
||||||
|
|
||||||
|
if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attributes:
|
||||||
|
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||||
|
//0x40: vertical flip
|
||||||
|
//0x20: horizontal flip
|
||||||
|
//0x10: palette#
|
||||||
|
void LCD::dmg_render_ob() {
|
||||||
|
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
|
||||||
|
unsigned sprite[10], sprites = 0;
|
||||||
|
|
||||||
|
//find first ten sprites on this scanline
|
||||||
|
for(unsigned s = 0; s < 40; s++) {
|
||||||
|
unsigned sy = oam[(s << 2) + 0] - 16;
|
||||||
|
unsigned sx = oam[(s << 2) + 1] - 8;
|
||||||
|
|
||||||
|
sy = status.ly - sy;
|
||||||
|
if(sy >= Height) continue;
|
||||||
|
|
||||||
|
sprite[sprites++] = s;
|
||||||
|
if(sprites == 10) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort by X-coordinate, when equal, lower address comes first
|
||||||
|
for(unsigned x = 0; x < sprites; x++) {
|
||||||
|
for(unsigned y = x + 1; y < sprites; y++) {
|
||||||
|
signed sx = oam[(sprite[x] << 2) + 1] - 8;
|
||||||
|
signed sy = oam[(sprite[y] << 2) + 1] - 8;
|
||||||
|
if(sy < sx) {
|
||||||
|
sprite[x] ^= sprite[y];
|
||||||
|
sprite[y] ^= sprite[x];
|
||||||
|
sprite[x] ^= sprite[y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//render backwards, so that first sprite has highest priority
|
||||||
|
for(signed s = sprites - 1; s >= 0; s--) {
|
||||||
|
unsigned n = sprite[s] << 2;
|
||||||
|
unsigned sy = oam[n + 0] - 16;
|
||||||
|
unsigned sx = oam[n + 1] - 8;
|
||||||
|
unsigned tile = oam[n + 2] & ~status.ob_size;
|
||||||
|
unsigned attr = oam[n + 3];
|
||||||
|
|
||||||
|
sy = status.ly - sy;
|
||||||
|
if(sy >= Height) continue;
|
||||||
|
if(attr & 0x40) sy ^= (Height - 1);
|
||||||
|
|
||||||
|
unsigned tdaddr = (tile << 4) + (sy << 1), data = 0;
|
||||||
|
data |= vram[tdaddr++] << 0;
|
||||||
|
data |= vram[tdaddr++] << 8;
|
||||||
|
if(attr & 0x20) data = hflip(data);
|
||||||
|
|
||||||
|
for(unsigned tx = 0; tx < 8; tx++) {
|
||||||
|
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||||
|
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||||
|
if(palette == 0) continue;
|
||||||
|
|
||||||
|
palette = obp[(bool)(attr & 0x10)][palette];
|
||||||
|
unsigned ox = sx + tx;
|
||||||
|
|
||||||
|
if(ox < 160) {
|
||||||
|
if(attr & 0x80) {
|
||||||
|
if(origin[ox] == Origin::BG) {
|
||||||
|
if(line[ox] > 0) continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line[ox] = palette;
|
||||||
|
origin[ox] = Origin::OB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -1,8 +1,16 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gameboy/gameboy.hpp>
|
||||||
|
|
||||||
|
//LY = 0-153
|
||||||
|
//Raster = 0-143
|
||||||
|
//Vblank = 144-153
|
||||||
|
|
||||||
|
//LX = 0-455
|
||||||
|
|
||||||
#define LCD_CPP
|
#define LCD_CPP
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
|
|
||||||
|
#include "dmg.cpp"
|
||||||
|
#include "cgb.cpp"
|
||||||
#include "mmio/mmio.cpp"
|
#include "mmio/mmio.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
LCD lcd;
|
LCD lcd;
|
||||||
@@ -18,22 +26,22 @@ void LCD::main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
|
status.lx += 4;
|
||||||
|
if(status.lx >= 456) scanline();
|
||||||
|
|
||||||
if(status.lx == 0) {
|
if(status.display_enable && status.lx == 0) {
|
||||||
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.lx == 252) {
|
if(status.display_enable && status.lx == 252) {
|
||||||
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
|
cpu.hblank();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCD::add_clocks(unsigned clocks) {
|
void LCD::add_clocks(unsigned clocks) {
|
||||||
status.lx += clocks;
|
clock += clocks * cpu.frequency;
|
||||||
if(status.lx >= 456) scanline();
|
|
||||||
|
|
||||||
clock += clocks;
|
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||||
co_switch(scheduler.active_thread = cpu.thread);
|
co_switch(scheduler.active_thread = cpu.thread);
|
||||||
}
|
}
|
||||||
@@ -43,174 +51,84 @@ void LCD::scanline() {
|
|||||||
status.lx -= 456;
|
status.lx -= 456;
|
||||||
if(++status.ly == 154) frame();
|
if(++status.ly == 154) frame();
|
||||||
|
|
||||||
if(status.interrupt_lyc == true) {
|
if(status.display_enable && status.interrupt_lyc == true) {
|
||||||
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status.ly < 144) render();
|
if(status.ly < 144) {
|
||||||
|
system.cgb() == false ? dmg_render() : cgb_render();
|
||||||
|
}
|
||||||
|
|
||||||
if(status.ly == 144) {
|
if(status.display_enable && status.ly == 144) {
|
||||||
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
||||||
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCD::frame() {
|
void LCD::frame() {
|
||||||
system.interface->video_refresh(screen);
|
interface->videoRefresh(screen);
|
||||||
system.interface->input_poll();
|
|
||||||
cpu.mmio_joyp_poll();
|
cpu.mmio_joyp_poll();
|
||||||
|
|
||||||
status.ly = 0;
|
status.ly = 0;
|
||||||
|
status.wyc = 0;
|
||||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCD::render() {
|
unsigned LCD::hflip(unsigned data) const {
|
||||||
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
|
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
|
||||||
|
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
|
||||||
if(status.display_enable == true) {
|
| ((data & 0x0808) << 1) | ((data & 0x0404) << 3)
|
||||||
if(status.bg_enable == true) render_bg();
|
| ((data & 0x0202) << 5) | ((data & 0x0101) << 7);
|
||||||
if(status.window_display_enable == true) render_window();
|
|
||||||
if(status.obj_enable == true) render_obj();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *output = screen + status.ly * 160;
|
|
||||||
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
|
|
||||||
system.interface->lcd_scanline();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
|
|
||||||
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
|
|
||||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
|
||||||
if(status.bg_tiledata_select == 0) {
|
|
||||||
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
|
|
||||||
} else {
|
|
||||||
tdaddr = 0x0000 + (vram[tmaddr] << 4);
|
|
||||||
}
|
|
||||||
tdaddr += (y & 7) << 1;
|
|
||||||
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::render_bg() {
|
|
||||||
unsigned iy = (status.ly + status.scy) & 255;
|
|
||||||
unsigned ix = status.scx, tx = ix & 7;
|
|
||||||
unsigned data = read_tile(status.bg_tilemap_select, ix, iy);
|
|
||||||
|
|
||||||
for(unsigned ox = 0; ox < 160; ox++) {
|
|
||||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
|
||||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
|
||||||
line[ox] = status.bgp[palette];
|
|
||||||
|
|
||||||
ix = (ix + 1) & 255;
|
|
||||||
tx = (tx + 1) & 7;
|
|
||||||
|
|
||||||
if(tx == 0) data = read_tile(status.bg_tilemap_select, ix, iy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::render_window() {
|
|
||||||
if(status.ly - status.wy >= 144U) return;
|
|
||||||
unsigned iy = status.ly - status.wy;
|
|
||||||
unsigned ix = (status.wx - 7) & 255, tx = ix & 7;
|
|
||||||
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
|
|
||||||
|
|
||||||
for(unsigned ox = 0; ox < 160; ox++) {
|
|
||||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
|
||||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
|
||||||
if(ox - (status.wx - 7) < 160U) line[ox] = status.bgp[palette];
|
|
||||||
|
|
||||||
ix = (ix + 1) & 255;
|
|
||||||
tx = (tx + 1) & 7;
|
|
||||||
|
|
||||||
if(tx == 0) data = read_tile(status.window_tilemap_select, ix, iy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::render_obj() {
|
|
||||||
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
|
|
||||||
|
|
||||||
unsigned sprite[10], sprites = 0;
|
|
||||||
|
|
||||||
//find first ten sprites on this scanline
|
|
||||||
for(unsigned s = 0; s < 40; s++) {
|
|
||||||
unsigned sy = oam[(s << 2) + 0] - 16;
|
|
||||||
unsigned sx = oam[(s << 2) + 1] - 8;
|
|
||||||
|
|
||||||
sy = status.ly - sy;
|
|
||||||
if(sy >= obj_size) continue;
|
|
||||||
|
|
||||||
sprite[sprites++] = s;
|
|
||||||
if(sprites == 10) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//sort by X-coordinate, when equal, lower address comes first
|
|
||||||
for(unsigned x = 0; x < sprites; x++) {
|
|
||||||
for(unsigned y = x + 1; y < sprites; y++) {
|
|
||||||
signed sx = oam[(sprite[x] << 2) + 1] - 8;
|
|
||||||
signed sy = oam[(sprite[y] << 2) + 1] - 8;
|
|
||||||
if(sy < sx) {
|
|
||||||
sprite[x] ^= sprite[y];
|
|
||||||
sprite[y] ^= sprite[x];
|
|
||||||
sprite[x] ^= sprite[y];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//render backwards, so that first sprite has highest priority
|
|
||||||
for(signed s = sprites - 1; s >= 0; s--) {
|
|
||||||
unsigned n = sprite[s] << 2;
|
|
||||||
unsigned sy = oam[n + 0] - 16;
|
|
||||||
unsigned sx = oam[n + 1] - 8;
|
|
||||||
unsigned tile = oam[n + 2];
|
|
||||||
unsigned attribute = oam[n + 3];
|
|
||||||
|
|
||||||
sy = status.ly - sy;
|
|
||||||
if(sy >= obj_size) continue;
|
|
||||||
if(attribute & 0x40) sy ^= (obj_size - 1);
|
|
||||||
|
|
||||||
unsigned tdaddr = (tile << 4) + (sy << 1);
|
|
||||||
uint8 d0 = vram[tdaddr + 0];
|
|
||||||
uint8 d1 = vram[tdaddr + 1];
|
|
||||||
unsigned xflip = attribute & 0x20 ? 7 : 0;
|
|
||||||
|
|
||||||
for(unsigned tx = 0; tx < 8; tx++) {
|
|
||||||
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
|
|
||||||
| ((d1 & (0x80 >> tx)) ? 2 : 0);
|
|
||||||
if(palette == 0) continue;
|
|
||||||
|
|
||||||
palette = status.obp[(bool)(attribute & 0x10)][palette];
|
|
||||||
unsigned ox = sx + (tx ^ xflip);
|
|
||||||
|
|
||||||
if(ox <= 159) {
|
|
||||||
if(attribute & 0x80) {
|
|
||||||
if(line[ox] > 0) continue;
|
|
||||||
}
|
|
||||||
line[ox] = palette;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCD::power() {
|
void LCD::power() {
|
||||||
create(Main, 4194304);
|
create(Main, 4 * 1024 * 1024);
|
||||||
|
|
||||||
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
||||||
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
|
|
||||||
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
|
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
|
||||||
|
|
||||||
for(unsigned n = 0; n < 8192; n++) vram[n] = 0x00;
|
bus.mmio[0xff40] = this; //LCDC
|
||||||
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
|
bus.mmio[0xff41] = this; //STAT
|
||||||
|
bus.mmio[0xff42] = this; //SCY
|
||||||
|
bus.mmio[0xff43] = this; //SCX
|
||||||
|
bus.mmio[0xff44] = this; //LY
|
||||||
|
bus.mmio[0xff45] = this; //LYC
|
||||||
|
bus.mmio[0xff47] = this; //BGP
|
||||||
|
bus.mmio[0xff48] = this; //OBP0
|
||||||
|
bus.mmio[0xff49] = this; //OBP1
|
||||||
|
bus.mmio[0xff4a] = this; //WY
|
||||||
|
bus.mmio[0xff4b] = this; //WX
|
||||||
|
|
||||||
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
|
if(system.cgb()) {
|
||||||
|
bus.mmio[0xff4f] = this; //VBK
|
||||||
|
bus.mmio[0xff68] = this; //BGPI
|
||||||
|
bus.mmio[0xff69] = this; //BGPD
|
||||||
|
bus.mmio[0xff6a] = this; //OBPI
|
||||||
|
bus.mmio[0xff6b] = this; //OBPD
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto &n : screen) n = 0x0000;
|
||||||
|
for(auto &n : line) n = 0x0000;
|
||||||
|
for(auto &n : origin) n = Origin::None;
|
||||||
|
|
||||||
|
for(auto &n : vram) n = 0x00;
|
||||||
|
for(auto &n : oam) n = 0x00;
|
||||||
|
for(auto &n : bgp) n = 0x00;
|
||||||
|
for(auto &n : obp[0]) n = 0x00;
|
||||||
|
for(auto &n : obp[1]) n = 0x00;
|
||||||
|
for(auto &n : bgpd) n = 0x0000;
|
||||||
|
for(auto &n : obpd) n = 0x0000;
|
||||||
|
|
||||||
status.lx = 0;
|
status.lx = 0;
|
||||||
|
status.wyc = 0;
|
||||||
|
|
||||||
status.display_enable = 0;
|
status.display_enable = 0;
|
||||||
status.window_tilemap_select = 0;
|
status.window_tilemap_select = 0;
|
||||||
status.window_display_enable = 0;
|
status.window_display_enable = 0;
|
||||||
status.bg_tiledata_select = 0;
|
status.bg_tiledata_select = 0;
|
||||||
status.bg_tilemap_select = 0;
|
status.bg_tilemap_select = 0;
|
||||||
status.obj_size = 0;
|
status.ob_size = 0;
|
||||||
status.obj_enable = 0;
|
status.ob_enable = 0;
|
||||||
status.bg_enable = 0;
|
status.bg_enable = 0;
|
||||||
|
|
||||||
status.interrupt_lyc = 0;
|
status.interrupt_lyc = 0;
|
||||||
@@ -222,15 +140,16 @@ void LCD::power() {
|
|||||||
status.scx = 0;
|
status.scx = 0;
|
||||||
status.ly = 0;
|
status.ly = 0;
|
||||||
status.lyc = 0;
|
status.lyc = 0;
|
||||||
|
|
||||||
for(unsigned n = 0; n < 4; n++) {
|
|
||||||
status.bgp[n] = n;
|
|
||||||
status.obp[0][n] = n;
|
|
||||||
status.obp[1][n] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.wy = 0;
|
status.wy = 0;
|
||||||
status.wx = 0;
|
status.wx = 0;
|
||||||
|
|
||||||
|
status.vram_bank = 0;
|
||||||
|
|
||||||
|
status.bgpi_increment = 0;
|
||||||
|
status.bgpi = 0;
|
||||||
|
|
||||||
|
status.obpi_increment = 0;
|
||||||
|
status.obpi = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LCD::LCD() {
|
LCD::LCD() {
|
||||||
|
@@ -3,6 +3,7 @@ struct LCD : Processor, MMIO {
|
|||||||
|
|
||||||
struct Status {
|
struct Status {
|
||||||
unsigned lx;
|
unsigned lx;
|
||||||
|
unsigned wyc;
|
||||||
|
|
||||||
//$ff40 LCDC
|
//$ff40 LCDC
|
||||||
bool display_enable;
|
bool display_enable;
|
||||||
@@ -10,8 +11,8 @@ struct LCD : Processor, MMIO {
|
|||||||
bool window_display_enable;
|
bool window_display_enable;
|
||||||
bool bg_tiledata_select;
|
bool bg_tiledata_select;
|
||||||
bool bg_tilemap_select;
|
bool bg_tilemap_select;
|
||||||
bool obj_size;
|
bool ob_size;
|
||||||
bool obj_enable;
|
bool ob_enable;
|
||||||
bool bg_enable;
|
bool bg_enable;
|
||||||
|
|
||||||
//$ff41 STAT
|
//$ff41 STAT
|
||||||
@@ -32,35 +33,57 @@ struct LCD : Processor, MMIO {
|
|||||||
//$ff45 LYC
|
//$ff45 LYC
|
||||||
uint8 lyc;
|
uint8 lyc;
|
||||||
|
|
||||||
//$ff47 BGP
|
|
||||||
uint8 bgp[4];
|
|
||||||
|
|
||||||
//$ff48 OBP0
|
|
||||||
//$ff49 OBP1
|
|
||||||
uint8 obp[2][4];
|
|
||||||
|
|
||||||
//$ff4a WY
|
//$ff4a WY
|
||||||
uint8 wy;
|
uint8 wy;
|
||||||
|
|
||||||
//$ff4b WX
|
//$ff4b WX
|
||||||
uint8 wx;
|
uint8 wx;
|
||||||
|
|
||||||
|
//$ff4f VBK
|
||||||
|
bool vram_bank;
|
||||||
|
|
||||||
|
//$ff68 BGPI
|
||||||
|
bool bgpi_increment;
|
||||||
|
uint6 bgpi;
|
||||||
|
|
||||||
|
//$ff6a OBPI
|
||||||
|
bool obpi_increment;
|
||||||
|
uint8 obpi;
|
||||||
} status;
|
} status;
|
||||||
|
|
||||||
uint8 screen[160 * 144];
|
uint16 screen[160 * 144];
|
||||||
uint8 vram[8192];
|
uint16 line[160];
|
||||||
|
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
|
||||||
|
uint8 origin[160];
|
||||||
|
|
||||||
|
uint8 vram[16384]; //GB = 8192, GBC = 16384
|
||||||
uint8 oam[160];
|
uint8 oam[160];
|
||||||
uint8 line[160];
|
uint8 bgp[4];
|
||||||
|
uint8 obp[2][4];
|
||||||
|
uint8 bgpd[64];
|
||||||
|
uint8 obpd[64];
|
||||||
|
|
||||||
static void Main();
|
static void Main();
|
||||||
void main();
|
void main();
|
||||||
void add_clocks(unsigned clocks);
|
void add_clocks(unsigned clocks);
|
||||||
void scanline();
|
void scanline();
|
||||||
void frame();
|
void frame();
|
||||||
void render();
|
|
||||||
uint16 read_tile(bool select, unsigned x, unsigned y);
|
unsigned hflip(unsigned data) const;
|
||||||
void render_bg();
|
|
||||||
void render_window();
|
//dmg.cpp
|
||||||
void render_obj();
|
void dmg_render();
|
||||||
|
uint16 dmg_read_tile(bool select, unsigned x, unsigned y);
|
||||||
|
void dmg_render_bg();
|
||||||
|
void dmg_render_window();
|
||||||
|
void dmg_render_ob();
|
||||||
|
|
||||||
|
//cgb.cpp
|
||||||
|
void cgb_render();
|
||||||
|
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data);
|
||||||
|
void cgb_render_bg();
|
||||||
|
void cgb_render_window();
|
||||||
|
void cgb_render_ob();
|
||||||
|
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
#ifdef LCD_CPP
|
#ifdef LCD_CPP
|
||||||
|
|
||||||
|
unsigned LCD::vram_addr(uint16 addr) const {
|
||||||
|
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
|
||||||
|
}
|
||||||
|
|
||||||
uint8 LCD::mmio_read(uint16 addr) {
|
uint8 LCD::mmio_read(uint16 addr) {
|
||||||
if(addr >= 0x8000 && addr <= 0x9fff) return vram[addr & 0x1fff];
|
if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)];
|
||||||
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
|
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
|
||||||
|
|
||||||
if(addr == 0xff40) { //LCDC
|
if(addr == 0xff40) { //LCDC
|
||||||
@@ -10,17 +14,17 @@ uint8 LCD::mmio_read(uint16 addr) {
|
|||||||
| (status.window_display_enable << 5)
|
| (status.window_display_enable << 5)
|
||||||
| (status.bg_tiledata_select << 4)
|
| (status.bg_tiledata_select << 4)
|
||||||
| (status.bg_tilemap_select << 3)
|
| (status.bg_tilemap_select << 3)
|
||||||
| (status.obj_size << 2)
|
| (status.ob_size << 2)
|
||||||
| (status.obj_enable << 1)
|
| (status.ob_enable << 1)
|
||||||
| (status.bg_enable << 0);
|
| (status.bg_enable << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff41) { //STAT
|
if(addr == 0xff41) { //STAT
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
if(status.ly >= 144) mode = 1; //Vblank
|
if(status.ly >= 144) mode = 1; //Vblank
|
||||||
else if(status.lx < 80) mode = 2; //OAM
|
else if(status.lx < 80) mode = 2; //OAM
|
||||||
else if(status.lx < 252) mode = 3; //LCD
|
else if(status.lx < 252) mode = 3; //LCD
|
||||||
else mode = 0; //Hblank
|
else mode = 0; //Hblank
|
||||||
|
|
||||||
return (status.interrupt_lyc << 6)
|
return (status.interrupt_lyc << 6)
|
||||||
| (status.interrupt_oam << 5)
|
| (status.interrupt_oam << 5)
|
||||||
@@ -47,24 +51,24 @@ uint8 LCD::mmio_read(uint16 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff47) { //BGP
|
if(addr == 0xff47) { //BGP
|
||||||
return (status.bgp[3] << 6)
|
return (bgp[3] << 6)
|
||||||
| (status.bgp[2] << 4)
|
| (bgp[2] << 4)
|
||||||
| (status.bgp[1] << 2)
|
| (bgp[1] << 2)
|
||||||
| (status.bgp[0] << 0);
|
| (bgp[0] << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff48) { //OBP0
|
if(addr == 0xff48) { //OBP0
|
||||||
return (status.obp[0][3] << 6)
|
return (obp[0][3] << 6)
|
||||||
| (status.obp[0][2] << 4)
|
| (obp[0][2] << 4)
|
||||||
| (status.obp[0][1] << 2)
|
| (obp[0][1] << 2)
|
||||||
| (status.obp[0][0] << 0);
|
| (obp[0][0] << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff49) { //OBP1
|
if(addr == 0xff49) { //OBP1
|
||||||
return (status.obp[1][3] << 6)
|
return (obp[1][3] << 6)
|
||||||
| (status.obp[1][2] << 4)
|
| (obp[1][2] << 4)
|
||||||
| (status.obp[1][1] << 2)
|
| (obp[1][1] << 2)
|
||||||
| (status.obp[1][0] << 0);
|
| (obp[1][0] << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff4a) { //WY
|
if(addr == 0xff4a) { //WY
|
||||||
@@ -75,21 +79,33 @@ uint8 LCD::mmio_read(uint16 addr) {
|
|||||||
return status.wx;
|
return status.wx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff69) { //BGPD
|
||||||
|
return bgpd[status.bgpi];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff6b) { //OBPD
|
||||||
|
return obpd[status.obpi];
|
||||||
|
}
|
||||||
|
|
||||||
return 0x00;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LCD::mmio_write(uint16 addr, uint8 data) {
|
void LCD::mmio_write(uint16 addr, uint8 data) {
|
||||||
if(addr >= 0x8000 && addr <= 0x9fff) { vram[addr & 0x1fff] = data; return; }
|
if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; }
|
||||||
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
|
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
|
||||||
|
|
||||||
if(addr == 0xff40) { //LCDC
|
if(addr == 0xff40) { //LCDC
|
||||||
|
if(status.display_enable == false && (data & 0x80)) {
|
||||||
|
status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone
|
||||||
|
}
|
||||||
|
|
||||||
status.display_enable = data & 0x80;
|
status.display_enable = data & 0x80;
|
||||||
status.window_tilemap_select = data & 0x40;
|
status.window_tilemap_select = data & 0x40;
|
||||||
status.window_display_enable = data & 0x20;
|
status.window_display_enable = data & 0x20;
|
||||||
status.bg_tiledata_select = data & 0x10;
|
status.bg_tiledata_select = data & 0x10;
|
||||||
status.bg_tilemap_select = data & 0x08;
|
status.bg_tilemap_select = data & 0x08;
|
||||||
status.obj_size = data & 0x04;
|
status.ob_size = data & 0x04;
|
||||||
status.obj_enable = data & 0x02;
|
status.ob_enable = data & 0x02;
|
||||||
status.bg_enable = data & 0x01;
|
status.bg_enable = data & 0x01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -122,32 +138,27 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff46) { //DMA
|
|
||||||
for(unsigned n = 0x00; n <= 0x9f; n++) bus.write(0xfe00 + n, bus.read((data << 8) + n));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff47) { //BGP
|
if(addr == 0xff47) { //BGP
|
||||||
status.bgp[3] = (data >> 6) & 3;
|
bgp[3] = (data >> 6) & 3;
|
||||||
status.bgp[2] = (data >> 4) & 3;
|
bgp[2] = (data >> 4) & 3;
|
||||||
status.bgp[1] = (data >> 2) & 3;
|
bgp[1] = (data >> 2) & 3;
|
||||||
status.bgp[0] = (data >> 0) & 3;
|
bgp[0] = (data >> 0) & 3;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff48) { //OBP0
|
if(addr == 0xff48) { //OBP0
|
||||||
status.obp[0][3] = (data >> 6) & 3;
|
obp[0][3] = (data >> 6) & 3;
|
||||||
status.obp[0][2] = (data >> 4) & 3;
|
obp[0][2] = (data >> 4) & 3;
|
||||||
status.obp[0][1] = (data >> 2) & 3;
|
obp[0][1] = (data >> 2) & 3;
|
||||||
status.obp[0][0] = (data >> 0) & 3;
|
obp[0][0] = (data >> 0) & 3;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0xff49) { //OBP1
|
if(addr == 0xff49) { //OBP1
|
||||||
status.obp[1][3] = (data >> 6) & 3;
|
obp[1][3] = (data >> 6) & 3;
|
||||||
status.obp[1][2] = (data >> 4) & 3;
|
obp[1][2] = (data >> 4) & 3;
|
||||||
status.obp[1][1] = (data >> 2) & 3;
|
obp[1][1] = (data >> 2) & 3;
|
||||||
status.obp[1][0] = (data >> 0) & 3;
|
obp[1][0] = (data >> 0) & 3;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +171,33 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
|
|||||||
status.wx = data;
|
status.wx = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff4f) { //VBK
|
||||||
|
status.vram_bank = data & 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff68) { //BGPI
|
||||||
|
status.bgpi_increment = data & 0x80;
|
||||||
|
status.bgpi = data & 0x3f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff69) { //BGPD
|
||||||
|
bgpd[status.bgpi] = data;
|
||||||
|
if(status.bgpi_increment) status.bgpi++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff6a) { //OBPI
|
||||||
|
status.obpi_increment = data & 0x80;
|
||||||
|
status.obpi = data & 0x3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff6b) { //OBPD
|
||||||
|
obpd[status.obpi] = data;
|
||||||
|
if(status.obpi_increment) status.obpi++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
|
unsigned vram_addr(uint16 addr) const;
|
||||||
uint8 mmio_read(uint16 addr);
|
uint8 mmio_read(uint16 addr);
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
@@ -1,15 +1,30 @@
|
|||||||
#ifdef LCD_CPP
|
#ifdef LCD_CPP
|
||||||
|
|
||||||
void LCD::serialize(serializer &s) {
|
void LCD::serialize(serializer &s) {
|
||||||
|
Processor::serialize(s);
|
||||||
|
|
||||||
|
s.array(screen);
|
||||||
|
s.array(line);
|
||||||
|
s.array(origin);
|
||||||
|
|
||||||
|
s.array(vram);
|
||||||
|
s.array(oam);
|
||||||
|
s.array(bgp);
|
||||||
|
s.array(obp[0]);
|
||||||
|
s.array(obp[1]);
|
||||||
|
s.array(bgpd);
|
||||||
|
s.array(obpd);
|
||||||
|
|
||||||
s.integer(status.lx);
|
s.integer(status.lx);
|
||||||
|
s.integer(status.wyc);
|
||||||
|
|
||||||
s.integer(status.display_enable);
|
s.integer(status.display_enable);
|
||||||
s.integer(status.window_tilemap_select);
|
s.integer(status.window_tilemap_select);
|
||||||
s.integer(status.window_display_enable);
|
s.integer(status.window_display_enable);
|
||||||
s.integer(status.bg_tiledata_select);
|
s.integer(status.bg_tiledata_select);
|
||||||
s.integer(status.bg_tilemap_select);
|
s.integer(status.bg_tilemap_select);
|
||||||
s.integer(status.obj_size);
|
s.integer(status.ob_size);
|
||||||
s.integer(status.obj_enable);
|
s.integer(status.ob_enable);
|
||||||
s.integer(status.bg_enable);
|
s.integer(status.bg_enable);
|
||||||
|
|
||||||
s.integer(status.interrupt_lyc);
|
s.integer(status.interrupt_lyc);
|
||||||
@@ -19,20 +34,20 @@ void LCD::serialize(serializer &s) {
|
|||||||
|
|
||||||
s.integer(status.scy);
|
s.integer(status.scy);
|
||||||
s.integer(status.scx);
|
s.integer(status.scx);
|
||||||
|
|
||||||
s.integer(status.ly);
|
s.integer(status.ly);
|
||||||
s.integer(status.lyc);
|
s.integer(status.lyc);
|
||||||
|
|
||||||
s.array(status.bgp);
|
|
||||||
s.array(status.obp[0]);
|
|
||||||
s.array(status.obp[1]);
|
|
||||||
|
|
||||||
s.integer(status.wy);
|
s.integer(status.wy);
|
||||||
s.integer(status.wx);
|
s.integer(status.wx);
|
||||||
|
|
||||||
s.array(screen);
|
s.integer(status.vram_bank);
|
||||||
s.array(vram);
|
|
||||||
s.array(oam);
|
s.integer(status.bgpi_increment);
|
||||||
s.array(line);
|
s.integer(status.bgpi);
|
||||||
|
|
||||||
|
s.integer(status.obpi_increment);
|
||||||
|
s.integer(status.obpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -42,7 +42,20 @@ Memory::~Memory() {
|
|||||||
//
|
//
|
||||||
|
|
||||||
uint8 Bus::read(uint16 addr) {
|
uint8 Bus::read(uint16 addr) {
|
||||||
return mmio[addr]->mmio_read(addr);
|
uint8 data = mmio[addr]->mmio_read(addr);
|
||||||
|
|
||||||
|
if(cheat.override[addr]) {
|
||||||
|
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||||
|
if(cheat[n].addr == addr) {
|
||||||
|
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||||
|
data = cheat[n].data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::write(uint16 addr, uint8 data) {
|
void Bus::write(uint16 addr, uint8 data) {
|
||||||
|
135
bsnes/gameboy/system/bootrom-cgb.cpp
Executable file
135
bsnes/gameboy/system/bootrom-cgb.cpp
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
#ifdef SYSTEM_CPP
|
||||||
|
|
||||||
|
//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9
|
||||||
|
const uint8_t System::BootROM::cgb[2048] = {
|
||||||
|
0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80,
|
||||||
|
0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e,
|
||||||
|
0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03,
|
||||||
|
0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00,
|
||||||
|
0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,
|
||||||
|
0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,
|
||||||
|
0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,
|
||||||
|
0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc,
|
||||||
|
0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe,
|
||||||
|
0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2,
|
||||||
|
0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00,
|
||||||
|
0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f,
|
||||||
|
0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0,
|
||||||
|
0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20,
|
||||||
|
0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd,
|
||||||
|
0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50,
|
||||||
|
0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa,
|
||||||
|
0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff,
|
||||||
|
0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37,
|
||||||
|
0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9,
|
||||||
|
0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a,
|
||||||
|
0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20,
|
||||||
|
0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a,
|
||||||
|
0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0,
|
||||||
|
0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb,
|
||||||
|
0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f,
|
||||||
|
0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1,
|
||||||
|
0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79,
|
||||||
|
0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21,
|
||||||
|
0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c,
|
||||||
|
0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19,
|
||||||
|
0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b,
|
||||||
|
0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02,
|
||||||
|
0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a,
|
||||||
|
0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0,
|
||||||
|
0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08,
|
||||||
|
0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8,
|
||||||
|
0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6,
|
||||||
|
0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f,
|
||||||
|
0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe,
|
||||||
|
0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e,
|
||||||
|
0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a,
|
||||||
|
0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41,
|
||||||
|
0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c,
|
||||||
|
0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13,
|
||||||
|
0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21,
|
||||||
|
0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9,
|
||||||
|
0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a,
|
||||||
|
0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8,
|
||||||
|
0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a,
|
||||||
|
0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d,
|
||||||
|
0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e,
|
||||||
|
0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99,
|
||||||
|
0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13,
|
||||||
|
0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05,
|
||||||
|
0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e,
|
||||||
|
0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e,
|
||||||
|
0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7,
|
||||||
|
0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79,
|
||||||
|
0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e,
|
||||||
|
0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00,
|
||||||
|
0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07,
|
||||||
|
0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa,
|
||||||
|
0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b,
|
||||||
|
0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb,
|
||||||
|
0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00,
|
||||||
|
0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07,
|
||||||
|
0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13,
|
||||||
|
0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a,
|
||||||
|
0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02,
|
||||||
|
0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09,
|
||||||
|
0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18,
|
||||||
|
0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28,
|
||||||
|
0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6,
|
||||||
|
0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56,
|
||||||
|
0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07,
|
||||||
|
0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9,
|
||||||
|
0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04,
|
||||||
|
0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00,
|
||||||
|
0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8,
|
||||||
|
0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0,
|
||||||
|
0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e,
|
||||||
|
0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff,
|
||||||
|
0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0,
|
||||||
|
0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07,
|
||||||
|
0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe,
|
||||||
|
0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff,
|
||||||
|
0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f,
|
||||||
|
0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c,
|
||||||
|
0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f,
|
||||||
|
0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81,
|
||||||
|
0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e,
|
||||||
|
0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c,
|
||||||
|
0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75,
|
||||||
|
0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49,
|
||||||
|
0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d,
|
||||||
|
0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61,
|
||||||
|
0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45,
|
||||||
|
0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43,
|
||||||
|
0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16,
|
||||||
|
0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b,
|
||||||
|
0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86,
|
||||||
|
0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf,
|
||||||
|
0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d,
|
||||||
|
0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64,
|
||||||
|
0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38,
|
||||||
|
0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e,
|
||||||
|
0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18,
|
||||||
|
0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0,
|
||||||
|
0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0,
|
||||||
|
0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00,
|
||||||
|
0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00,
|
||||||
|
0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00,
|
||||||
|
0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00,
|
||||||
|
0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00,
|
||||||
|
0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d,
|
||||||
|
0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00,
|
||||||
|
0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00,
|
||||||
|
0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00,
|
||||||
|
0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00,
|
||||||
|
0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00,
|
||||||
|
0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c,
|
||||||
|
0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00,
|
||||||
|
0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00,
|
||||||
|
0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c,
|
||||||
|
0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12,
|
||||||
|
0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@@ -1,6 +1,6 @@
|
|||||||
#ifdef SYSTEM_CPP
|
#ifdef SYSTEM_CPP
|
||||||
|
|
||||||
//MD5SUM = 32fbbd84168d3482956eb3c5051637f5
|
//SHA256 = cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7
|
||||||
const uint8_t System::BootROM::dmg[256] = {
|
const uint8_t System::BootROM::dmg[256] = {
|
||||||
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
|
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
|
||||||
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,
|
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#ifdef SYSTEM_CPP
|
#ifdef SYSTEM_CPP
|
||||||
|
|
||||||
//MD5SUM = d574d4f9c12f305074798f54c091a8b4
|
//SHA256 = 0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360
|
||||||
const uint8_t System::BootROM::sgb[256] = {
|
const uint8_t System::BootROM::sgb[256] = {
|
||||||
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
|
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
|
||||||
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,
|
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,
|
||||||
|
@@ -29,6 +29,7 @@ bool System::unserialize(serializer &s) {
|
|||||||
if(version != Info::SerializerVersion) return false;
|
if(version != Info::SerializerVersion) return false;
|
||||||
//if(crc32 != 0) return false;
|
//if(crc32 != 0) return false;
|
||||||
|
|
||||||
|
power();
|
||||||
serialize_all(s);
|
serialize_all(s);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ namespace GameBoy {
|
|||||||
|
|
||||||
#include "bootrom-dmg.cpp"
|
#include "bootrom-dmg.cpp"
|
||||||
#include "bootrom-sgb.cpp"
|
#include "bootrom-sgb.cpp"
|
||||||
|
#include "bootrom-cgb.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
System system;
|
System system;
|
||||||
|
|
||||||
@@ -33,11 +34,12 @@ void System::runthreadtosave() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Interface *interface_) {
|
void System::init() {
|
||||||
interface = interface_;
|
assert(interface != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load(Revision revision) {
|
||||||
|
this->revision = revision;
|
||||||
serialize_init();
|
serialize_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,21 +4,31 @@ enum class Input : unsigned {
|
|||||||
Up, Down, Left, Right, B, A, Select, Start,
|
Up, Down, Left, Right, B, A, Select, Start,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct System {
|
struct System : property<System> {
|
||||||
|
enum class Revision : unsigned {
|
||||||
|
GameBoy,
|
||||||
|
SuperGameBoy,
|
||||||
|
GameBoyColor,
|
||||||
|
};
|
||||||
|
readonly<Revision> revision;
|
||||||
|
inline bool dmg() const { return revision == Revision::GameBoy; }
|
||||||
|
inline bool sgb() const { return revision == Revision::SuperGameBoy; }
|
||||||
|
inline bool cgb() const { return revision == Revision::GameBoyColor; }
|
||||||
|
|
||||||
struct BootROM {
|
struct BootROM {
|
||||||
static const uint8 dmg[256];
|
static const uint8 dmg[ 256];
|
||||||
static const uint8 sgb[256];
|
static const uint8 sgb[ 256];
|
||||||
|
static const uint8 cgb[2048];
|
||||||
} bootROM;
|
} bootROM;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void runtosave();
|
void runtosave();
|
||||||
void runthreadtosave();
|
void runthreadtosave();
|
||||||
|
|
||||||
void init(Interface*);
|
void init();
|
||||||
void load();
|
void load(Revision);
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
Interface *interface;
|
|
||||||
unsigned clocks_executed;
|
unsigned clocks_executed;
|
||||||
|
|
||||||
//serialization.cpp
|
//serialization.cpp
|
||||||
|
82
bsnes/gameboy/video/video.cpp
Executable file
82
bsnes/gameboy/video/video.cpp
Executable file
@@ -0,0 +1,82 @@
|
|||||||
|
#include <gameboy/gameboy.hpp>
|
||||||
|
|
||||||
|
#define VIDEO_CPP
|
||||||
|
namespace GameBoy {
|
||||||
|
|
||||||
|
Video video;
|
||||||
|
|
||||||
|
unsigned Video::palette_dmg(unsigned color) const {
|
||||||
|
unsigned R = monochrome[color][0] * 1023.0;
|
||||||
|
unsigned G = monochrome[color][1] * 1023.0;
|
||||||
|
unsigned B = monochrome[color][2] * 1023.0;
|
||||||
|
|
||||||
|
return (R << 20) + (G << 10) + (B << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Video::palette_sgb(unsigned color) const {
|
||||||
|
unsigned R = (3 - color) * 341;
|
||||||
|
unsigned G = (3 - color) * 341;
|
||||||
|
unsigned B = (3 - color) * 341;
|
||||||
|
|
||||||
|
return (R << 20) + (G << 10) + (B << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Video::palette_cgb(unsigned color) const {
|
||||||
|
unsigned r = (color >> 0) & 31;
|
||||||
|
unsigned g = (color >> 5) & 31;
|
||||||
|
unsigned b = (color >> 10) & 31;
|
||||||
|
|
||||||
|
unsigned R = (r * 26 + g * 4 + b * 2);
|
||||||
|
unsigned G = ( g * 24 + b * 8);
|
||||||
|
unsigned B = (r * 6 + g * 4 + b * 22);
|
||||||
|
|
||||||
|
R = min(960, R);
|
||||||
|
G = min(960, G);
|
||||||
|
B = min(960, B);
|
||||||
|
|
||||||
|
return (R << 20) + (G << 10) + (B << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Video::generate(Format format) {
|
||||||
|
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
|
||||||
|
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
|
||||||
|
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
|
||||||
|
|
||||||
|
if(format == Format::RGB24) {
|
||||||
|
for(unsigned n = 0; n < (1 << 15); n++) {
|
||||||
|
unsigned color = palette[n];
|
||||||
|
palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(format == Format::RGB16) {
|
||||||
|
for(unsigned n = 0; n < (1 << 15); n++) {
|
||||||
|
unsigned color = palette[n];
|
||||||
|
palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(format == Format::RGB15) {
|
||||||
|
for(unsigned n = 0; n < (1 << 15); n++) {
|
||||||
|
unsigned color = palette[n];
|
||||||
|
palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Video::Video() {
|
||||||
|
palette = new unsigned[1 << 15];
|
||||||
|
}
|
||||||
|
|
||||||
|
Video::~Video() {
|
||||||
|
delete[] palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double Video::monochrome[4][3] = {
|
||||||
|
{ 0.605, 0.734, 0.059 },
|
||||||
|
{ 0.543, 0.672, 0.059 },
|
||||||
|
{ 0.188, 0.383, 0.188 },
|
||||||
|
{ 0.059, 0.219, 0.059 },
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
17
bsnes/gameboy/video/video.hpp
Executable file
17
bsnes/gameboy/video/video.hpp
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
struct Video {
|
||||||
|
enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 };
|
||||||
|
unsigned *palette;
|
||||||
|
|
||||||
|
unsigned palette_dmg(unsigned color) const;
|
||||||
|
unsigned palette_sgb(unsigned color) const;
|
||||||
|
unsigned palette_cgb(unsigned color) const;
|
||||||
|
|
||||||
|
void generate(Format format);
|
||||||
|
Video();
|
||||||
|
~Video();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const double monochrome[4][3];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Video video;
|
@@ -19,6 +19,9 @@ ifeq ($(platform),)
|
|||||||
ifeq ($(uname),)
|
ifeq ($(uname),)
|
||||||
platform := win
|
platform := win
|
||||||
delete = del $(subst /,\,$1)
|
delete = del $(subst /,\,$1)
|
||||||
|
else ifneq ($(findstring CYGWIN,$(uname)),)
|
||||||
|
platform := win
|
||||||
|
delete = del $(subst /,\,$1)
|
||||||
else ifneq ($(findstring Darwin,$(uname)),)
|
else ifneq ($(findstring Darwin,$(uname)),)
|
||||||
platform := osx
|
platform := osx
|
||||||
delete = rm -f $1
|
delete = rm -f $1
|
||||||
@@ -32,12 +35,15 @@ ifeq ($(compiler),)
|
|||||||
ifeq ($(platform),win)
|
ifeq ($(platform),win)
|
||||||
compiler := gcc
|
compiler := gcc
|
||||||
else ifeq ($(platform),osx)
|
else ifeq ($(platform),osx)
|
||||||
compiler := gcc-mp-4.5
|
compiler := gcc-mp-4.6
|
||||||
else
|
else
|
||||||
compiler := gcc-4.5
|
compiler := gcc-4.6
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
c := $(compiler) -std=gnu99
|
||||||
|
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||||
|
|
||||||
ifeq ($(prefix),)
|
ifeq ($(prefix),)
|
||||||
prefix := /usr/local
|
prefix := /usr/local
|
||||||
endif
|
endif
|
||||||
|
@@ -2,13 +2,12 @@
|
|||||||
#define NALL_ARRAY_HPP
|
#define NALL_ARRAY_HPP
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
#include <nall/bit.hpp>
|
#include <nall/bit.hpp>
|
||||||
#include <nall/concept.hpp>
|
|
||||||
#include <nall/foreach.hpp>
|
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
@@ -26,7 +25,7 @@ namespace nall {
|
|||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
if(pool) free(pool);
|
if(pool) free(pool);
|
||||||
pool = 0;
|
pool = nullptr;
|
||||||
poolsize = 0;
|
poolsize = 0;
|
||||||
buffersize = 0;
|
buffersize = 0;
|
||||||
}
|
}
|
||||||
@@ -54,15 +53,12 @@ namespace nall {
|
|||||||
operator[](buffersize) = data;
|
operator[](buffersize) = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename U> void insert(unsigned index, const U list) {
|
void append(const T data[], unsigned length) {
|
||||||
unsigned listsize = container_size(list);
|
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
|
||||||
resize(buffersize + listsize);
|
|
||||||
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
|
|
||||||
foreach(item, list) pool[index++] = item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(unsigned index, const T item) {
|
void remove() {
|
||||||
insert(index, array<T>{ item });
|
if(size > 0) resize(size - 1); //remove last element only
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(unsigned index, unsigned count = 1) {
|
void remove(unsigned index, unsigned count = 1) {
|
||||||
@@ -82,10 +78,10 @@ namespace nall {
|
|||||||
memset(pool, 0, buffersize * sizeof(T));
|
memset(pool, 0, buffersize * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
array() : pool(0), poolsize(0), buffersize(0) {
|
array() : pool(nullptr), poolsize(0), buffersize(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
array(std::initializer_list<T> list) : pool(0), poolsize(0), buffersize(0) {
|
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +99,7 @@ namespace nall {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
array(const array &source) : pool(0), poolsize(0), buffersize(0) {
|
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||||
operator=(source);
|
operator=(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,29 +109,38 @@ namespace nall {
|
|||||||
pool = source.pool;
|
pool = source.pool;
|
||||||
poolsize = source.poolsize;
|
poolsize = source.poolsize;
|
||||||
buffersize = source.buffersize;
|
buffersize = source.buffersize;
|
||||||
source.pool = 0;
|
source.pool = nullptr;
|
||||||
source.reset();
|
source.reset();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
|
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||||
operator=(std::move(source));
|
operator=(std::move(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
//index
|
//access
|
||||||
inline T& operator[](unsigned index) {
|
inline T& operator[](unsigned position) {
|
||||||
if(index >= buffersize) resize(index + 1);
|
if(position >= buffersize) resize(position + 1);
|
||||||
if(index >= buffersize) throw "array[] out of bounds";
|
if(position >= buffersize) throw "array[] out of bounds";
|
||||||
return pool[index];
|
return pool[position];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const T& operator[](unsigned index) const {
|
inline const T& operator[](unsigned position) const {
|
||||||
if(index >= buffersize) throw "array[] out of bounds";
|
if(position >= buffersize) throw "array[] out of bounds";
|
||||||
return pool[index];
|
return pool[position];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const T& operator()(unsigned position, const T& data) {
|
||||||
|
if(position >= buffersize) return data;
|
||||||
|
return pool[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
//iteration
|
||||||
|
T* begin() { return &pool[0]; }
|
||||||
|
T* end() { return &pool[buffersize]; }
|
||||||
|
const T* begin() const { return &pool[0]; }
|
||||||
|
const T* end() const { return &pool[buffersize]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> struct has_size<array<T>> { enum { value = true }; };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
88
bsnes/nall/atoi.hpp
Executable file
88
bsnes/nall/atoi.hpp
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#ifndef NALL_ATOI_HPP
|
||||||
|
#define NALL_ATOI_HPP
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//note: this header is intended to form the base for user-defined literals;
|
||||||
|
//once they are supported by GCC. eg:
|
||||||
|
//unsigned operator "" b(const char *s) { return binary(s); }
|
||||||
|
//-> signed data = 1001b;
|
||||||
|
//(0b1001 is nicer, but is not part of the C++ standard)
|
||||||
|
|
||||||
|
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
|
||||||
|
return (
|
||||||
|
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
|
||||||
|
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
|
||||||
|
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
|
||||||
|
sum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
constexpr inline uintmax_t binary(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||||
|
*s == '%' ? binary_(s + 1) :
|
||||||
|
binary_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t octal(const char *s) {
|
||||||
|
return (
|
||||||
|
octal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline intmax_t integer(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '+' ? +decimal_(s + 1) :
|
||||||
|
*s == '-' ? -decimal_(s + 1) :
|
||||||
|
decimal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t decimal(const char *s) {
|
||||||
|
return (
|
||||||
|
decimal_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uintmax_t hex(const char *s) {
|
||||||
|
return (
|
||||||
|
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||||
|
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||||
|
*s == '$' ? hex_(s + 1) :
|
||||||
|
hex_(s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double fp(const char *s) {
|
||||||
|
return atof(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -5,8 +5,7 @@
|
|||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
class base64 {
|
struct base64 {
|
||||||
public:
|
|
||||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||||
output = new char[inlength * 8 / 6 + 6]();
|
output = new char[inlength * 8 / 6 + 6]();
|
||||||
|
|
||||||
@@ -72,6 +71,7 @@ namespace nall {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static char enc(uint8_t n) {
|
static char enc(uint8_t n) {
|
||||||
|
//base64 for URL encodings
|
||||||
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
return lookup_table[n & 63];
|
return lookup_table[n & 63];
|
||||||
}
|
}
|
||||||
|
@@ -2,39 +2,39 @@
|
|||||||
#define NALL_BIT_HPP
|
#define NALL_BIT_HPP
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
template<int bits> inline unsigned uclamp(const unsigned x) {
|
template<int bits> constexpr inline unsigned uclamp(const unsigned x) {
|
||||||
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||||
return y + ((x - y) & -(x < y)); //min(x, y);
|
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int bits> inline unsigned uclip(const unsigned x) {
|
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
|
||||||
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||||
return (x & m);
|
return (x & m);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int bits> inline signed sclamp(const signed x) {
|
template<int bits> constexpr inline signed sclamp(const signed x) {
|
||||||
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
|
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
|
||||||
return (x > m) ? m : (x < -b) ? -b : x;
|
return (x > m) ? m : (x < -b) ? -b : x;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int bits> inline signed sclip(const signed x) {
|
template<int bits> constexpr inline signed sclip(const signed x) {
|
||||||
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
|
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
|
||||||
return ((x & m) ^ b) - b;
|
return ((x & m) ^ b) - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace bit {
|
namespace bit {
|
||||||
//lowest(0b1110) == 0b0010
|
//lowest(0b1110) == 0b0010
|
||||||
template<typename T> inline T lowest(const T x) {
|
template<typename T> constexpr inline T lowest(const T x) {
|
||||||
return x & -x;
|
return x & -x;
|
||||||
}
|
}
|
||||||
|
|
||||||
//clear_lowest(0b1110) == 0b1100
|
//clear_lowest(0b1110) == 0b1100
|
||||||
template<typename T> inline T clear_lowest(const T x) {
|
template<typename T> constexpr inline T clear_lowest(const T x) {
|
||||||
return x & (x - 1);
|
return x & (x - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//set_lowest(0b0101) == 0b0111
|
//set_lowest(0b0101) == 0b0111
|
||||||
template<typename T> inline T set_lowest(const T x) {
|
template<typename T> constexpr inline T set_lowest(const T x) {
|
||||||
return x | (x + 1);
|
return x | (x + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
bsnes/nall/bmp.hpp
Executable file
101
bsnes/nall/bmp.hpp
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef NALL_BMP_HPP
|
||||||
|
#define NALL_BMP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
|
||||||
|
//BMP reader / writer
|
||||||
|
//author: byuu
|
||||||
|
//note: only 24-bit RGB and 32-bit ARGB uncompressed images supported
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bmp {
|
||||||
|
inline static bool read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height);
|
||||||
|
inline static bool write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bmp::read(const string &filename, uint32_t *&data, unsigned &width, unsigned &height) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::read) == false) return false;
|
||||||
|
if(fp.size() < 0x36) return false;
|
||||||
|
|
||||||
|
if(fp.readm(2) != 0x424d) return false;
|
||||||
|
fp.seek(0x000a);
|
||||||
|
unsigned offset = fp.readl(4);
|
||||||
|
unsigned dibsize = fp.readl(4);
|
||||||
|
if(dibsize != 40) return false;
|
||||||
|
signed headerWidth = fp.readl(4);
|
||||||
|
if(headerWidth < 0) return false;
|
||||||
|
signed headerHeight = fp.readl(4);
|
||||||
|
fp.readl(2);
|
||||||
|
unsigned bitsPerPixel = fp.readl(2);
|
||||||
|
if(bitsPerPixel != 24 && bitsPerPixel != 32) return false;
|
||||||
|
unsigned compression = fp.readl(4);
|
||||||
|
if(compression != 0) return false;
|
||||||
|
fp.seek(offset);
|
||||||
|
|
||||||
|
bool noFlip = headerHeight < 0;
|
||||||
|
width = headerWidth, height = abs(headerHeight);
|
||||||
|
data = new uint32_t[width * height];
|
||||||
|
|
||||||
|
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||||
|
unsigned alignedWidth = width * bytesPerPixel;
|
||||||
|
unsigned paddingLength = 0;
|
||||||
|
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
uint32_t *p = noFlip ? data + y * width : data + (height - 1 - y) * width;
|
||||||
|
for(unsigned x = 0; x < width; x++, p++) {
|
||||||
|
*p = fp.readl(bytesPerPixel);
|
||||||
|
if(bytesPerPixel == 3) *p |= 255 << 24;
|
||||||
|
}
|
||||||
|
if(paddingLength) fp.readl(paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bmp::write(const string &filename, const uint32_t *data, unsigned width, unsigned height, unsigned pitch, bool alpha) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::write) == false) return false;
|
||||||
|
|
||||||
|
unsigned bitsPerPixel = alpha ? 32 : 24;
|
||||||
|
unsigned bytesPerPixel = bitsPerPixel / 8;
|
||||||
|
unsigned alignedWidth = width * bytesPerPixel;
|
||||||
|
unsigned paddingLength = 0;
|
||||||
|
unsigned imageSize = alignedWidth * height;
|
||||||
|
unsigned fileSize = 0x36 + imageSize;
|
||||||
|
while(alignedWidth % 4) alignedWidth++, paddingLength++;
|
||||||
|
|
||||||
|
fp.writem(0x424d, 2); //signature
|
||||||
|
fp.writel(fileSize, 4); //file size
|
||||||
|
fp.writel(0, 2); //reserved
|
||||||
|
fp.writel(0, 2); //reserved
|
||||||
|
fp.writel(0x36, 4); //offset
|
||||||
|
|
||||||
|
fp.writel(40, 4); //DIB size
|
||||||
|
fp.writel(width, 4); //width
|
||||||
|
fp.writel(-height, 4); //height
|
||||||
|
fp.writel(1, 2); //color planes
|
||||||
|
fp.writel(bitsPerPixel, 2); //bits per pixel
|
||||||
|
fp.writel(0, 4); //compression method (BI_RGB)
|
||||||
|
fp.writel(imageSize, 4); //image data size
|
||||||
|
fp.writel(3780, 4); //horizontal resolution
|
||||||
|
fp.writel(3780, 4); //vertical resolution
|
||||||
|
fp.writel(0, 4); //palette size
|
||||||
|
fp.writel(0, 4); //important color count
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
const uint32_t *p = (const uint32_t*)((const uint8_t*)data + y * pitch);
|
||||||
|
for(unsigned x = 0; x < width; x++) fp.writel(*p++, bytesPerPixel);
|
||||||
|
if(paddingLength) fp.writel(0, paddingLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
214
bsnes/nall/bps/delta.hpp
Executable file
214
bsnes/nall/bps/delta.hpp
Executable file
@@ -0,0 +1,214 @@
|
|||||||
|
#ifndef NALL_BPS_DELTA_HPP
|
||||||
|
#define NALL_BPS_DELTA_HPP
|
||||||
|
|
||||||
|
#include <nall/crc32.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bpsdelta {
|
||||||
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
|
inline void target(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
inline bool source(const string &filename);
|
||||||
|
inline bool target(const string &filename);
|
||||||
|
inline bool create(const string &filename, const string &metadata = "");
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||||
|
enum : unsigned { Granularity = 1 };
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
unsigned offset;
|
||||||
|
Node *next;
|
||||||
|
inline Node() : offset(0), next(nullptr) {}
|
||||||
|
inline ~Node() { if(next) delete next; }
|
||||||
|
};
|
||||||
|
|
||||||
|
filemap sourceFile;
|
||||||
|
const uint8_t *sourceData;
|
||||||
|
unsigned sourceSize;
|
||||||
|
|
||||||
|
filemap targetFile;
|
||||||
|
const uint8_t *targetData;
|
||||||
|
unsigned targetSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
void bpsdelta::source(const uint8_t *data, unsigned size) {
|
||||||
|
sourceData = data;
|
||||||
|
sourceSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bpsdelta::target(const uint8_t *data, unsigned size) {
|
||||||
|
targetData = data;
|
||||||
|
targetSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpsdelta::source(const string &filename) {
|
||||||
|
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
source(sourceFile.data(), sourceFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpsdelta::target(const string &filename) {
|
||||||
|
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
target(targetFile.data(), targetFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpsdelta::create(const string &filename, const string &metadata) {
|
||||||
|
file modifyFile;
|
||||||
|
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||||
|
|
||||||
|
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
|
||||||
|
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||||
|
|
||||||
|
auto write = [&](uint8_t data) {
|
||||||
|
modifyFile.write(data);
|
||||||
|
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto encode = [&](uint64_t data) {
|
||||||
|
while(true) {
|
||||||
|
uint64_t x = data & 0x7f;
|
||||||
|
data >>= 7;
|
||||||
|
if(data == 0) {
|
||||||
|
write(0x80 | x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(x);
|
||||||
|
data--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
write('B');
|
||||||
|
write('P');
|
||||||
|
write('S');
|
||||||
|
write('1');
|
||||||
|
|
||||||
|
encode(sourceSize);
|
||||||
|
encode(targetSize);
|
||||||
|
|
||||||
|
unsigned markupSize = metadata.length();
|
||||||
|
encode(markupSize);
|
||||||
|
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||||
|
|
||||||
|
Node *sourceTree[65536], *targetTree[65536];
|
||||||
|
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
|
||||||
|
|
||||||
|
//source tree creation
|
||||||
|
for(unsigned offset = 0; offset < sourceSize; offset++) {
|
||||||
|
uint16_t symbol = sourceData[offset + 0];
|
||||||
|
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
|
||||||
|
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
|
||||||
|
Node *node = new Node;
|
||||||
|
node->offset = offset;
|
||||||
|
node->next = sourceTree[symbol];
|
||||||
|
sourceTree[symbol] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned targetReadLength = 0;
|
||||||
|
|
||||||
|
auto targetReadFlush = [&]() {
|
||||||
|
if(targetReadLength) {
|
||||||
|
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||||
|
unsigned offset = outputOffset - targetReadLength;
|
||||||
|
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while(outputOffset < targetSize) {
|
||||||
|
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
|
||||||
|
|
||||||
|
uint16_t symbol = targetData[outputOffset + 0];
|
||||||
|
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
|
||||||
|
|
||||||
|
{ //source read
|
||||||
|
unsigned length = 0, offset = outputOffset;
|
||||||
|
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
|
||||||
|
length++;
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
if(length > maxLength) maxLength = length, mode = SourceRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ //source copy
|
||||||
|
Node *node = sourceTree[symbol];
|
||||||
|
while(node) {
|
||||||
|
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||||
|
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
|
||||||
|
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ //target copy
|
||||||
|
Node *node = targetTree[symbol];
|
||||||
|
while(node) {
|
||||||
|
unsigned length = 0, x = node->offset, y = outputOffset;
|
||||||
|
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
|
||||||
|
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
//target tree append
|
||||||
|
node = new Node;
|
||||||
|
node->offset = outputOffset;
|
||||||
|
node->next = targetTree[symbol];
|
||||||
|
targetTree[symbol] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ //target read
|
||||||
|
if(maxLength < 4) {
|
||||||
|
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
|
||||||
|
mode = TargetRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode != TargetRead) targetReadFlush();
|
||||||
|
|
||||||
|
switch(mode) {
|
||||||
|
case SourceRead:
|
||||||
|
encode(SourceRead | ((maxLength - 1) << 2));
|
||||||
|
break;
|
||||||
|
case TargetRead:
|
||||||
|
//delay write to group sequential TargetRead commands into one
|
||||||
|
targetReadLength += maxLength;
|
||||||
|
break;
|
||||||
|
case SourceCopy:
|
||||||
|
case TargetCopy:
|
||||||
|
encode(mode | ((maxLength - 1) << 2));
|
||||||
|
signed relativeOffset;
|
||||||
|
if(mode == SourceCopy) {
|
||||||
|
relativeOffset = maxOffset - sourceRelativeOffset;
|
||||||
|
sourceRelativeOffset = maxOffset + maxLength;
|
||||||
|
} else {
|
||||||
|
relativeOffset = maxOffset - targetRelativeOffset;
|
||||||
|
targetRelativeOffset = maxOffset + maxLength;
|
||||||
|
}
|
||||||
|
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputOffset += maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetReadFlush();
|
||||||
|
|
||||||
|
sourceChecksum = ~sourceChecksum;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||||
|
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||||
|
uint32_t outputChecksum = ~modifyChecksum;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||||
|
|
||||||
|
modifyFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
152
bsnes/nall/bps/linear.hpp
Executable file
152
bsnes/nall/bps/linear.hpp
Executable file
@@ -0,0 +1,152 @@
|
|||||||
|
#ifndef NALL_BPS_LINEAR_HPP
|
||||||
|
#define NALL_BPS_LINEAR_HPP
|
||||||
|
|
||||||
|
#include <nall/crc32.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bpslinear {
|
||||||
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
|
inline void target(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
inline bool source(const string &filename);
|
||||||
|
inline bool target(const string &filename);
|
||||||
|
inline bool create(const string &filename, const string &metadata = "");
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||||
|
enum : unsigned { Granularity = 1 };
|
||||||
|
|
||||||
|
filemap sourceFile;
|
||||||
|
const uint8_t *sourceData;
|
||||||
|
unsigned sourceSize;
|
||||||
|
|
||||||
|
filemap targetFile;
|
||||||
|
const uint8_t *targetData;
|
||||||
|
unsigned targetSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
void bpslinear::source(const uint8_t *data, unsigned size) {
|
||||||
|
sourceData = data;
|
||||||
|
sourceSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bpslinear::target(const uint8_t *data, unsigned size) {
|
||||||
|
targetData = data;
|
||||||
|
targetSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpslinear::source(const string &filename) {
|
||||||
|
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
source(sourceFile.data(), sourceFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpslinear::target(const string &filename) {
|
||||||
|
if(targetFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
target(targetFile.data(), targetFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpslinear::create(const string &filename, const string &metadata) {
|
||||||
|
file modifyFile;
|
||||||
|
if(modifyFile.open(filename, file::mode::write) == false) return false;
|
||||||
|
|
||||||
|
uint32_t modifyChecksum = ~0;
|
||||||
|
unsigned targetRelativeOffset = 0, outputOffset = 0;
|
||||||
|
|
||||||
|
auto write = [&](uint8_t data) {
|
||||||
|
modifyFile.write(data);
|
||||||
|
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto encode = [&](uint64_t data) {
|
||||||
|
while(true) {
|
||||||
|
uint64_t x = data & 0x7f;
|
||||||
|
data >>= 7;
|
||||||
|
if(data == 0) {
|
||||||
|
write(0x80 | x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(x);
|
||||||
|
data--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned targetReadLength = 0;
|
||||||
|
|
||||||
|
auto targetReadFlush = [&]() {
|
||||||
|
if(targetReadLength) {
|
||||||
|
encode(TargetRead | ((targetReadLength - 1) << 2));
|
||||||
|
unsigned offset = outputOffset - targetReadLength;
|
||||||
|
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
write('B');
|
||||||
|
write('P');
|
||||||
|
write('S');
|
||||||
|
write('1');
|
||||||
|
|
||||||
|
encode(sourceSize);
|
||||||
|
encode(targetSize);
|
||||||
|
|
||||||
|
unsigned markupSize = metadata.length();
|
||||||
|
encode(markupSize);
|
||||||
|
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
|
||||||
|
|
||||||
|
while(outputOffset < targetSize) {
|
||||||
|
unsigned sourceLength = 0;
|
||||||
|
for(unsigned n = 0; outputOffset + n < min(sourceSize, targetSize); n++) {
|
||||||
|
if(sourceData[outputOffset + n] != targetData[outputOffset + n]) break;
|
||||||
|
sourceLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned rleLength = 0;
|
||||||
|
for(unsigned n = 1; outputOffset + n < targetSize; n++) {
|
||||||
|
if(targetData[outputOffset] != targetData[outputOffset + n]) break;
|
||||||
|
rleLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rleLength >= 4) {
|
||||||
|
//write byte to repeat
|
||||||
|
targetReadLength++;
|
||||||
|
outputOffset++;
|
||||||
|
targetReadFlush();
|
||||||
|
|
||||||
|
//copy starting from repetition byte
|
||||||
|
encode(TargetCopy | ((rleLength - 1) << 2));
|
||||||
|
unsigned relativeOffset = (outputOffset - 1) - targetRelativeOffset;
|
||||||
|
encode(relativeOffset << 1);
|
||||||
|
outputOffset += rleLength;
|
||||||
|
targetRelativeOffset = outputOffset - 1;
|
||||||
|
} else if(sourceLength >= 4) {
|
||||||
|
targetReadFlush();
|
||||||
|
encode(SourceRead | ((sourceLength - 1) << 2));
|
||||||
|
outputOffset += sourceLength;
|
||||||
|
} else {
|
||||||
|
targetReadLength += Granularity;
|
||||||
|
outputOffset += Granularity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targetReadFlush();
|
||||||
|
|
||||||
|
uint32_t sourceChecksum = crc32_calculate(sourceData, sourceSize);
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
|
||||||
|
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
|
||||||
|
uint32_t outputChecksum = ~modifyChecksum;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||||
|
|
||||||
|
modifyFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
121
bsnes/nall/bps/metadata.hpp
Executable file
121
bsnes/nall/bps/metadata.hpp
Executable file
@@ -0,0 +1,121 @@
|
|||||||
|
#ifndef NALL_BPS_METADATA_HPP
|
||||||
|
#define NALL_BPS_METADATA_HPP
|
||||||
|
|
||||||
|
#include <nall/crc32.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bpsmetadata {
|
||||||
|
inline bool load(const string &filename);
|
||||||
|
inline bool save(const string &filename, const string &metadata);
|
||||||
|
inline string metadata() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
file sourceFile;
|
||||||
|
string metadataString;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bpsmetadata::load(const string &filename) {
|
||||||
|
if(sourceFile.open(filename, file::mode::read) == false) return false;
|
||||||
|
|
||||||
|
auto read = [&]() -> uint8_t {
|
||||||
|
return sourceFile.read();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto decode = [&]() -> uint64_t {
|
||||||
|
uint64_t data = 0, shift = 1;
|
||||||
|
while(true) {
|
||||||
|
uint8_t x = read();
|
||||||
|
data += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80) break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(read() != 'B') return false;
|
||||||
|
if(read() != 'P') return false;
|
||||||
|
if(read() != 'S') return false;
|
||||||
|
if(read() != '1') return false;
|
||||||
|
decode();
|
||||||
|
decode();
|
||||||
|
unsigned metadataSize = decode();
|
||||||
|
char data[metadataSize + 1];
|
||||||
|
for(unsigned n = 0; n < metadataSize; n++) data[n] = read();
|
||||||
|
data[metadataSize] = 0;
|
||||||
|
metadataString = (const char*)data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpsmetadata::save(const string &filename, const string &metadata) {
|
||||||
|
file targetFile;
|
||||||
|
if(targetFile.open(filename, file::mode::write) == false) return false;
|
||||||
|
if(sourceFile.open() == false) return false;
|
||||||
|
sourceFile.seek(0);
|
||||||
|
|
||||||
|
auto read = [&]() -> uint8_t {
|
||||||
|
return sourceFile.read();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto decode = [&]() -> uint64_t {
|
||||||
|
uint64_t data = 0, shift = 1;
|
||||||
|
while(true) {
|
||||||
|
uint8_t x = read();
|
||||||
|
data += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80) break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t checksum = ~0;
|
||||||
|
|
||||||
|
auto write = [&](uint8_t data) {
|
||||||
|
targetFile.write(data);
|
||||||
|
checksum = crc32_adjust(checksum, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto encode = [&](uint64_t data) {
|
||||||
|
while(true) {
|
||||||
|
uint64_t x = data & 0x7f;
|
||||||
|
data >>= 7;
|
||||||
|
if(data == 0) {
|
||||||
|
write(0x80 | x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(x);
|
||||||
|
data--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < 4; n++) write(read());
|
||||||
|
encode(decode());
|
||||||
|
encode(decode());
|
||||||
|
unsigned sourceLength = decode();
|
||||||
|
unsigned targetLength = metadata.length();
|
||||||
|
encode(targetLength);
|
||||||
|
sourceFile.seek(sourceLength, file::index::relative);
|
||||||
|
for(unsigned n = 0; n < targetLength; n++) write(metadata[n]);
|
||||||
|
unsigned length = sourceFile.size() - sourceFile.offset() - 4;
|
||||||
|
for(unsigned n = 0; n < length; n++) write(read());
|
||||||
|
uint32_t outputChecksum = ~checksum;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
|
||||||
|
|
||||||
|
targetFile.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bpsmetadata::metadata() const {
|
||||||
|
return metadataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
219
bsnes/nall/bps/patch.hpp
Executable file
219
bsnes/nall/bps/patch.hpp
Executable file
@@ -0,0 +1,219 @@
|
|||||||
|
#ifndef NALL_BPS_PATCH_HPP
|
||||||
|
#define NALL_BPS_PATCH_HPP
|
||||||
|
|
||||||
|
#include <nall/crc32.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/filemap.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct bpspatch {
|
||||||
|
inline bool modify(const uint8_t *data, unsigned size);
|
||||||
|
inline void source(const uint8_t *data, unsigned size);
|
||||||
|
inline void target(uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
inline bool modify(const string &filename);
|
||||||
|
inline bool source(const string &filename);
|
||||||
|
inline bool target(const string &filename);
|
||||||
|
|
||||||
|
inline string metadata() const;
|
||||||
|
inline unsigned size() const;
|
||||||
|
|
||||||
|
enum result : unsigned {
|
||||||
|
unknown,
|
||||||
|
success,
|
||||||
|
patch_too_small,
|
||||||
|
patch_invalid_header,
|
||||||
|
source_too_small,
|
||||||
|
target_too_small,
|
||||||
|
source_checksum_invalid,
|
||||||
|
target_checksum_invalid,
|
||||||
|
patch_checksum_invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline result apply();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
|
||||||
|
|
||||||
|
filemap modifyFile;
|
||||||
|
const uint8_t *modifyData;
|
||||||
|
unsigned modifySize;
|
||||||
|
|
||||||
|
filemap sourceFile;
|
||||||
|
const uint8_t *sourceData;
|
||||||
|
unsigned sourceSize;
|
||||||
|
|
||||||
|
filemap targetFile;
|
||||||
|
uint8_t *targetData;
|
||||||
|
unsigned targetSize;
|
||||||
|
|
||||||
|
unsigned modifySourceSize;
|
||||||
|
unsigned modifyTargetSize;
|
||||||
|
unsigned modifyMarkupSize;
|
||||||
|
string metadataString;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bpspatch::modify(const uint8_t *data, unsigned size) {
|
||||||
|
if(size < 19) return false;
|
||||||
|
modifyData = data;
|
||||||
|
modifySize = size;
|
||||||
|
|
||||||
|
unsigned offset = 4;
|
||||||
|
auto decode = [&]() -> uint64_t {
|
||||||
|
uint64_t data = 0, shift = 1;
|
||||||
|
while(true) {
|
||||||
|
uint8_t x = modifyData[offset++];
|
||||||
|
data += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80) break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
modifySourceSize = decode();
|
||||||
|
modifyTargetSize = decode();
|
||||||
|
modifyMarkupSize = decode();
|
||||||
|
|
||||||
|
char buffer[modifyMarkupSize + 1];
|
||||||
|
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
|
||||||
|
buffer[modifyMarkupSize] = 0;
|
||||||
|
metadataString = (const char*)buffer;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bpspatch::source(const uint8_t *data, unsigned size) {
|
||||||
|
sourceData = data;
|
||||||
|
sourceSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bpspatch::target(uint8_t *data, unsigned size) {
|
||||||
|
targetData = data;
|
||||||
|
targetSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpspatch::modify(const string &filename) {
|
||||||
|
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
return modify(modifyFile.data(), modifyFile.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpspatch::source(const string &filename) {
|
||||||
|
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
source(sourceFile.data(), sourceFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bpspatch::target(const string &filename) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, file::mode::write) == false) return false;
|
||||||
|
fp.truncate(modifyTargetSize);
|
||||||
|
fp.close();
|
||||||
|
|
||||||
|
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
|
||||||
|
target(targetFile.data(), targetFile.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string bpspatch::metadata() const {
|
||||||
|
return metadataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned bpspatch::size() const {
|
||||||
|
return modifyTargetSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpspatch::result bpspatch::apply() {
|
||||||
|
if(modifySize < 19) return result::patch_too_small;
|
||||||
|
|
||||||
|
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
|
||||||
|
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
|
||||||
|
|
||||||
|
auto read = [&]() -> uint8_t {
|
||||||
|
uint8_t data = modifyData[modifyOffset++];
|
||||||
|
modifyChecksum = crc32_adjust(modifyChecksum, data);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto decode = [&]() -> uint64_t {
|
||||||
|
uint64_t data = 0, shift = 1;
|
||||||
|
while(true) {
|
||||||
|
uint8_t x = read();
|
||||||
|
data += (x & 0x7f) * shift;
|
||||||
|
if(x & 0x80) break;
|
||||||
|
shift <<= 7;
|
||||||
|
data += shift;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto write = [&](uint8_t data) {
|
||||||
|
targetData[outputOffset++] = data;
|
||||||
|
targetChecksum = crc32_adjust(targetChecksum, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(read() != 'B') return result::patch_invalid_header;
|
||||||
|
if(read() != 'P') return result::patch_invalid_header;
|
||||||
|
if(read() != 'S') return result::patch_invalid_header;
|
||||||
|
if(read() != '1') return result::patch_invalid_header;
|
||||||
|
|
||||||
|
modifySourceSize = decode();
|
||||||
|
modifyTargetSize = decode();
|
||||||
|
modifyMarkupSize = decode();
|
||||||
|
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
|
||||||
|
|
||||||
|
if(modifySourceSize > sourceSize) return result::source_too_small;
|
||||||
|
if(modifyTargetSize > targetSize) return result::target_too_small;
|
||||||
|
|
||||||
|
while(modifyOffset < modifySize - 12) {
|
||||||
|
unsigned length = decode();
|
||||||
|
unsigned mode = length & 3;
|
||||||
|
length = (length >> 2) + 1;
|
||||||
|
|
||||||
|
switch(mode) {
|
||||||
|
case SourceRead:
|
||||||
|
while(length--) write(sourceData[outputOffset]);
|
||||||
|
break;
|
||||||
|
case TargetRead:
|
||||||
|
while(length--) write(read());
|
||||||
|
break;
|
||||||
|
case SourceCopy:
|
||||||
|
case TargetCopy:
|
||||||
|
signed offset = decode();
|
||||||
|
bool negative = offset & 1;
|
||||||
|
offset >>= 1;
|
||||||
|
if(negative) offset = -offset;
|
||||||
|
|
||||||
|
if(mode == SourceCopy) {
|
||||||
|
sourceRelativeOffset += offset;
|
||||||
|
while(length--) write(sourceData[sourceRelativeOffset++]);
|
||||||
|
} else {
|
||||||
|
targetRelativeOffset += offset;
|
||||||
|
while(length--) write(targetData[targetRelativeOffset++]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
|
||||||
|
uint32_t checksum = ~modifyChecksum;
|
||||||
|
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
|
||||||
|
|
||||||
|
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
|
||||||
|
targetChecksum = ~targetChecksum;
|
||||||
|
|
||||||
|
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
|
||||||
|
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
|
||||||
|
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
|
||||||
|
|
||||||
|
return result::success;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
152
bsnes/nall/compositor.hpp
Executable file
152
bsnes/nall/compositor.hpp
Executable file
@@ -0,0 +1,152 @@
|
|||||||
|
#ifndef NALL_COMPOSITOR_HPP
|
||||||
|
#define NALL_COMPOSITOR_HPP
|
||||||
|
|
||||||
|
#include <nall/intrinsics.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct compositor {
|
||||||
|
inline static bool enabled();
|
||||||
|
inline static bool enable(bool status);
|
||||||
|
|
||||||
|
#if defined(PLATFORM_X)
|
||||||
|
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
|
||||||
|
inline static Compositor detect();
|
||||||
|
|
||||||
|
inline static bool enabled_metacity();
|
||||||
|
inline static bool enable_metacity(bool status);
|
||||||
|
|
||||||
|
inline static bool enabled_xfwm4();
|
||||||
|
inline static bool enable_xfwm4(bool status);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(PLATFORM_X)
|
||||||
|
|
||||||
|
//Metacity
|
||||||
|
|
||||||
|
bool compositor::enabled_metacity() {
|
||||||
|
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
|
||||||
|
if(fp == 0) return false;
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||||
|
|
||||||
|
if(!memcmp(buffer, "true", 4)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable_metacity(bool status) {
|
||||||
|
FILE *fp;
|
||||||
|
if(status) {
|
||||||
|
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
|
||||||
|
} else {
|
||||||
|
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
|
||||||
|
}
|
||||||
|
if(fp == 0) return false;
|
||||||
|
pclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Xfwm4
|
||||||
|
|
||||||
|
bool compositor::enabled_xfwm4() {
|
||||||
|
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||||
|
if(fp == 0) return false;
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||||
|
|
||||||
|
if(!memcmp(buffer, "true", 4)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable_xfwm4(bool status) {
|
||||||
|
FILE *fp;
|
||||||
|
if(status) {
|
||||||
|
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||||
|
} else {
|
||||||
|
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'", "r");
|
||||||
|
}
|
||||||
|
if(fp == 0) return false;
|
||||||
|
pclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//General
|
||||||
|
|
||||||
|
compositor::Compositor compositor::detect() {
|
||||||
|
Compositor result = Compositor::Unknown;
|
||||||
|
|
||||||
|
FILE *fp;
|
||||||
|
char buffer[512];
|
||||||
|
|
||||||
|
fp = popen("pidof metacity", "r");
|
||||||
|
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
fp = popen("pidof xfwm4", "r");
|
||||||
|
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
|
||||||
|
pclose(fp);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
switch(detect()) {
|
||||||
|
case Compositor::Metacity: return enabled_metacity();
|
||||||
|
case Compositor::Xfwm4: return enabled_xfwm4();
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool status) {
|
||||||
|
switch(detect()) {
|
||||||
|
case Compositor::Metacity: return enable_metacity(status);
|
||||||
|
case Compositor::Xfwm4: return enable_xfwm4(status);
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||||
|
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||||
|
if(module == 0) return false;
|
||||||
|
|
||||||
|
auto pDwmIsCompositionEnabled = (HRESULT (WINAPI*)(BOOL*))GetProcAddress(module, "DwmIsCompositionEnabled");
|
||||||
|
if(pDwmIsCompositionEnabled == 0) return false;
|
||||||
|
|
||||||
|
BOOL result;
|
||||||
|
if(pDwmIsCompositionEnabled(&result) != S_OK) return false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool status) {
|
||||||
|
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||||
|
if(module == 0) module = LoadLibraryW(L"dwmapi");
|
||||||
|
if(module == 0) return false;
|
||||||
|
|
||||||
|
auto pDwmEnableComposition = (HRESULT (WINAPI*)(UINT))GetProcAddress(module, "DwmEnableComposition");
|
||||||
|
if(pDwmEnableComposition == 0) return false;
|
||||||
|
|
||||||
|
if(pDwmEnableComposition(status) != S_OK) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool compositor::enabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compositor::enable(bool) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -1,34 +0,0 @@
|
|||||||
#ifndef NALL_CONCEPT_HPP
|
|
||||||
#define NALL_CONCEPT_HPP
|
|
||||||
|
|
||||||
#include <nall/static.hpp>
|
|
||||||
#include <nall/utility.hpp>
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
//unsigned count() const;
|
|
||||||
template<typename T> struct has_count { enum { value = false }; };
|
|
||||||
|
|
||||||
//unsigned length() const;
|
|
||||||
template<typename T> struct has_length { enum { value = false }; };
|
|
||||||
|
|
||||||
//unsigned size() const;
|
|
||||||
template<typename T> struct has_size { enum { value = false }; };
|
|
||||||
|
|
||||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_count<T>>::type = 0) {
|
|
||||||
return object.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_length<T>>::type = 0) {
|
|
||||||
return object.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<has_size<T>>::type = 0) {
|
|
||||||
return object.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> unsigned container_size(const T& object, typename mp_enable_if<std::is_array<T>>::type = 0) {
|
|
||||||
return sizeof(T) / sizeof(typename std::remove_extent<T>::type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -34,11 +34,11 @@ namespace nall {
|
|||||||
|
|
||||||
string get() const {
|
string get() const {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case boolean_t: return string() << *(bool*)data;
|
case boolean_t: return { *(bool*)data };
|
||||||
case signed_t: return string() << *(signed*)data;
|
case signed_t: return { *(signed*)data };
|
||||||
case unsigned_t: return string() << *(unsigned*)data;
|
case unsigned_t: return { *(unsigned*)data };
|
||||||
case double_t: return string() << *(double*)data;
|
case double_t: return { *(double*)data };
|
||||||
case string_t: return string() << "\"" << *(string*)data << "\"";
|
case string_t: return { "\"", *(string*)data, "\"" };
|
||||||
}
|
}
|
||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ namespace nall {
|
|||||||
else list[n].type = unknown_t;
|
else list[n].type = unknown_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool load(const char *filename) {
|
virtual bool load(const string &filename) {
|
||||||
string data;
|
string data;
|
||||||
if(data.readfile(filename) == true) {
|
if(data.readfile(filename) == true) {
|
||||||
data.replace("\r", "");
|
data.replace("\r", "");
|
||||||
@@ -100,14 +100,14 @@ namespace nall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool save(const char *filename) const {
|
virtual bool save(const string &filename) const {
|
||||||
file fp;
|
file fp;
|
||||||
if(fp.open(filename, file::mode::write)) {
|
if(fp.open(filename, file::mode::write)) {
|
||||||
for(unsigned i = 0; i < list.size(); i++) {
|
for(unsigned i = 0; i < list.size(); i++) {
|
||||||
string output;
|
string output;
|
||||||
output << list[i].name << " = " << list[i].get();
|
output.append(list[i].name, " = ", list[i].get());
|
||||||
if(list[i].desc != "") output << " # " << list[i].desc;
|
if(list[i].desc != "") output.append(" # ", list[i].desc);
|
||||||
output << "\r\n";
|
output.append("\r\n");
|
||||||
fp.print(output);
|
fp.print(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
#ifndef NALL_DETECT_HPP
|
|
||||||
#define NALL_DETECT_HPP
|
|
||||||
|
|
||||||
/* Compiler detection */
|
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#define COMPILER_GCC
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define COMPILER_VISUALC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Platform detection */
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define PLATFORM_WIN
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#define PLATFORM_OSX
|
|
||||||
#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
|
||||||
#define PLATFORM_X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Endian detection */
|
|
||||||
|
|
||||||
#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
|
||||||
#define ARCH_LSB
|
|
||||||
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__)
|
|
||||||
#define ARCH_MSB
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,75 +0,0 @@
|
|||||||
#ifndef NALL_DICTIONARY_HPP
|
|
||||||
#define NALL_DICTIONARY_HPP
|
|
||||||
|
|
||||||
#include <nall/array.hpp>
|
|
||||||
#include <nall/string.hpp>
|
|
||||||
#include <nall/utility.hpp>
|
|
||||||
|
|
||||||
namespace nall {
|
|
||||||
class dictionary {
|
|
||||||
public:
|
|
||||||
string operator[](const char *input) {
|
|
||||||
for(unsigned i = 0; i < index_input.size(); i++) {
|
|
||||||
if(index_input[i] == input) return index_output[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
//no match, use input; remove input identifier, if one exists
|
|
||||||
if(strbegin(input, "{{")) {
|
|
||||||
if(auto pos = strpos(input, "}}")) {
|
|
||||||
string temp = substr(input, pos() + 2);
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool import(const char *filename) {
|
|
||||||
string data;
|
|
||||||
if(data.readfile(filename) == false) return false;
|
|
||||||
data.ltrim<1>("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists
|
|
||||||
data.replace("\r", "");
|
|
||||||
|
|
||||||
lstring line;
|
|
||||||
line.split("\n", data);
|
|
||||||
for(unsigned i = 0; i < line.size(); i++) {
|
|
||||||
lstring part;
|
|
||||||
//format: "Input" = "Output"
|
|
||||||
part.qsplit("=", line[i]);
|
|
||||||
if(part.size() != 2) continue;
|
|
||||||
|
|
||||||
//remove whitespace
|
|
||||||
part[0].trim();
|
|
||||||
part[1].trim();
|
|
||||||
|
|
||||||
//remove quotes
|
|
||||||
part[0].trim<1>("\"");
|
|
||||||
part[1].trim<1>("\"");
|
|
||||||
|
|
||||||
unsigned n = index_input.size();
|
|
||||||
index_input[n] = part[0];
|
|
||||||
index_output[n] = part[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
index_input.reset();
|
|
||||||
index_output.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
~dictionary() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
dictionary& operator=(const dictionary&) = delete;
|
|
||||||
dictionary(const dictionary&) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
lstring index_input;
|
|
||||||
lstring index_output;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,12 +1,13 @@
|
|||||||
#ifndef NALL_DIRECTORY_HPP
|
#ifndef NALL_DIRECTORY_HPP
|
||||||
#define NALL_DIRECTORY_HPP
|
#define NALL_DIRECTORY_HPP
|
||||||
|
|
||||||
#include <nall/foreach.hpp>
|
#include <nall/intrinsics.hpp>
|
||||||
#include <nall/sort.hpp>
|
#include <nall/sort.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(PLATFORM_WINDOWS)
|
||||||
#include <nall/utf8.hpp>
|
#include <nall/windows/utf8.hpp>
|
||||||
#else
|
#else
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -22,7 +23,7 @@ struct directory {
|
|||||||
static lstring contents(const string &pathname, const string &pattern = "*");
|
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(PLATFORM_WINDOWS)
|
||||||
inline bool directory::exists(const string &pathname) {
|
inline bool directory::exists(const string &pathname) {
|
||||||
DWORD result = GetFileAttributes(utf16_t(pathname));
|
DWORD result = GetFileAttributes(utf16_t(pathname));
|
||||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||||
@@ -56,7 +57,7 @@ struct directory {
|
|||||||
FindClose(handle);
|
FindClose(handle);
|
||||||
}
|
}
|
||||||
if(list.size() > 0) sort(&list[0], list.size());
|
if(list.size() > 0) sort(&list[0], list.size());
|
||||||
foreach(name, list) name.append("/"); //must append after sorting
|
for(auto &name : list) name.append("/"); //must append after sorting
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ struct directory {
|
|||||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||||
lstring files = directory::files(pathname, pattern);
|
lstring files = directory::files(pathname, pattern);
|
||||||
foreach(file, files) folders.append(file);
|
for(auto &file : files) folders.append(file);
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -116,7 +117,7 @@ struct directory {
|
|||||||
closedir(dp);
|
closedir(dp);
|
||||||
}
|
}
|
||||||
if(list.size() > 0) sort(&list[0], list.size());
|
if(list.size() > 0) sort(&list[0], list.size());
|
||||||
foreach(name, list) name.append("/"); //must append after sorting
|
for(auto &name : list) name.append("/"); //must append after sorting
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ struct directory {
|
|||||||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||||
lstring files = directory::files(pathname, pattern);
|
lstring files = directory::files(pathname, pattern);
|
||||||
foreach(file, files) folders.append(file);
|
for(auto &file : files) folders.append(file);
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
//dynamic linking support
|
//dynamic linking support
|
||||||
|
|
||||||
#include <nall/detect.hpp>
|
#include <nall/intrinsics.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
|
|
||||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#elif defined(PLATFORM_WIN)
|
#elif defined(PLATFORM_WINDOWS)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <nall/utf8.hpp>
|
#include <nall/windows/utf8.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
@@ -81,7 +81,7 @@ namespace nall {
|
|||||||
dlclose((void*)handle);
|
dlclose((void*)handle);
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
#elif defined(PLATFORM_WIN)
|
#elif defined(PLATFORM_WINDOWS)
|
||||||
inline bool library::open(const char *name, const char *path) {
|
inline bool library::open(const char *name, const char *path) {
|
||||||
if(handle) close();
|
if(handle) close();
|
||||||
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
||||||
|
13
bsnes/nall/dsp.hpp
Executable file
13
bsnes/nall/dsp.hpp
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef NALL_DSP_HPP
|
||||||
|
#define NALL_DSP_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#ifdef __SSE__
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NALL_DSP_INTERNAL_HPP
|
||||||
|
#include <nall/dsp/core.hpp>
|
||||||
|
#undef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#endif
|
51
bsnes/nall/dsp/buffer.hpp
Executable file
51
bsnes/nall/dsp/buffer.hpp
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct Buffer {
|
||||||
|
double **sample;
|
||||||
|
uint16_t rdoffset;
|
||||||
|
uint16_t wroffset;
|
||||||
|
unsigned channels;
|
||||||
|
|
||||||
|
void setChannels(unsigned channels) {
|
||||||
|
for(unsigned c = 0; c < this->channels; c++) {
|
||||||
|
if(sample[c]) delete[] sample[c];
|
||||||
|
}
|
||||||
|
if(sample) delete[] sample;
|
||||||
|
|
||||||
|
this->channels = channels;
|
||||||
|
if(channels == 0) return;
|
||||||
|
|
||||||
|
sample = new double*[channels];
|
||||||
|
for(unsigned c = 0; c < channels; c++) {
|
||||||
|
sample[c] = new double[65536]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double& read(unsigned channel, signed offset = 0) {
|
||||||
|
return sample[channel][(uint16_t)(rdoffset + offset)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double& write(unsigned channel, signed offset = 0) {
|
||||||
|
return sample[channel][(uint16_t)(wroffset + offset)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear() {
|
||||||
|
for(unsigned c = 0; c < channels; c++) {
|
||||||
|
for(unsigned n = 0; n < 65536; n++) {
|
||||||
|
sample[c][n] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdoffset = 0;
|
||||||
|
wroffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer() {
|
||||||
|
channels = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Buffer() {
|
||||||
|
setChannels(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
167
bsnes/nall/dsp/core.hpp
Executable file
167
bsnes/nall/dsp/core.hpp
Executable file
@@ -0,0 +1,167 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
//precision: can be float, double or long double
|
||||||
|
#define real float
|
||||||
|
|
||||||
|
struct DSP;
|
||||||
|
|
||||||
|
struct Resampler {
|
||||||
|
DSP &dsp;
|
||||||
|
real frequency;
|
||||||
|
|
||||||
|
virtual void setFrequency() = 0;
|
||||||
|
virtual void clear() = 0;
|
||||||
|
virtual void sample() = 0;
|
||||||
|
Resampler(DSP &dsp) : dsp(dsp) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DSP {
|
||||||
|
enum class ResampleEngine : unsigned {
|
||||||
|
Nearest,
|
||||||
|
Linear,
|
||||||
|
Cosine,
|
||||||
|
Cubic,
|
||||||
|
Hermite,
|
||||||
|
Average,
|
||||||
|
Sinc,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void setChannels(unsigned channels);
|
||||||
|
inline void setPrecision(unsigned precision);
|
||||||
|
inline void setFrequency(real frequency); //inputFrequency
|
||||||
|
inline void setVolume(real volume);
|
||||||
|
inline void setBalance(real balance);
|
||||||
|
|
||||||
|
inline void setResampler(ResampleEngine resamplingEngine);
|
||||||
|
inline void setResamplerFrequency(real frequency); //outputFrequency
|
||||||
|
|
||||||
|
inline void sample(signed channel[]);
|
||||||
|
inline bool pending();
|
||||||
|
inline void read(signed channel[]);
|
||||||
|
|
||||||
|
inline void clear();
|
||||||
|
inline DSP();
|
||||||
|
inline ~DSP();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class ResampleNearest;
|
||||||
|
friend class ResampleLinear;
|
||||||
|
friend class ResampleCosine;
|
||||||
|
friend class ResampleCubic;
|
||||||
|
friend class ResampleAverage;
|
||||||
|
friend class ResampleHermite;
|
||||||
|
friend class ResampleSinc;
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
unsigned channels;
|
||||||
|
unsigned precision;
|
||||||
|
real frequency;
|
||||||
|
real volume;
|
||||||
|
real balance;
|
||||||
|
|
||||||
|
//internal
|
||||||
|
real intensity;
|
||||||
|
real intensityInverse;
|
||||||
|
} settings;
|
||||||
|
|
||||||
|
Resampler *resampler;
|
||||||
|
inline void write(real channel[]);
|
||||||
|
|
||||||
|
#include "buffer.hpp"
|
||||||
|
Buffer buffer;
|
||||||
|
Buffer output;
|
||||||
|
|
||||||
|
inline void adjustVolume();
|
||||||
|
inline void adjustBalance();
|
||||||
|
inline signed clamp(const unsigned bits, const signed x);
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "resample/nearest.hpp"
|
||||||
|
#include "resample/linear.hpp"
|
||||||
|
#include "resample/cosine.hpp"
|
||||||
|
#include "resample/cubic.hpp"
|
||||||
|
#include "resample/hermite.hpp"
|
||||||
|
#include "resample/average.hpp"
|
||||||
|
#include "resample/sinc.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
|
||||||
|
void DSP::sample(signed channel[]) {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
|
||||||
|
}
|
||||||
|
buffer.wroffset++;
|
||||||
|
resampler->sample();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DSP::pending() {
|
||||||
|
return output.rdoffset != output.wroffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::read(signed channel[]) {
|
||||||
|
adjustVolume();
|
||||||
|
adjustBalance();
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
|
||||||
|
}
|
||||||
|
output.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::write(real channel[]) {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
output.write(c) = channel[c];
|
||||||
|
}
|
||||||
|
output.wroffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::adjustVolume() {
|
||||||
|
for(unsigned c = 0; c < settings.channels; c++) {
|
||||||
|
output.read(c) *= settings.volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::adjustBalance() {
|
||||||
|
if(settings.channels != 2) return; //TODO: support > 2 channels
|
||||||
|
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
|
||||||
|
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed DSP::clamp(const unsigned bits, const signed x) {
|
||||||
|
const signed b = 1U << (bits - 1);
|
||||||
|
const signed m = (1U << (bits - 1)) - 1;
|
||||||
|
return (x > m) ? m : (x < -b) ? -b : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::clear() {
|
||||||
|
buffer.clear();
|
||||||
|
output.clear();
|
||||||
|
resampler->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DSP::DSP() {
|
||||||
|
setResampler(ResampleEngine::Hermite);
|
||||||
|
setResamplerFrequency(44100.0);
|
||||||
|
|
||||||
|
setChannels(2);
|
||||||
|
setPrecision(16);
|
||||||
|
setFrequency(44100.0);
|
||||||
|
setVolume(1.0);
|
||||||
|
setBalance(0.0);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DSP::~DSP() {
|
||||||
|
if(resampler) delete resampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef real
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
72
bsnes/nall/dsp/resample/average.hpp
Executable file
72
bsnes/nall/dsp/resample/average.hpp
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleAverage : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
inline void sampleLinear();
|
||||||
|
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleAverage::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::sample() {
|
||||||
|
//can only average if input frequency >= output frequency
|
||||||
|
if(step < 1.0) return sampleLinear();
|
||||||
|
|
||||||
|
fraction += 1.0;
|
||||||
|
|
||||||
|
real scalar = 1.0;
|
||||||
|
if(fraction > step) scalar = 1.0 - (fraction - step);
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fraction >= step) {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) /= step;
|
||||||
|
}
|
||||||
|
dsp.output.wroffset++;
|
||||||
|
|
||||||
|
fraction -= step;
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleAverage::sampleLinear() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
44
bsnes/nall/dsp/resample/cosine.hpp
Executable file
44
bsnes/nall/dsp/resample/cosine.hpp
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleCosine : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleCosine::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCosine::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCosine::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
50
bsnes/nall/dsp/resample/cubic.hpp
Executable file
50
bsnes/nall/dsp/resample/cubic.hpp
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleCubic : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleCubic::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCubic::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleCubic::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -3);
|
||||||
|
real b = dsp.buffer.read(n, -2);
|
||||||
|
real c = dsp.buffer.read(n, -1);
|
||||||
|
real d = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
real A = d - c - a + b;
|
||||||
|
real B = a - b - A;
|
||||||
|
real C = c - a;
|
||||||
|
real D = b;
|
||||||
|
|
||||||
|
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
62
bsnes/nall/dsp/resample/hermite.hpp
Executable file
62
bsnes/nall/dsp/resample/hermite.hpp
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleHermite : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleHermite::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleHermite::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleHermite::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -3);
|
||||||
|
real b = dsp.buffer.read(n, -2);
|
||||||
|
real c = dsp.buffer.read(n, -1);
|
||||||
|
real d = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||||
|
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||||
|
|
||||||
|
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||||
|
|
||||||
|
mu1 = fraction;
|
||||||
|
mu2 = mu1 * mu1;
|
||||||
|
mu3 = mu2 * mu1;
|
||||||
|
|
||||||
|
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||||
|
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||||
|
|
||||||
|
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||||
|
a1 = mu3 - 2 * mu2 + mu1;
|
||||||
|
a2 = mu3 - mu2;
|
||||||
|
a3 = -2 * mu3 + 3 * mu2;
|
||||||
|
|
||||||
|
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
600
bsnes/nall/dsp/resample/lib/sinc.hpp
Executable file
600
bsnes/nall/dsp/resample/lib/sinc.hpp
Executable file
@@ -0,0 +1,600 @@
|
|||||||
|
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
|
||||||
|
// so that the SSE code is not used.
|
||||||
|
|
||||||
|
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
|
||||||
|
typedef float resample_samp_t;
|
||||||
|
|
||||||
|
|
||||||
|
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
|
||||||
|
#define RESAMPLE_SSEREGPARM
|
||||||
|
|
||||||
|
#if defined(__SSE__)
|
||||||
|
#define SINCRESAMPLE_USE_SSE 1
|
||||||
|
#ifndef __x86_64__
|
||||||
|
#undef RESAMPLE_SSEREGPARM
|
||||||
|
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// TODO: altivec here
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ResampleUtility
|
||||||
|
{
|
||||||
|
inline void kaiser_window(double* io, int count, double beta);
|
||||||
|
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
|
||||||
|
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
|
||||||
|
inline void normalize(double* io, int size, double gain = 1.0);
|
||||||
|
|
||||||
|
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class SincResampleHR
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
|
||||||
|
|
||||||
|
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||||
|
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||||
|
inline bool output_avail(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
|
||||||
|
|
||||||
|
unsigned ratio;
|
||||||
|
unsigned num_convolutions;
|
||||||
|
|
||||||
|
resample_coeff_t *coeffs;
|
||||||
|
std::vector<unsigned char> coeffs_mem;
|
||||||
|
|
||||||
|
// second half of ringbuffer should be copy of first half.
|
||||||
|
resample_samp_t *rb;
|
||||||
|
std::vector<unsigned char> rb_mem;
|
||||||
|
|
||||||
|
signed rb_readpos;
|
||||||
|
signed rb_writepos;
|
||||||
|
signed rb_in;
|
||||||
|
signed rb_eff_size;
|
||||||
|
|
||||||
|
friend class SincResample;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SincResample
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
QUALITY_LOW = 0,
|
||||||
|
QUALITY_MEDIUM = 2,
|
||||||
|
QUALITY_HIGH = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
|
||||||
|
|
||||||
|
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||||
|
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||||
|
inline bool output_avail(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
|
||||||
|
|
||||||
|
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
|
||||||
|
|
||||||
|
unsigned num_convolutions;
|
||||||
|
unsigned num_phases;
|
||||||
|
|
||||||
|
unsigned step_int;
|
||||||
|
double step_fract;
|
||||||
|
|
||||||
|
double input_pos_fract;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
|
||||||
|
std::vector<unsigned char> coeff_mem;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<resample_samp_t> rb; // second half should be copy of first half.
|
||||||
|
signed rb_readpos;
|
||||||
|
signed rb_writepos;
|
||||||
|
signed rb_in;
|
||||||
|
|
||||||
|
bool hr_used;
|
||||||
|
SincResampleHR hr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Code:
|
||||||
|
//
|
||||||
|
//#include "resample.hpp"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
namespace bit
|
||||||
|
{
|
||||||
|
inline unsigned round(unsigned x) {
|
||||||
|
if((x & (x - 1)) == 0) return x;
|
||||||
|
while(x & (x - 1)) x &= x - 1;
|
||||||
|
return x << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
|
||||||
|
{
|
||||||
|
const unsigned align_boundary = 16;
|
||||||
|
std::vector<double> coeffs_tmp;
|
||||||
|
double cutoff; // 1.0 = f/2
|
||||||
|
|
||||||
|
ratio = ratio_arg;
|
||||||
|
|
||||||
|
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
|
||||||
|
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
|
||||||
|
|
||||||
|
cutoff = (1.0 / ratio) - (d / num_convolutions);
|
||||||
|
|
||||||
|
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
|
||||||
|
assert(num_convolutions > ratio);
|
||||||
|
|
||||||
|
|
||||||
|
// Generate windowed sinc of POWER
|
||||||
|
coeffs_tmp.resize(num_convolutions);
|
||||||
|
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
|
||||||
|
|
||||||
|
// Copy from coeffs_tmp to coeffs~
|
||||||
|
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
|
||||||
|
// exist are allocated, zero'd mem.
|
||||||
|
|
||||||
|
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
|
||||||
|
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
|
||||||
|
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < num_convolutions; i++)
|
||||||
|
coeffs[i] = coeffs_tmp[i];
|
||||||
|
|
||||||
|
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
|
||||||
|
rb_readpos = 0;
|
||||||
|
rb_writepos = 0;
|
||||||
|
rb_in = 0;
|
||||||
|
|
||||||
|
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
|
||||||
|
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool SincResampleHR::output_avail(void)
|
||||||
|
{
|
||||||
|
return(rb_in >= (signed)num_convolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SincResampleHR::write(resample_samp_t sample)
|
||||||
|
{
|
||||||
|
assert(!output_avail());
|
||||||
|
|
||||||
|
rb[rb_writepos] = sample;
|
||||||
|
rb[rb_writepos + rb_eff_size] = sample;
|
||||||
|
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
|
||||||
|
rb_in++;
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
|
||||||
|
{
|
||||||
|
#if SINCRESAMPLE_USE_SSE
|
||||||
|
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
|
||||||
|
resample_samp_t accum;
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 8)
|
||||||
|
{
|
||||||
|
for(unsigned i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
__m128 co[2];
|
||||||
|
__m128 w[2];
|
||||||
|
|
||||||
|
co[i] = _mm_load_ps(&coeff[c + i * 4]);
|
||||||
|
w[i] = _mm_load_ps(&wave[c + i * 4]);
|
||||||
|
|
||||||
|
w[i] = _mm_mul_ps(w[i], co[i]);
|
||||||
|
|
||||||
|
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
|
||||||
|
_mm_store_ss(&accum, accum_vec);
|
||||||
|
|
||||||
|
return accum;
|
||||||
|
#else
|
||||||
|
resample_samp_t accum[4] = { 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c+= 4)
|
||||||
|
{
|
||||||
|
accum[0] += wave[c + 0] * coeff[c + 0];
|
||||||
|
accum[1] += wave[c + 1] * coeff[c + 1];
|
||||||
|
accum[2] += wave[c + 2] * coeff[c + 2];
|
||||||
|
accum[3] += wave[c + 3] * coeff[c + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
resample_samp_t SincResampleHR::read(void)
|
||||||
|
{
|
||||||
|
assert(output_avail());
|
||||||
|
resample_samp_t ret;
|
||||||
|
|
||||||
|
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
|
||||||
|
|
||||||
|
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
|
||||||
|
rb_in -= ratio;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
|
||||||
|
{
|
||||||
|
const struct
|
||||||
|
{
|
||||||
|
double beta;
|
||||||
|
double d;
|
||||||
|
unsigned pn_nume;
|
||||||
|
unsigned phases_min;
|
||||||
|
} qtab[5] =
|
||||||
|
{
|
||||||
|
{ 5.658, 3.62, 4096, 4 },
|
||||||
|
{ 6.764, 4.32, 8192, 4 },
|
||||||
|
{ 7.865, 5.0, 16384, 8 },
|
||||||
|
{ 8.960, 5.7, 32768, 16 },
|
||||||
|
{ 10.056, 6.4, 65536, 32 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
assert(ceil(input_rate) > 0);
|
||||||
|
assert(ceil(output_rate) > 0);
|
||||||
|
assert(ceil(input_rate / output_rate) <= 1024);
|
||||||
|
assert(ceil(output_rate / input_rate) <= 1024);
|
||||||
|
|
||||||
|
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
|
||||||
|
// upsampling.
|
||||||
|
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
|
||||||
|
assert(quality >= 0 && quality <= 4);
|
||||||
|
|
||||||
|
hr_used = false;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
|
||||||
|
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
|
||||||
|
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
|
||||||
|
// to virtually eliminate over-attenuation.
|
||||||
|
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
|
||||||
|
|
||||||
|
if(ioratio_rd >= 8)
|
||||||
|
{
|
||||||
|
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
|
||||||
|
hr_used = true;
|
||||||
|
|
||||||
|
input_rate /= ioratio_rd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
|
||||||
|
{
|
||||||
|
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
|
||||||
|
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
|
||||||
|
const unsigned conv_alignment_bytes = 16; // must be power of 2
|
||||||
|
const double input_to_output_ratio = input_rate / output_rate;
|
||||||
|
const double output_to_input_ratio = output_rate / input_rate;
|
||||||
|
double cutoff; // 1.0 = input_rate / 2
|
||||||
|
std::vector<double> coeff_init_buffer;
|
||||||
|
|
||||||
|
// Round up num_convolutions to be even.
|
||||||
|
if(output_rate > input_rate)
|
||||||
|
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
|
||||||
|
else
|
||||||
|
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
|
||||||
|
|
||||||
|
if(output_rate > input_rate) // Upsampling
|
||||||
|
cutoff = desired_bandwidth;
|
||||||
|
else // Downsampling
|
||||||
|
cutoff = output_to_input_ratio * desired_bandwidth;
|
||||||
|
|
||||||
|
// Round up to be even.
|
||||||
|
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
|
||||||
|
|
||||||
|
// Adjust cutoff to account for the multiple phases.
|
||||||
|
cutoff = cutoff / num_phases;
|
||||||
|
|
||||||
|
assert((num_convolutions & 1) == 0);
|
||||||
|
assert((num_phases & 1) == 0);
|
||||||
|
|
||||||
|
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
|
||||||
|
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
|
||||||
|
|
||||||
|
coeff_init_buffer.resize(num_phases * num_convolutions);
|
||||||
|
|
||||||
|
coeffs.resize(num_phases + 1 + 1);
|
||||||
|
|
||||||
|
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
|
||||||
|
|
||||||
|
// Assign aligned pointers into coeff_mem
|
||||||
|
{
|
||||||
|
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
|
||||||
|
|
||||||
|
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
|
||||||
|
{
|
||||||
|
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
|
||||||
|
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
|
||||||
|
|
||||||
|
// Reorder coefficients to allow for more efficient convolution.
|
||||||
|
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
|
||||||
|
{
|
||||||
|
for(int conv = 0; conv < (int)num_convolutions; conv++)
|
||||||
|
{
|
||||||
|
double coeff;
|
||||||
|
|
||||||
|
if(phase == -1 && conv == 0)
|
||||||
|
coeff = 0;
|
||||||
|
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
|
||||||
|
coeff = 0;
|
||||||
|
else
|
||||||
|
coeff = coeff_init_buffer[conv * num_phases + phase];
|
||||||
|
|
||||||
|
coeffs[phase + 1][conv] = coeff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a bit of mem
|
||||||
|
coeff_init_buffer.resize(0);
|
||||||
|
|
||||||
|
step_int = floor(input_to_output_ratio);
|
||||||
|
step_fract = input_to_output_ratio - step_int;
|
||||||
|
|
||||||
|
input_pos_fract = 0;
|
||||||
|
|
||||||
|
// Do NOT use rb.size() later in the code, since it'll include the padding.
|
||||||
|
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
|
||||||
|
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
|
||||||
|
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
|
||||||
|
// prevent illegal memory accesses.
|
||||||
|
rb.resize(num_convolutions * 2 + max_mult_minus1);
|
||||||
|
|
||||||
|
rb_readpos = 0;
|
||||||
|
rb_writepos = 0;
|
||||||
|
rb_in = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
|
||||||
|
{
|
||||||
|
resample_samp_t accum = 0;
|
||||||
|
#if SINCRESAMPLE_USE_SSE
|
||||||
|
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 8) //8) //4)
|
||||||
|
{
|
||||||
|
__m128 coeff_a[2];
|
||||||
|
__m128 coeff_b[2];
|
||||||
|
__m128 w[2];
|
||||||
|
__m128 result_a[2], result_b[2];
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
|
||||||
|
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
|
||||||
|
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
|
||||||
|
|
||||||
|
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
|
||||||
|
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
|
||||||
|
|
||||||
|
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
|
||||||
|
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 accum_vec, av_a, av_b;
|
||||||
|
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
|
||||||
|
__m128 mult_b_vec = _mm_set1_ps(ffract);
|
||||||
|
|
||||||
|
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
|
||||||
|
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(av_a, av_b);
|
||||||
|
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||||
|
|
||||||
|
_mm_store_ss(&accum, accum_vec);
|
||||||
|
#else
|
||||||
|
resample_coeff_t mult_a = 1.0 - ffract;
|
||||||
|
resample_coeff_t mult_b = ffract;
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < count; c += 4)
|
||||||
|
{
|
||||||
|
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
|
||||||
|
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
|
||||||
|
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
|
||||||
|
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool SincResample::output_avail(void)
|
||||||
|
{
|
||||||
|
return(rb_in >= (int)num_convolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
resample_samp_t SincResample::read(void)
|
||||||
|
{
|
||||||
|
assert(output_avail());
|
||||||
|
double phase = input_pos_fract * num_phases - 0.5;
|
||||||
|
signed phase_int = (signed)floor(phase);
|
||||||
|
double phase_fract = phase - phase_int;
|
||||||
|
unsigned phase_a = num_phases - 1 - phase_int;
|
||||||
|
unsigned phase_b = phase_a - 1;
|
||||||
|
resample_samp_t ret;
|
||||||
|
|
||||||
|
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
|
||||||
|
|
||||||
|
unsigned int_increment = step_int;
|
||||||
|
|
||||||
|
input_pos_fract += step_fract;
|
||||||
|
int_increment += floor(input_pos_fract);
|
||||||
|
input_pos_fract -= floor(input_pos_fract);
|
||||||
|
|
||||||
|
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
|
||||||
|
rb_in -= int_increment;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SincResample::write(resample_samp_t sample)
|
||||||
|
{
|
||||||
|
assert(!output_avail());
|
||||||
|
|
||||||
|
if(hr_used)
|
||||||
|
{
|
||||||
|
hr.write(sample);
|
||||||
|
|
||||||
|
if(hr.output_avail())
|
||||||
|
{
|
||||||
|
sample = hr.read();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb[rb_writepos + 0 * num_convolutions] = sample;
|
||||||
|
rb[rb_writepos + 1 * num_convolutions] = sample;
|
||||||
|
rb_writepos = (rb_writepos + 1) % num_convolutions;
|
||||||
|
rb_in++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::kaiser_window( double* io, int count, double beta)
|
||||||
|
{
|
||||||
|
int const accuracy = 24; //16; //12;
|
||||||
|
|
||||||
|
double* end = io + count;
|
||||||
|
|
||||||
|
double beta2 = beta * beta * (double) -0.25;
|
||||||
|
double to_fract = beta2 / ((double) count * count);
|
||||||
|
double i = 0;
|
||||||
|
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||||
|
|
||||||
|
for ( ; io < end; ++io, i += 1 )
|
||||||
|
{
|
||||||
|
double x = i * i * to_fract - beta2;
|
||||||
|
double u = x;
|
||||||
|
double k = x + 1;
|
||||||
|
|
||||||
|
double n = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
u *= x / (n * n);
|
||||||
|
n += 1;
|
||||||
|
k += u;
|
||||||
|
}
|
||||||
|
while ( k <= u * (1 << accuracy) );
|
||||||
|
|
||||||
|
if ( !i )
|
||||||
|
rescale = 1 / k; // otherwise values get large
|
||||||
|
|
||||||
|
*io *= k * rescale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
|
||||||
|
{
|
||||||
|
assert( size % 2 == 0 ); // size must be even
|
||||||
|
|
||||||
|
int const half_size = size / 2;
|
||||||
|
double* const mid = &out [half_size];
|
||||||
|
|
||||||
|
// Generate right half of sinc
|
||||||
|
for ( int i = 0; i < half_size; i++ )
|
||||||
|
{
|
||||||
|
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||||
|
mid [i] = sin( angle * cutoff ) / angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
kaiser_window( mid, half_size, kaiser );
|
||||||
|
|
||||||
|
// Mirror for left half
|
||||||
|
for ( int i = 0; i < half_size; i++ )
|
||||||
|
out [i] = mid [half_size - 1 - i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
|
||||||
|
{
|
||||||
|
assert( size % 2 == 1); // size must be odd
|
||||||
|
|
||||||
|
for(int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
if(i == (size / 2))
|
||||||
|
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||||
|
else
|
||||||
|
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||||
|
|
||||||
|
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
|
||||||
|
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
|
||||||
|
|
||||||
|
// printf("%d %f\n", i, out[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
|
||||||
|
|
||||||
|
// Mirror for left half
|
||||||
|
for ( int i = 0; i < size / 2; i++ )
|
||||||
|
out [i] = out [size - 1 - i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleUtility::normalize(double* io, int size, double gain)
|
||||||
|
{
|
||||||
|
double sum = 0;
|
||||||
|
for ( int i = 0; i < size; i++ )
|
||||||
|
sum += io [i];
|
||||||
|
|
||||||
|
double scale = gain / sum;
|
||||||
|
for ( int i = 0; i < size; i++ )
|
||||||
|
io [i] *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
|
||||||
|
{
|
||||||
|
unsigned char* null_ptr = (unsigned char *)NULL;
|
||||||
|
unsigned char* uc_ptr = (unsigned char *)ptr;
|
||||||
|
|
||||||
|
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
|
||||||
|
|
||||||
|
//while((uc_ptr - null_ptr) & (boundary - 1))
|
||||||
|
// uc_ptr++;
|
||||||
|
|
||||||
|
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
|
||||||
|
|
||||||
|
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
|
||||||
|
|
||||||
|
return uc_ptr;
|
||||||
|
}
|
43
bsnes/nall/dsp/resample/linear.hpp
Executable file
43
bsnes/nall/dsp/resample/linear.hpp
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleLinear : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleLinear::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleLinear::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleLinear::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = a * (1.0 - mu) + b * mu;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
bsnes/nall/dsp/resample/nearest.hpp
Executable file
43
bsnes/nall/dsp/resample/nearest.hpp
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
struct ResampleNearest : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
|
||||||
|
|
||||||
|
real fraction;
|
||||||
|
real step;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleNearest::setFrequency() {
|
||||||
|
fraction = 0.0;
|
||||||
|
step = dsp.settings.frequency / frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleNearest::clear() {
|
||||||
|
fraction = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleNearest::sample() {
|
||||||
|
while(fraction <= 1.0) {
|
||||||
|
real channel[dsp.settings.channels];
|
||||||
|
|
||||||
|
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||||
|
real a = dsp.buffer.read(n, -1);
|
||||||
|
real b = dsp.buffer.read(n, -0);
|
||||||
|
|
||||||
|
real mu = fraction;
|
||||||
|
|
||||||
|
channel[n] = mu < 0.5 ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.write(channel);
|
||||||
|
fraction += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
fraction -= 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
54
bsnes/nall/dsp/resample/sinc.hpp
Executable file
54
bsnes/nall/dsp/resample/sinc.hpp
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
#include "lib/sinc.hpp"
|
||||||
|
|
||||||
|
struct ResampleSinc : Resampler {
|
||||||
|
inline void setFrequency();
|
||||||
|
inline void clear();
|
||||||
|
inline void sample();
|
||||||
|
inline ResampleSinc(DSP &dsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void remakeSinc();
|
||||||
|
SincResample *sinc_resampler[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
void ResampleSinc::setFrequency() {
|
||||||
|
remakeSinc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::clear() {
|
||||||
|
remakeSinc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::sample() {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
sinc_resampler[c]->write(dsp.buffer.read(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sinc_resampler[0]->output_avail()) {
|
||||||
|
do {
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
dsp.output.write(c) = sinc_resampler[c]->read();
|
||||||
|
}
|
||||||
|
dsp.output.wroffset++;
|
||||||
|
} while(sinc_resampler[0]->output_avail());
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp.buffer.rdoffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
|
||||||
|
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResampleSinc::remakeSinc() {
|
||||||
|
assert(dsp.settings.channels < 8);
|
||||||
|
|
||||||
|
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||||
|
if(sinc_resampler[c]) delete sinc_resampler[c];
|
||||||
|
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
50
bsnes/nall/dsp/settings.hpp
Executable file
50
bsnes/nall/dsp/settings.hpp
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifdef NALL_DSP_INTERNAL_HPP
|
||||||
|
|
||||||
|
void DSP::setChannels(unsigned channels) {
|
||||||
|
assert(channels > 0);
|
||||||
|
buffer.setChannels(channels);
|
||||||
|
output.setChannels(channels);
|
||||||
|
settings.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setPrecision(unsigned precision) {
|
||||||
|
settings.precision = precision;
|
||||||
|
settings.intensity = 1 << (settings.precision - 1);
|
||||||
|
settings.intensityInverse = 1.0 / settings.intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setFrequency(real frequency) {
|
||||||
|
settings.frequency = frequency;
|
||||||
|
resampler->setFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setVolume(real volume) {
|
||||||
|
settings.volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setBalance(real balance) {
|
||||||
|
settings.balance = balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setResampler(ResampleEngine engine) {
|
||||||
|
if(resampler) delete resampler;
|
||||||
|
|
||||||
|
switch(engine) {
|
||||||
|
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
|
||||||
|
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
|
||||||
|
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
|
||||||
|
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
|
||||||
|
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
|
||||||
|
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
|
||||||
|
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSP::setResamplerFrequency(real frequency) {
|
||||||
|
resampler->frequency = frequency;
|
||||||
|
resampler->setFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -1,7 +1,9 @@
|
|||||||
#ifndef NALL_ENDIAN_HPP
|
#ifndef NALL_ENDIAN_HPP
|
||||||
#define NALL_ENDIAN_HPP
|
#define NALL_ENDIAN_HPP
|
||||||
|
|
||||||
#if !defined(ARCH_MSB)
|
#include <nall/intrinsics.hpp>
|
||||||
|
|
||||||
|
#if defined(ENDIAN_LSB)
|
||||||
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||||
#define order_lsb2(a,b) a,b
|
#define order_lsb2(a,b) a,b
|
||||||
#define order_lsb3(a,b,c) a,b,c
|
#define order_lsb3(a,b,c) a,b,c
|
||||||
@@ -17,7 +19,7 @@
|
|||||||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||||
#else
|
#elif defined(ENDIAN_MSB)
|
||||||
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||||
#define order_lsb2(a,b) b,a
|
#define order_lsb2(a,b) b,a
|
||||||
#define order_lsb3(a,b,c) c,b,a
|
#define order_lsb3(a,b,c) c,b,a
|
||||||
@@ -33,6 +35,8 @@
|
|||||||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||||
|
#else
|
||||||
|
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,11 +4,11 @@
|
|||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/string.hpp>
|
#include <nall/string.hpp>
|
||||||
#include <nall/utf8.hpp>
|
|
||||||
#include <nall/utility.hpp>
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/windows/utf8.hpp>
|
||||||
|
|
||||||
namespace nall {
|
namespace nall {
|
||||||
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
|
inline FILE* fopen_utf8(const string &utf8_filename, const char *mode) {
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
return fopen(utf8_filename, mode);
|
return fopen(utf8_filename, mode);
|
||||||
#else
|
#else
|
||||||
@@ -22,6 +22,29 @@ namespace nall {
|
|||||||
enum class index : unsigned { absolute, relative };
|
enum class index : unsigned { absolute, relative };
|
||||||
enum class time : unsigned { create, modify, access };
|
enum class time : unsigned { create, modify, access };
|
||||||
|
|
||||||
|
static bool read(const string &filename, uint8_t *&data, unsigned &size) {
|
||||||
|
data = 0;
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::read) == false) return false;
|
||||||
|
size = fp.size();
|
||||||
|
data = new uint8_t[size];
|
||||||
|
fp.read(data, size);
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read(const string &filename, const uint8_t *&data, unsigned &size) {
|
||||||
|
return file::read(filename, (uint8_t*&)data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||||
|
file fp;
|
||||||
|
if(fp.open(filename, mode::write) == false) return false;
|
||||||
|
fp.write(data, size);
|
||||||
|
fp.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t read() {
|
uint8_t read() {
|
||||||
if(!fp) return 0xff; //file not open
|
if(!fp) return 0xff; //file not open
|
||||||
if(file_mode == mode::write) return 0xff; //reads not permitted
|
if(file_mode == mode::write) return 0xff; //reads not permitted
|
||||||
@@ -135,7 +158,7 @@ namespace nall {
|
|||||||
return file_offset >= file_size;
|
return file_offset >= file_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool exists(const char *filename) {
|
static bool exists(const string &filename) {
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
struct stat64 data;
|
struct stat64 data;
|
||||||
return stat64(filename, &data) == 0;
|
return stat64(filename, &data) == 0;
|
||||||
@@ -145,7 +168,7 @@ namespace nall {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintmax_t size(const char *filename) {
|
static uintmax_t size(const string &filename) {
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
struct stat64 data;
|
struct stat64 data;
|
||||||
stat64(filename, &data);
|
stat64(filename, &data);
|
||||||
@@ -156,7 +179,7 @@ namespace nall {
|
|||||||
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
static time_t timestamp(const char *filename, file::time mode = file::time::create) {
|
static time_t timestamp(const string &filename, file::time mode = file::time::create) {
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
struct stat64 data;
|
struct stat64 data;
|
||||||
stat64(filename, &data);
|
stat64(filename, &data);
|
||||||
@@ -175,7 +198,7 @@ namespace nall {
|
|||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool open(const char *filename, mode mode_) {
|
bool open(const string &filename, mode mode_) {
|
||||||
if(fp) return false;
|
if(fp) return false;
|
||||||
|
|
||||||
switch(file_mode = mode_) {
|
switch(file_mode = mode_) {
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
#ifndef NALL_FILEMAP_HPP
|
#ifndef NALL_FILEMAP_HPP
|
||||||
#define NALL_FILEMAP_HPP
|
#define NALL_FILEMAP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
#include <nall/stdint.hpp>
|
#include <nall/stdint.hpp>
|
||||||
#include <nall/utf8.hpp>
|
#include <nall/windows/utf8.hpp>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -47,6 +48,12 @@ namespace nall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool p_open(const char *filename, mode mode_) {
|
bool p_open(const char *filename, mode mode_) {
|
||||||
|
if(file::exists(filename) && file::size(filename) == 0) {
|
||||||
|
p_handle = 0;
|
||||||
|
p_size = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int desired_access, creation_disposition, flprotect, map_access;
|
int desired_access, creation_disposition, flprotect, map_access;
|
||||||
|
|
||||||
switch(mode_) {
|
switch(mode_) {
|
||||||
@@ -133,6 +140,12 @@ namespace nall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool p_open(const char *filename, mode mode_) {
|
bool p_open(const char *filename, mode mode_) {
|
||||||
|
if(file::exists(filename) && file::size(filename) == 0) {
|
||||||
|
p_handle = 0;
|
||||||
|
p_size = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int open_flags, mmap_flags;
|
int open_flags, mmap_flags;
|
||||||
|
|
||||||
switch(mode_) {
|
switch(mode_) {
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
#ifndef NALL_FOREACH_HPP
|
|
||||||
#define NALL_FOREACH_HPP
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <nall/concept.hpp>
|
|
||||||
|
|
||||||
#undef foreach
|
|
||||||
|
|
||||||
#define foreach2(iter, object) foreach3(iter, object, foreach_counter)
|
|
||||||
#define foreach3(iter, object, foreach_counter) \
|
|
||||||
for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \
|
|
||||||
for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0)
|
|
||||||
|
|
||||||
#define foreach_impl(...) foreach_decl(__VA_ARGS__, foreach3(__VA_ARGS__), foreach2(__VA_ARGS__), foreach_too_few_arguments)
|
|
||||||
#define foreach_decl(_1, _2, _3, N, ...) N
|
|
||||||
#define foreach(...) foreach_impl(__VA_ARGS__)
|
|
||||||
|
|
||||||
#endif
|
|
@@ -36,19 +36,19 @@ namespace nall {
|
|||||||
public:
|
public:
|
||||||
operator bool() const { return callback; }
|
operator bool() const { return callback; }
|
||||||
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
||||||
void reset() { if(callback) { delete callback; callback = 0; } }
|
void reset() { if(callback) { delete callback; callback = nullptr; } }
|
||||||
|
|
||||||
function& operator=(const function &source) {
|
function& operator=(const function &source) {
|
||||||
if(this != &source) {
|
if(this != &source) {
|
||||||
if(callback) { delete callback; callback = 0; }
|
if(callback) { delete callback; callback = nullptr; }
|
||||||
if(source.callback) callback = source.callback->copy();
|
if(source.callback) callback = source.callback->copy();
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function(const function &source) : callback(0) { operator=(source); }
|
function(const function &source) : callback(nullptr) { operator=(source); }
|
||||||
function() : callback(0) {}
|
function() : callback(nullptr) {}
|
||||||
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
|
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
|
||||||
function(R (*function)(P...)) { callback = new global(function); }
|
function(R (*function)(P...)) { callback = new global(function); }
|
||||||
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
||||||
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
||||||
|
@@ -5,8 +5,8 @@ namespace nall {
|
|||||||
|
|
||||||
class GameBoyCartridge {
|
class GameBoyCartridge {
|
||||||
public:
|
public:
|
||||||
string xml;
|
string markup;
|
||||||
inline GameBoyCartridge(const uint8_t *data, unsigned size);
|
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
struct Information {
|
struct Information {
|
||||||
@@ -21,8 +21,8 @@ public:
|
|||||||
} info;
|
} info;
|
||||||
};
|
};
|
||||||
|
|
||||||
GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||||
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
markup = "";
|
||||||
if(romsize < 0x4000) return;
|
if(romsize < 0x4000) return;
|
||||||
|
|
||||||
info.mapper = "unknown";
|
info.mapper = "unknown";
|
||||||
@@ -34,6 +34,20 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
|||||||
info.romsize = 0;
|
info.romsize = 0;
|
||||||
info.ramsize = 0;
|
info.ramsize = 0;
|
||||||
|
|
||||||
|
unsigned base = romsize - 0x8000;
|
||||||
|
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||||
|
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||||
|
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||||
|
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||||
|
) {
|
||||||
|
//MMM01 stores header at bottom of image
|
||||||
|
//flip this around for consistency with all other mappers
|
||||||
|
uint8_t header[0x8000];
|
||||||
|
memcpy(header, romdata + base, 0x8000);
|
||||||
|
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||||
|
memcpy(romdata, header, 0x8000);
|
||||||
|
}
|
||||||
|
|
||||||
switch(romdata[0x0147]) {
|
switch(romdata[0x0147]) {
|
||||||
case 0x00: info.mapper = "none"; break;
|
case 0x00: info.mapper = "none"; break;
|
||||||
case 0x01: info.mapper = "MBC1"; break;
|
case 0x01: info.mapper = "MBC1"; break;
|
||||||
@@ -86,18 +100,26 @@ GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) {
|
|||||||
|
|
||||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||||
|
|
||||||
xml << "<cartridge mapper='" << info.mapper << "'";
|
markup.append(
|
||||||
if(info.rtc) xml << " rtc='true'";
|
"<?xml version='1.0' encoding='UTF-8'?>\n",
|
||||||
if(info.rumble) xml << " rumble='true'";
|
"<cartridge mapper='", info.mapper, "' rtc='", info.rtc, "' rumble='", info.rumble, "'>\n",
|
||||||
xml << ">\n";
|
" <rom size='0x", hex(romsize), "'/>\n");
|
||||||
|
if(info.ramsize > 0) markup.append(
|
||||||
|
" <ram size='0x", hex(info.ramsize), "' battery='", info.battery, "'/>\n");
|
||||||
|
markup.append(
|
||||||
|
"</cartridge>\n");
|
||||||
|
|
||||||
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
|
/*
|
||||||
|
markup.append("cartridge mapper=", info.mapper);
|
||||||
|
if(info.rtc) markup.append(" rtc");
|
||||||
|
if(info.rumble) markup.append(" rumble");
|
||||||
|
markup.append("\n");
|
||||||
|
|
||||||
|
markup.append("\t" "rom size=", hex(romsize), "\n"); //TODO: trust/check info.romsize?
|
||||||
|
|
||||||
if(info.ramsize > 0)
|
if(info.ramsize > 0)
|
||||||
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
|
markup.append("\t" "ram size=", hex(info.ramsize), info.battery ? " non-volatile\n" : "\n");
|
||||||
|
*/
|
||||||
xml << "</cartridge>\n";
|
|
||||||
xml.transform("'", "\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
87
bsnes/nall/gzip.hpp
Executable file
87
bsnes/nall/gzip.hpp
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#ifndef NALL_GZIP_HPP
|
||||||
|
#define NALL_GZIP_HPP
|
||||||
|
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/inflate.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct gzip {
|
||||||
|
string filename;
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
|
||||||
|
bool decompress(const string &filename);
|
||||||
|
bool decompress(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
|
gzip();
|
||||||
|
~gzip();
|
||||||
|
};
|
||||||
|
|
||||||
|
bool gzip::decompress(const string &filename) {
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
if(file::read(filename, data, size) == false) return false;
|
||||||
|
bool result = decompress(data, size);
|
||||||
|
delete[] data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gzip::decompress(const uint8_t *data, unsigned size) {
|
||||||
|
if(size < 18) return false;
|
||||||
|
if(data[0] != 0x1f) return false;
|
||||||
|
if(data[1] != 0x8b) return false;
|
||||||
|
unsigned cm = data[2];
|
||||||
|
unsigned flg = data[3];
|
||||||
|
unsigned mtime = data[4];
|
||||||
|
mtime |= data[5] << 8;
|
||||||
|
mtime |= data[6] << 16;
|
||||||
|
mtime |= data[7] << 24;
|
||||||
|
unsigned xfl = data[8];
|
||||||
|
unsigned os = data[9];
|
||||||
|
unsigned p = 10;
|
||||||
|
unsigned isize = data[size - 4];
|
||||||
|
isize |= data[size - 3] << 8;
|
||||||
|
isize |= data[size - 2] << 16;
|
||||||
|
isize |= data[size - 1] << 24;
|
||||||
|
filename = "";
|
||||||
|
|
||||||
|
if(flg & 0x04) { //FEXTRA
|
||||||
|
unsigned xlen = data[p + 0];
|
||||||
|
xlen |= data[p + 1] << 8;
|
||||||
|
p += 2 + xlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x08) { //FNAME
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
for(unsigned n = 0; n < PATH_MAX; n++, p++) {
|
||||||
|
buffer[n] = data[p];
|
||||||
|
if(data[p] == 0) break;
|
||||||
|
}
|
||||||
|
if(data[p++]) return false;
|
||||||
|
filename = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x10) { //FCOMMENT
|
||||||
|
while(data[p++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flg & 0x02) { //FHCRC
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->size = isize;
|
||||||
|
this->data = new uint8_t[this->size];
|
||||||
|
return inflate(this->data, this->size, data + p, size - p - 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip::gzip() : data(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip::~gzip() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
55
bsnes/nall/hid.hpp
Executable file
55
bsnes/nall/hid.hpp
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef NALL_HID_HPP
|
||||||
|
#define NALL_HID_HPP
|
||||||
|
|
||||||
|
#include <nall/xorg/xorg.hpp>
|
||||||
|
#include <nall/input.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
namespace HID {
|
||||||
|
|
||||||
|
struct Keyboard {
|
||||||
|
XlibDisplay *display;
|
||||||
|
|
||||||
|
inline void poll() {
|
||||||
|
XQueryKeymap(display, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator[](unsigned id) {
|
||||||
|
return state[scancode[id] >> 3] & (1 << (scancode[id] & 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Keyboard() {
|
||||||
|
display = XOpenDisplay(0);
|
||||||
|
memset(&scancode, 0, sizeof scancode);
|
||||||
|
|
||||||
|
#define map(key, sym) scancode[key] = XKeysymToKeycode(display, sym)
|
||||||
|
|
||||||
|
using nall::Keyboard;
|
||||||
|
map(Keyboard::Insert, XK_Insert);
|
||||||
|
map(Keyboard::Delete, XK_Delete);
|
||||||
|
map(Keyboard::Home, XK_Home);
|
||||||
|
map(Keyboard::End, XK_End);
|
||||||
|
map(Keyboard::PageUp, XK_Prior);
|
||||||
|
map(Keyboard::PageDown, XK_Next);
|
||||||
|
|
||||||
|
map(Keyboard::Up, XK_Up);
|
||||||
|
map(Keyboard::Down, XK_Down);
|
||||||
|
map(Keyboard::Left, XK_Left);
|
||||||
|
map(Keyboard::Right, XK_Right);
|
||||||
|
|
||||||
|
#undef map
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~Keyboard() {
|
||||||
|
XCloseDisplay(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char state[32];
|
||||||
|
uint8_t scancode[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
176
bsnes/nall/http.hpp
Executable file
176
bsnes/nall/http.hpp
Executable file
@@ -0,0 +1,176 @@
|
|||||||
|
#ifndef NALL_HTTP_HPP
|
||||||
|
#define NALL_HTTP_HPP
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
|
||||||
|
namespace nall {
|
||||||
|
|
||||||
|
struct http {
|
||||||
|
string hostname;
|
||||||
|
addrinfo *serverinfo;
|
||||||
|
int serversocket;
|
||||||
|
string header;
|
||||||
|
|
||||||
|
inline void download(const string &path, uint8_t *&data, unsigned &size) {
|
||||||
|
data = 0;
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
send({
|
||||||
|
"GET ", path, " HTTP/1.1\r\n"
|
||||||
|
"Host: ", hostname, "\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n"
|
||||||
|
});
|
||||||
|
|
||||||
|
header = downloadHeader();
|
||||||
|
downloadContent(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool connect(string host, unsigned port) {
|
||||||
|
hostname = host;
|
||||||
|
|
||||||
|
addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
int status = getaddrinfo(hostname, string(port), &hints, &serverinfo);
|
||||||
|
if(status != 0) return false;
|
||||||
|
|
||||||
|
serversocket = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol);
|
||||||
|
if(serversocket == -1) return false;
|
||||||
|
|
||||||
|
int result = ::connect(serversocket, serverinfo->ai_addr, serverinfo->ai_addrlen);
|
||||||
|
if(result == -1) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool send(const string &data) {
|
||||||
|
return send((const uint8_t*)(const char*)data, data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool send(const uint8_t *data, unsigned size) {
|
||||||
|
while(size) {
|
||||||
|
int length = ::send(serversocket, (const char*)data, size, 0);
|
||||||
|
if(length == -1) return false;
|
||||||
|
data += length;
|
||||||
|
size -= length;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline string downloadHeader() {
|
||||||
|
string output;
|
||||||
|
do {
|
||||||
|
char buffer[2];
|
||||||
|
int length = recv(serversocket, buffer, 1, 0);
|
||||||
|
if(length <= 0) return output;
|
||||||
|
buffer[1] = 0;
|
||||||
|
output.append(buffer);
|
||||||
|
} while(output.endswith("\r\n\r\n") == false);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline string downloadChunkLength() {
|
||||||
|
string output;
|
||||||
|
do {
|
||||||
|
char buffer[2];
|
||||||
|
int length = recv(serversocket, buffer, 1, 0);
|
||||||
|
if(length <= 0) return output;
|
||||||
|
buffer[1] = 0;
|
||||||
|
output.append(buffer);
|
||||||
|
} while(output.endswith("\r\n") == false);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void downloadContent(uint8_t *&data, unsigned &size) {
|
||||||
|
unsigned capacity = 0;
|
||||||
|
|
||||||
|
if(header.iposition("\r\nTransfer-Encoding: chunked\r\n")) {
|
||||||
|
while(true) {
|
||||||
|
unsigned length = hex(downloadChunkLength());
|
||||||
|
if(length == 0) break;
|
||||||
|
capacity += length;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
|
||||||
|
char buffer[length];
|
||||||
|
while(length) {
|
||||||
|
int packetlength = recv(serversocket, buffer, length, 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
length -= packetlength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(auto position = header.iposition("\r\nContent-Length: ")) {
|
||||||
|
unsigned length = decimal((const char*)header + position() + 16);
|
||||||
|
while(length) {
|
||||||
|
char buffer[256];
|
||||||
|
int packetlength = recv(serversocket, buffer, min(256, length), 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
capacity += packetlength;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
length -= packetlength;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while(true) {
|
||||||
|
char buffer[256];
|
||||||
|
int packetlength = recv(serversocket, buffer, 256, 0);
|
||||||
|
if(packetlength <= 0) break;
|
||||||
|
capacity += packetlength;
|
||||||
|
data = (uint8_t*)realloc(data, capacity);
|
||||||
|
memcpy(data + size, buffer, packetlength);
|
||||||
|
size += packetlength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (uint8_t*)realloc(data, capacity + 1);
|
||||||
|
data[capacity] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void disconnect() {
|
||||||
|
close(serversocket);
|
||||||
|
freeaddrinfo(serverinfo);
|
||||||
|
serverinfo = 0;
|
||||||
|
serversocket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
inline int close(int sock) {
|
||||||
|
return closesocket(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline http() {
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) {
|
||||||
|
WSADATA wsaData;
|
||||||
|
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
WSACleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user