mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-19 06:01:42 +02:00
Compare commits
141 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
77bb5b7891 | ||
|
4b2944c39b | ||
|
4c29e6fbab | ||
|
a454e9d927 | ||
|
d2241f1931 | ||
|
0cd0dcd811 | ||
|
1c18812f47 | ||
|
28885db586 | ||
|
d423ae0a29 | ||
|
303a0a67d0 | ||
|
01b4cb9919 | ||
|
17b5bae86a | ||
|
6189c93f3d | ||
|
1de484262c | ||
|
b8d0ec29b2 | ||
|
79a47d133a | ||
|
c8bb4949b1 | ||
|
f587cb0dcc | ||
|
ea086fe33f | ||
|
c66cc73374 | ||
|
5a1dcf5079 | ||
|
e16dd58184 | ||
|
395e5a5639 | ||
|
ec939065bd | ||
|
77578cd0a4 | ||
|
04087a74b0 | ||
|
6701403745 | ||
|
95c62f92ac | ||
|
0f54be93b7 | ||
|
8db134843f | ||
|
06e83c6154 | ||
|
cbfbec4dc3 | ||
|
386ac87d21 | ||
|
533aa97011 | ||
|
d118b70b30 | ||
|
aa8ac7bbb8 | ||
|
ad71e18e02 | ||
|
a00c7cb639 | ||
|
112520cf45 | ||
|
11d6f09359 | ||
|
3ed42af8a1 | ||
|
482b4119f6 | ||
|
f1d6325bcd | ||
|
e48671694e | ||
|
338f13e57b | ||
|
6cbc312f11 | ||
|
7a96321e78 | ||
|
6cfb9e89e7 | ||
|
a37ce1cb2f | ||
|
10fd29e7bb | ||
|
0370229444 | ||
|
82afd511fc | ||
|
ad3eafd735 | ||
|
4bc5f66aa5 | ||
|
730e6ae4cc | ||
|
892bb3ab01 | ||
|
e4e50308d2 | ||
|
cc518dcc3c | ||
|
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 | ||
|
2a90e12999 | ||
|
d129b72ced | ||
|
bc0b86891a | ||
|
52443936e6 | ||
|
6694a1c986 | ||
|
7ffaeb2ac1 | ||
|
67e6a6e742 | ||
|
9a3650c6ab |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
purify/*.o
|
||||||
|
purify/purify
|
||||||
|
purify/analyze-gba
|
@@ -1,41 +1,48 @@
|
|||||||
include nall/Makefile
|
include nall/Makefile
|
||||||
snes := snes
|
|
||||||
gameboy := gameboy
|
|
||||||
profile := accuracy
|
|
||||||
ui := ui
|
|
||||||
|
|
||||||
# debugger
|
nes := nes
|
||||||
options :=
|
snes := snes
|
||||||
|
gb := gb
|
||||||
|
gba := gba
|
||||||
|
|
||||||
|
profile := accuracy
|
||||||
|
target := ui
|
||||||
|
|
||||||
|
# options += console
|
||||||
|
|
||||||
# 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 := -s
|
||||||
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
|
flags += -march=native
|
||||||
|
link += -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 -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||||
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||||
link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
|
||||||
else
|
else
|
||||||
unknown_platform: help;
|
unknown_platform: help;
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ui := target-$(target)
|
||||||
|
|
||||||
# implicit rules
|
# implicit rules
|
||||||
compile = \
|
compile = \
|
||||||
$(strip \
|
$(strip \
|
||||||
@@ -54,6 +61,7 @@ all: build;
|
|||||||
obj/libco.o: libco/libco.c libco/*
|
obj/libco.o: libco/libco.c libco/*
|
||||||
|
|
||||||
include $(ui)/Makefile
|
include $(ui)/Makefile
|
||||||
|
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||||
|
|
||||||
# targets
|
# targets
|
||||||
clean:
|
clean:
|
||||||
@@ -69,7 +77,24 @@ clean:
|
|||||||
-@$(call delete,*.pdb)
|
-@$(call delete,*.pdb)
|
||||||
-@$(call delete,*.manifest)
|
-@$(call delete,*.manifest)
|
||||||
|
|
||||||
archive-all:
|
sync:
|
||||||
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
|
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:
|
||||||
|
if [ -f bsnes.tar.bz2 ]; then rm bsnes.tar.bz2; fi
|
||||||
|
tar -cjf bsnes.tar.bz2 `ls`
|
||||||
|
|
||||||
help:;
|
help:;
|
||||||
|
130
bsnes/base/base.hpp
Executable file
130
bsnes/base/base.hpp
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#ifndef BASE_HPP
|
||||||
|
#define BASE_HPP
|
||||||
|
|
||||||
|
static const char Version[] = "088";
|
||||||
|
|
||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/algorithm.hpp>
|
||||||
|
#include <nall/any.hpp>
|
||||||
|
#include <nall/array.hpp>
|
||||||
|
#include <nall/bitarray.hpp>
|
||||||
|
#include <nall/dl.hpp>
|
||||||
|
#include <nall/dsp.hpp>
|
||||||
|
#include <nall/endian.hpp>
|
||||||
|
#include <nall/file.hpp>
|
||||||
|
#include <nall/function.hpp>
|
||||||
|
#include <nall/moduloarray.hpp>
|
||||||
|
#include <nall/priorityqueue.hpp>
|
||||||
|
#include <nall/property.hpp>
|
||||||
|
#include <nall/random.hpp>
|
||||||
|
#include <nall/serializer.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
#include <nall/string.hpp>
|
||||||
|
#include <nall/utility.hpp>
|
||||||
|
#include <nall/varint.hpp>
|
||||||
|
#include <nall/vector.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
|
//debugging function hook:
|
||||||
|
//no overhead (and no debugger invocation) if not compiled with -DDEBUGGER
|
||||||
|
//wraps testing of function to allow invocation without a defined callback
|
||||||
|
template<typename T> struct hook;
|
||||||
|
template<typename R, typename... P> struct hook<R (P...)> {
|
||||||
|
function<R (P...)> callback;
|
||||||
|
|
||||||
|
R operator()(P... p) const {
|
||||||
|
#if defined(DEBUGGER)
|
||||||
|
if(callback) return callback(std::forward<P>(p)...);
|
||||||
|
#endif
|
||||||
|
return R();
|
||||||
|
}
|
||||||
|
|
||||||
|
hook() {}
|
||||||
|
hook(const hook &hook) { callback = hook.callback; }
|
||||||
|
hook(void *function) { callback = function; }
|
||||||
|
hook(R (*function)(P...)) { callback = function; }
|
||||||
|
template<typename C> hook(R (C::*function)(P...), C *object) { callback = { function, object }; }
|
||||||
|
template<typename C> hook(R (C::*function)(P...) const, C *object) { callback = { function, object }; }
|
||||||
|
template<typename L> hook(const L& function) { callback = function; }
|
||||||
|
|
||||||
|
hook& operator=(const hook& hook) { callback = hook.callback; return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(DEBUGGER)
|
||||||
|
#define privileged public
|
||||||
|
#else
|
||||||
|
#define privileged private
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int1_t int1;
|
||||||
|
typedef int2_t int2;
|
||||||
|
typedef int3_t int3;
|
||||||
|
typedef int4_t int4;
|
||||||
|
typedef int5_t int5;
|
||||||
|
typedef int6_t int6;
|
||||||
|
typedef int7_t int7;
|
||||||
|
typedef int8_t int8;
|
||||||
|
typedef int9_t int9;
|
||||||
|
typedef int10_t int10;
|
||||||
|
typedef int11_t int11;
|
||||||
|
typedef int12_t int12;
|
||||||
|
typedef int13_t int13;
|
||||||
|
typedef int14_t int14;
|
||||||
|
typedef int15_t int15;
|
||||||
|
typedef int16_t int16;
|
||||||
|
typedef int17_t int17;
|
||||||
|
typedef int18_t int18;
|
||||||
|
typedef int19_t int19;
|
||||||
|
typedef int20_t int20;
|
||||||
|
typedef int21_t int21;
|
||||||
|
typedef int22_t int22;
|
||||||
|
typedef int23_t int23;
|
||||||
|
typedef int24_t int24;
|
||||||
|
typedef int25_t int25;
|
||||||
|
typedef int26_t int26;
|
||||||
|
typedef int27_t int27;
|
||||||
|
typedef int28_t int28;
|
||||||
|
typedef int29_t int29;
|
||||||
|
typedef int30_t int30;
|
||||||
|
typedef int31_t int31;
|
||||||
|
typedef int32_t int32;
|
||||||
|
typedef int64_t int64;
|
||||||
|
|
||||||
|
typedef uint1_t uint1;
|
||||||
|
typedef uint2_t uint2;
|
||||||
|
typedef uint3_t uint3;
|
||||||
|
typedef uint4_t uint4;
|
||||||
|
typedef uint5_t uint5;
|
||||||
|
typedef uint6_t uint6;
|
||||||
|
typedef uint7_t uint7;
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef uint9_t uint9;
|
||||||
|
typedef uint10_t uint10;
|
||||||
|
typedef uint11_t uint11;
|
||||||
|
typedef uint12_t uint12;
|
||||||
|
typedef uint13_t uint13;
|
||||||
|
typedef uint14_t uint14;
|
||||||
|
typedef uint15_t uint15;
|
||||||
|
typedef uint16_t uint16;
|
||||||
|
typedef uint17_t uint17;
|
||||||
|
typedef uint18_t uint18;
|
||||||
|
typedef uint19_t uint19;
|
||||||
|
typedef uint20_t uint20;
|
||||||
|
typedef uint21_t uint21;
|
||||||
|
typedef uint22_t uint22;
|
||||||
|
typedef uint23_t uint23;
|
||||||
|
typedef uint24_t uint24;
|
||||||
|
typedef uint25_t uint25;
|
||||||
|
typedef uint26_t uint26;
|
||||||
|
typedef uint27_t uint27;
|
||||||
|
typedef uint28_t uint28;
|
||||||
|
typedef uint29_t uint29;
|
||||||
|
typedef uint30_t uint30;
|
||||||
|
typedef uint31_t uint31;
|
||||||
|
typedef uint32_t uint32;
|
||||||
|
typedef uint_t<33> uint33;
|
||||||
|
typedef uint64_t uint64;
|
||||||
|
|
||||||
|
typedef varuint_t<unsigned> varuint;
|
||||||
|
|
||||||
|
#endif
|
170384
bsnes/data/cheats.xml
170384
bsnes/data/cheats.xml
File diff suppressed because it is too large
Load Diff
3812
bsnes/data/laevateinn.hpp
Executable file
3812
bsnes/data/laevateinn.hpp
Executable file
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
|||||||
gameboy_objects := gameboy-system gameboy-scheduler
|
|
||||||
gameboy_objects += gameboy-memory gameboy-cartridge
|
|
||||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
|
||||||
objects += $(gameboy_objects)
|
|
||||||
|
|
||||||
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-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
|
||||||
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
|
|
||||||
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-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/)
|
|
@@ -1,132 +0,0 @@
|
|||||||
#ifdef APU_CPP
|
|
||||||
|
|
||||||
void APU::Master::run() {
|
|
||||||
if(enable == false) {
|
|
||||||
center = 0;
|
|
||||||
left = 0;
|
|
||||||
right = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signed sample = 0, channels;
|
|
||||||
sample += apu.square1.output;
|
|
||||||
sample += apu.square2.output;
|
|
||||||
sample += apu.wave.output;
|
|
||||||
sample += apu.noise.output;
|
|
||||||
sample >>= 2;
|
|
||||||
center = sclamp<16>(sample);
|
|
||||||
|
|
||||||
if(left_enable == false && right_enable == false) {
|
|
||||||
left = center;
|
|
||||||
right = center;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample = 0;
|
|
||||||
channels = 0;
|
|
||||||
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
|
|
||||||
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
|
|
||||||
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
|
|
||||||
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
|
|
||||||
if(channels) sample /= channels;
|
|
||||||
left = sclamp<16>(sample);
|
|
||||||
|
|
||||||
switch(left_volume) {
|
|
||||||
case 0: left >>= 3; break; // 12.5%
|
|
||||||
case 1: left >>= 2; break; // 25.0%
|
|
||||||
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
|
|
||||||
case 3: left >>= 1; break; // 50.0%
|
|
||||||
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
|
|
||||||
case 5: left -= (left >> 2); break; // 75.0%
|
|
||||||
case 6: left -= (left >> 3); break; // 87.5%
|
|
||||||
//case 7: break; //100.0%
|
|
||||||
}
|
|
||||||
if(left_enable == false) left = 0;
|
|
||||||
|
|
||||||
sample = 0;
|
|
||||||
channels = 0;
|
|
||||||
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
|
|
||||||
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
|
|
||||||
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
|
|
||||||
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
|
|
||||||
if(channels) sample /= channels;
|
|
||||||
right = sclamp<16>(sample);
|
|
||||||
|
|
||||||
switch(right_volume) {
|
|
||||||
case 0: right >>= 3; break; // 12.5%
|
|
||||||
case 1: right >>= 2; break; // 25.0%
|
|
||||||
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
|
|
||||||
case 3: right >>= 1; break; // 50.0%
|
|
||||||
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
|
|
||||||
case 5: right -= (right >> 2); break; // 75.0%
|
|
||||||
case 6: right -= (right >> 3); break; // 87.5%
|
|
||||||
//case 7: break; //100.0%
|
|
||||||
}
|
|
||||||
if(right_enable == false) right = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APU::Master::write(unsigned r, uint8 data) {
|
|
||||||
if(r == 0) {
|
|
||||||
left_enable = data & 0x80;
|
|
||||||
left_volume = (data >> 4) & 7;
|
|
||||||
right_enable = data & 0x08;
|
|
||||||
right_volume = (data >> 0) & 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(r == 1) {
|
|
||||||
channel4_left_enable = data & 0x80;
|
|
||||||
channel3_left_enable = data & 0x40;
|
|
||||||
channel2_left_enable = data & 0x20;
|
|
||||||
channel1_left_enable = data & 0x10;
|
|
||||||
channel4_right_enable = data & 0x08;
|
|
||||||
channel3_right_enable = data & 0x04;
|
|
||||||
channel2_right_enable = data & 0x02;
|
|
||||||
channel1_right_enable = data & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(r == 2) {
|
|
||||||
enable = data & 0x80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void APU::Master::power() {
|
|
||||||
left_enable = 0;
|
|
||||||
left_volume = 0;
|
|
||||||
right_enable = 0;
|
|
||||||
right_volume = 0;
|
|
||||||
channel4_left_enable = 0;
|
|
||||||
channel3_left_enable = 0;
|
|
||||||
channel2_left_enable = 0;
|
|
||||||
channel1_left_enable = 0;
|
|
||||||
channel4_right_enable = 0;
|
|
||||||
channel3_right_enable = 0;
|
|
||||||
channel2_right_enable = 0;
|
|
||||||
channel1_right_enable = 0;
|
|
||||||
enable = 0;
|
|
||||||
|
|
||||||
center = 0;
|
|
||||||
left = 0;
|
|
||||||
right = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APU::Master::serialize(serializer &s) {
|
|
||||||
s.integer(left_enable);
|
|
||||||
s.integer(left_volume);
|
|
||||||
s.integer(right_enable);
|
|
||||||
s.integer(right_volume);
|
|
||||||
s.integer(channel4_left_enable);
|
|
||||||
s.integer(channel3_left_enable);
|
|
||||||
s.integer(channel2_left_enable);
|
|
||||||
s.integer(channel1_left_enable);
|
|
||||||
s.integer(channel4_right_enable);
|
|
||||||
s.integer(channel3_right_enable);
|
|
||||||
s.integer(channel2_right_enable);
|
|
||||||
s.integer(channel1_right_enable);
|
|
||||||
s.integer(enable);
|
|
||||||
|
|
||||||
s.integer(center);
|
|
||||||
s.integer(left);
|
|
||||||
s.integer(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,31 +0,0 @@
|
|||||||
struct Square1 {
|
|
||||||
unsigned sweep_frequency;
|
|
||||||
unsigned sweep_direction;
|
|
||||||
unsigned sweep_shift;
|
|
||||||
unsigned duty;
|
|
||||||
unsigned length;
|
|
||||||
unsigned envelope_volume;
|
|
||||||
unsigned envelope_direction;
|
|
||||||
unsigned envelope_frequency;
|
|
||||||
unsigned frequency;
|
|
||||||
unsigned counter;
|
|
||||||
|
|
||||||
int16 output;
|
|
||||||
bool duty_output;
|
|
||||||
unsigned phase;
|
|
||||||
unsigned period;
|
|
||||||
unsigned envelope_period;
|
|
||||||
unsigned sweep_period;
|
|
||||||
signed frequency_shadow;
|
|
||||||
bool enable;
|
|
||||||
unsigned volume;
|
|
||||||
|
|
||||||
void run();
|
|
||||||
void sweep();
|
|
||||||
void clock_length();
|
|
||||||
void clock_sweep();
|
|
||||||
void clock_envelope();
|
|
||||||
void write(unsigned r, uint8 data);
|
|
||||||
void power();
|
|
||||||
void serialize(serializer&);
|
|
||||||
};
|
|
@@ -1,23 +0,0 @@
|
|||||||
struct Square2 {
|
|
||||||
unsigned duty;
|
|
||||||
unsigned length;
|
|
||||||
unsigned envelope_volume;
|
|
||||||
unsigned envelope_direction;
|
|
||||||
unsigned envelope_frequency;
|
|
||||||
unsigned frequency;
|
|
||||||
unsigned counter;
|
|
||||||
|
|
||||||
int16 output;
|
|
||||||
bool duty_output;
|
|
||||||
unsigned phase;
|
|
||||||
unsigned period;
|
|
||||||
unsigned envelope_period;
|
|
||||||
unsigned volume;
|
|
||||||
|
|
||||||
void run();
|
|
||||||
void clock_length();
|
|
||||||
void clock_envelope();
|
|
||||||
void write(unsigned r, uint8 data);
|
|
||||||
void power();
|
|
||||||
void serialize(serializer&);
|
|
||||||
};
|
|
@@ -1,53 +0,0 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
|
||||||
|
|
||||||
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
|
|
||||||
if(within<0x0000, 0x3fff>(addr)) {
|
|
||||||
return cartridge.rom_read(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0x4000, 0x7fff>(addr)) {
|
|
||||||
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
|
||||||
if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
|
||||||
return 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
|
|
||||||
if(within<0x0000, 0x1fff>(addr)) {
|
|
||||||
ram_enable = (data & 0x0f) == 0x0a;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0x2000, 0x3fff>(addr)) {
|
|
||||||
rom_select = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0x4000, 0x5fff>(addr)) {
|
|
||||||
ram_select = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0x6000, 0x7fff>(addr)) {
|
|
||||||
//unknown purpose
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(within<0xa000, 0xbfff>(addr)) {
|
|
||||||
if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cartridge::HuC1::power() {
|
|
||||||
ram_enable = false;
|
|
||||||
rom_select = 0x01;
|
|
||||||
ram_select = 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,9 +0,0 @@
|
|||||||
struct HuC1 : MMIO {
|
|
||||||
bool ram_enable; //0000-1fff
|
|
||||||
uint8 rom_select; //2000-3fff
|
|
||||||
uint8 ram_select; //4000-5fff
|
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
|
||||||
void power();
|
|
||||||
} huc1;
|
|
@@ -1,10 +0,0 @@
|
|||||||
struct MBC1 : MMIO {
|
|
||||||
bool ram_enable; //0000-1fff
|
|
||||||
uint8 rom_select; //2000-3fff
|
|
||||||
uint8 ram_select; //4000-5fff
|
|
||||||
bool mode_select; //6000-7fff
|
|
||||||
|
|
||||||
uint8 mmio_read(uint16 addr);
|
|
||||||
void mmio_write(uint16 addr, uint8 data);
|
|
||||||
void power();
|
|
||||||
} mbc1;
|
|
@@ -1,144 +0,0 @@
|
|||||||
#ifdef CPU_CPP
|
|
||||||
|
|
||||||
void CPU::mmio_joyp_poll() {
|
|
||||||
unsigned button = 0, dpad = 0;
|
|
||||||
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::B) << 1;
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::A) << 0;
|
|
||||||
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
|
|
||||||
|
|
||||||
status.joyp = 0x0f;
|
|
||||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
|
||||||
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
|
|
||||||
if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;
|
|
||||||
if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 CPU::mmio_read(uint16 addr) {
|
|
||||||
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
|
|
||||||
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
|
|
||||||
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
|
||||||
|
|
||||||
if(addr == 0xff00) { //JOYP
|
|
||||||
return (status.p15 << 5)
|
|
||||||
| (status.p14 << 4)
|
|
||||||
| (status.joyp << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff01) { //SB
|
|
||||||
return 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff02) { //SC
|
|
||||||
return (status.serial_transfer << 7)
|
|
||||||
| (status.serial_clock << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff04) { //DIV
|
|
||||||
return status.div;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff05) { //TIMA
|
|
||||||
return status.tima;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff06) { //TMA
|
|
||||||
return status.tma;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff07) { //TAC
|
|
||||||
return (status.timer_enable << 2)
|
|
||||||
| (status.timer_clock << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff0f) { //IF
|
|
||||||
return (status.interrupt_request_joypad << 4)
|
|
||||||
| (status.interrupt_request_serial << 3)
|
|
||||||
| (status.interrupt_request_timer << 2)
|
|
||||||
| (status.interrupt_request_stat << 1)
|
|
||||||
| (status.interrupt_request_vblank << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xffff) { //IE
|
|
||||||
return (status.interrupt_enable_joypad << 4)
|
|
||||||
| (status.interrupt_enable_serial << 3)
|
|
||||||
| (status.interrupt_enable_timer << 2)
|
|
||||||
| (status.interrupt_enable_stat << 1)
|
|
||||||
| (status.interrupt_enable_vblank << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPU::mmio_write(uint16 addr, uint8 data) {
|
|
||||||
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
|
|
||||||
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
|
|
||||||
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
|
|
||||||
|
|
||||||
if(addr == 0xff00) { //JOYP
|
|
||||||
status.p15 = data & 0x20;
|
|
||||||
status.p14 = data & 0x10;
|
|
||||||
system.interface->joyp_write(status.p15, status.p14);
|
|
||||||
mmio_joyp_poll();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff01) { //SB
|
|
||||||
status.serial_data = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff02) { //SC
|
|
||||||
status.serial_transfer = data & 0x80;
|
|
||||||
status.serial_clock = data & 0x01;
|
|
||||||
if(status.serial_transfer) status.serial_bits = 8;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff04) { //DIV
|
|
||||||
status.div = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff05) { //TIMA
|
|
||||||
status.tima = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff06) { //TMA
|
|
||||||
status.tma = data;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff07) { //TAC
|
|
||||||
status.timer_enable = data & 0x04;
|
|
||||||
status.timer_clock = data & 0x03;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xff0f) { //IF
|
|
||||||
status.interrupt_request_joypad = data & 0x10;
|
|
||||||
status.interrupt_request_serial = data & 0x08;
|
|
||||||
status.interrupt_request_timer = data & 0x04;
|
|
||||||
status.interrupt_request_stat = data & 0x02;
|
|
||||||
status.interrupt_request_vblank = data & 0x01;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(addr == 0xffff) { //IE
|
|
||||||
status.interrupt_enable_joypad = data & 0x10;
|
|
||||||
status.interrupt_enable_serial = data & 0x08;
|
|
||||||
status.interrupt_enable_timer = data & 0x04;
|
|
||||||
status.interrupt_enable_stat = data & 0x02;
|
|
||||||
status.interrupt_enable_vblank = data & 0x01;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,96 +0,0 @@
|
|||||||
//bgameboy
|
|
||||||
//author: byuu
|
|
||||||
//project started: 2010-12-27
|
|
||||||
|
|
||||||
namespace GameBoy {
|
|
||||||
namespace Info {
|
|
||||||
static const char Name[] = "bgameboy";
|
|
||||||
static const char Version[] = "000.19";
|
|
||||||
static unsigned SerializerVersion = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <libco/libco.h>
|
|
||||||
|
|
||||||
#include <nall/foreach.hpp>
|
|
||||||
#include <nall/platform.hpp>
|
|
||||||
#include <nall/property.hpp>
|
|
||||||
#include <nall/random.hpp>
|
|
||||||
#include <nall/serializer.hpp>
|
|
||||||
#include <nall/stdint.hpp>
|
|
||||||
#include <nall/string.hpp>
|
|
||||||
#include <nall/varint.hpp>
|
|
||||||
using namespace nall;
|
|
||||||
|
|
||||||
namespace GameBoy {
|
|
||||||
typedef int8_t int8;
|
|
||||||
typedef int16_t int16;
|
|
||||||
typedef int32_t int32;
|
|
||||||
typedef int64_t int64;
|
|
||||||
|
|
||||||
typedef uint8_t uint8;
|
|
||||||
typedef uint16_t uint16;
|
|
||||||
typedef uint32_t uint32;
|
|
||||||
typedef uint64_t uint64;
|
|
||||||
|
|
||||||
typedef uint_t< 1> uint1;
|
|
||||||
typedef uint_t< 2> uint2;
|
|
||||||
typedef uint_t< 3> uint3;
|
|
||||||
typedef uint_t< 4> uint4;
|
|
||||||
typedef uint_t< 5> uint5;
|
|
||||||
typedef uint_t< 6> uint6;
|
|
||||||
typedef uint_t< 7> uint7;
|
|
||||||
|
|
||||||
typedef uint_t< 9> uint9;
|
|
||||||
typedef uint_t<10> uint10;
|
|
||||||
typedef uint_t<11> uint11;
|
|
||||||
typedef uint_t<12> uint12;
|
|
||||||
typedef uint_t<13> uint13;
|
|
||||||
typedef uint_t<14> uint14;
|
|
||||||
typedef uint_t<15> uint15;
|
|
||||||
|
|
||||||
typedef uint_t<17> uint17;
|
|
||||||
typedef uint_t<18> uint18;
|
|
||||||
typedef uint_t<19> uint19;
|
|
||||||
typedef uint_t<20> uint20;
|
|
||||||
typedef uint_t<21> uint21;
|
|
||||||
typedef uint_t<22> uint22;
|
|
||||||
typedef uint_t<23> uint23;
|
|
||||||
typedef uint_t<24> uint24;
|
|
||||||
typedef uint_t<25> uint25;
|
|
||||||
typedef uint_t<26> uint26;
|
|
||||||
typedef uint_t<27> uint27;
|
|
||||||
typedef uint_t<28> uint28;
|
|
||||||
typedef uint_t<29> uint29;
|
|
||||||
typedef uint_t<30> uint30;
|
|
||||||
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 {
|
|
||||||
cothread_t thread;
|
|
||||||
unsigned frequency;
|
|
||||||
int64 clock;
|
|
||||||
|
|
||||||
inline void create(void (*entrypoint_)(), unsigned frequency_) {
|
|
||||||
if(thread) co_delete(thread);
|
|
||||||
thread = co_create(65536 * sizeof(void*), entrypoint_);
|
|
||||||
frequency = frequency_;
|
|
||||||
clock = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Processor() : thread(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <gameboy/memory/memory.hpp>
|
|
||||||
#include <gameboy/system/system.hpp>
|
|
||||||
#include <gameboy/scheduler/scheduler.hpp>
|
|
||||||
#include <gameboy/cartridge/cartridge.hpp>
|
|
||||||
#include <gameboy/cpu/cpu.hpp>
|
|
||||||
#include <gameboy/apu/apu.hpp>
|
|
||||||
#include <gameboy/lcd/lcd.hpp>
|
|
||||||
};
|
|
@@ -1,11 +0,0 @@
|
|||||||
class Interface {
|
|
||||||
public:
|
|
||||||
virtual void joyp_write(bool p15, bool p14) {}
|
|
||||||
|
|
||||||
virtual void video_refresh(const uint8_t *data) {}
|
|
||||||
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
|
|
||||||
virtual void input_poll() {}
|
|
||||||
virtual bool input_poll(unsigned id) {}
|
|
||||||
|
|
||||||
virtual void message(const string &text) { print(text, "\n"); }
|
|
||||||
};
|
|
@@ -1,238 +0,0 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
|
||||||
|
|
||||||
#define LCD_CPP
|
|
||||||
namespace GameBoy {
|
|
||||||
|
|
||||||
#include "mmio/mmio.cpp"
|
|
||||||
#include "serialization.cpp"
|
|
||||||
LCD lcd;
|
|
||||||
|
|
||||||
void LCD::Main() {
|
|
||||||
lcd.main();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::main() {
|
|
||||||
while(true) {
|
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_clocks(4);
|
|
||||||
|
|
||||||
if(status.lx == 0) {
|
|
||||||
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(status.lx == 252) {
|
|
||||||
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::add_clocks(unsigned clocks) {
|
|
||||||
status.lx += clocks;
|
|
||||||
if(status.lx >= 456) scanline();
|
|
||||||
|
|
||||||
clock += clocks;
|
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
|
||||||
co_switch(scheduler.active_thread = cpu.thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::scanline() {
|
|
||||||
status.lx -= 456;
|
|
||||||
if(++status.ly == 154) frame();
|
|
||||||
|
|
||||||
if(status.interrupt_lyc == true) {
|
|
||||||
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(status.ly < 144) render();
|
|
||||||
|
|
||||||
if(status.ly == 144) {
|
|
||||||
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
|
||||||
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::frame() {
|
|
||||||
system.interface->video_refresh(screen);
|
|
||||||
system.interface->input_poll();
|
|
||||||
cpu.mmio_joyp_poll();
|
|
||||||
|
|
||||||
status.ly = 0;
|
|
||||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCD::render() {
|
|
||||||
for(unsigned n = 0; n < 160; n++) line[n] = 0x00;
|
|
||||||
|
|
||||||
if(status.display_enable == true) {
|
|
||||||
if(status.bg_enable == true) render_bg();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
create(Main, 4194304);
|
|
||||||
|
|
||||||
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 = 0; n < 8192; n++) vram[n] = 0x00;
|
|
||||||
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
|
|
||||||
|
|
||||||
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
|
|
||||||
|
|
||||||
status.lx = 0;
|
|
||||||
|
|
||||||
status.display_enable = 0;
|
|
||||||
status.window_tilemap_select = 0;
|
|
||||||
status.window_display_enable = 0;
|
|
||||||
status.bg_tiledata_select = 0;
|
|
||||||
status.bg_tilemap_select = 0;
|
|
||||||
status.obj_size = 0;
|
|
||||||
status.obj_enable = 0;
|
|
||||||
status.bg_enable = 0;
|
|
||||||
|
|
||||||
status.interrupt_lyc = 0;
|
|
||||||
status.interrupt_oam = 0;
|
|
||||||
status.interrupt_vblank = 0;
|
|
||||||
status.interrupt_hblank = 0;
|
|
||||||
|
|
||||||
status.scy = 0;
|
|
||||||
status.scx = 0;
|
|
||||||
status.ly = 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.wx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LCD::LCD() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,71 +0,0 @@
|
|||||||
struct LCD : Processor, MMIO {
|
|
||||||
#include "mmio/mmio.hpp"
|
|
||||||
|
|
||||||
struct Status {
|
|
||||||
unsigned lx;
|
|
||||||
|
|
||||||
//$ff40 LCDC
|
|
||||||
bool display_enable;
|
|
||||||
bool window_tilemap_select;
|
|
||||||
bool window_display_enable;
|
|
||||||
bool bg_tiledata_select;
|
|
||||||
bool bg_tilemap_select;
|
|
||||||
bool obj_size;
|
|
||||||
bool obj_enable;
|
|
||||||
bool bg_enable;
|
|
||||||
|
|
||||||
//$ff41 STAT
|
|
||||||
bool interrupt_lyc;
|
|
||||||
bool interrupt_oam;
|
|
||||||
bool interrupt_vblank;
|
|
||||||
bool interrupt_hblank;
|
|
||||||
|
|
||||||
//$ff42 SCY
|
|
||||||
uint8 scy;
|
|
||||||
|
|
||||||
//$ff43 SCX
|
|
||||||
uint8 scx;
|
|
||||||
|
|
||||||
//$ff44 LY
|
|
||||||
uint8 ly;
|
|
||||||
|
|
||||||
//$ff45 LYC
|
|
||||||
uint8 lyc;
|
|
||||||
|
|
||||||
//$ff47 BGP
|
|
||||||
uint8 bgp[4];
|
|
||||||
|
|
||||||
//$ff48 OBP0
|
|
||||||
//$ff49 OBP1
|
|
||||||
uint8 obp[2][4];
|
|
||||||
|
|
||||||
//$ff4a WY
|
|
||||||
uint8 wy;
|
|
||||||
|
|
||||||
//$ff4b WX
|
|
||||||
uint8 wx;
|
|
||||||
} status;
|
|
||||||
|
|
||||||
uint8 screen[160 * 144];
|
|
||||||
uint8 vram[8192];
|
|
||||||
uint8 oam[160];
|
|
||||||
uint8 line[160];
|
|
||||||
|
|
||||||
static void Main();
|
|
||||||
void main();
|
|
||||||
void add_clocks(unsigned clocks);
|
|
||||||
void scanline();
|
|
||||||
void frame();
|
|
||||||
void render();
|
|
||||||
uint16 read_tile(bool select, unsigned x, unsigned y);
|
|
||||||
void render_bg();
|
|
||||||
void render_window();
|
|
||||||
void render_obj();
|
|
||||||
|
|
||||||
void power();
|
|
||||||
|
|
||||||
void serialize(serializer&);
|
|
||||||
LCD();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern LCD lcd;
|
|
@@ -1,23 +0,0 @@
|
|||||||
#ifdef SYSTEM_CPP
|
|
||||||
|
|
||||||
//MD5SUM = 32fbbd84168d3482956eb3c5051637f5
|
|
||||||
const uint8_t System::BootROM::dmg[256] = {
|
|
||||||
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,
|
|
||||||
0x47,0x11,0x04,0x01,0x21,0x10,0x80,0x1a,0xcd,0x95,0x00,0xcd,0x96,0x00,0x13,0x7b,
|
|
||||||
0xfe,0x34,0x20,0xf3,0x11,0xd8,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,
|
|
||||||
0x3e,0x19,0xea,0x10,0x99,0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,
|
|
||||||
0xf9,0x2e,0x0f,0x18,0xf3,0x67,0x3e,0x64,0x57,0xe0,0x42,0x3e,0x91,0xe0,0x40,0x04,
|
|
||||||
0x1e,0x02,0x0e,0x0c,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x0d,0x20,0xf7,0x1d,0x20,0xf2,
|
|
||||||
0x0e,0x13,0x24,0x7c,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x06,
|
|
||||||
0x7b,0xe2,0x0c,0x3e,0x87,0xe2,0xf0,0x42,0x90,0xe0,0x42,0x15,0x20,0xd2,0x05,0x20,
|
|
||||||
0x4f,0x16,0x20,0x18,0xcb,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,
|
|
||||||
0x05,0x20,0xf5,0x22,0x23,0x22,0x23,0xc9,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,
|
|
||||||
0x21,0x04,0x01,0x11,0xa8,0x00,0x1a,0x13,0xbe,0x20,0xfe,0x23,0x7d,0xfe,0x34,0x20,
|
|
||||||
0xf5,0x06,0x19,0x78,0x86,0x23,0x05,0x20,0xfb,0x86,0x20,0xfe,0x3e,0x01,0xe0,0x50,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,23 +0,0 @@
|
|||||||
#ifdef SYSTEM_CPP
|
|
||||||
|
|
||||||
//MD5SUM = d574d4f9c12f305074798f54c091a8b4
|
|
||||||
const uint8_t System::BootROM::sgb[256] = {
|
|
||||||
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,
|
|
||||||
0x77,0x3e,0xfc,0xe0,0x47,0x21,0x5f,0xc0,0x0e,0x08,0xaf,0x32,0x0d,0x20,0xfc,0x11,
|
|
||||||
0x4f,0x01,0x3e,0xfb,0x0e,0x06,0xf5,0x06,0x00,0x1a,0x1b,0x32,0x80,0x47,0x0d,0x20,
|
|
||||||
0xf8,0x32,0xf1,0x32,0x0e,0x0e,0xd6,0x02,0xfe,0xef,0x20,0xea,0x11,0x04,0x01,0x21,
|
|
||||||
0x10,0x80,0x1a,0xcd,0xd3,0x00,0xcd,0xd4,0x00,0x13,0x7b,0xfe,0x34,0x20,0xf3,0x11,
|
|
||||||
0xe6,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0x3e,0x19,0xea,0x10,0x99,
|
|
||||||
0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,
|
|
||||||
0x3e,0x91,0xe0,0x40,0x21,0x00,0xc0,0x0e,0x00,0x3e,0x00,0xe2,0x3e,0x30,0xe2,0x06,
|
|
||||||
0x10,0x1e,0x08,0x2a,0x57,0xcb,0x42,0x3e,0x10,0x20,0x02,0x3e,0x20,0xe2,0x3e,0x30,
|
|
||||||
0xe2,0xcb,0x1a,0x1d,0x20,0xef,0x05,0x20,0xe8,0x3e,0x20,0xe2,0x3e,0x30,0xe2,0xcd,
|
|
||||||
0xc2,0x00,0x7d,0xfe,0x60,0x20,0xd2,0x0e,0x13,0x3e,0xc1,0xe2,0x0c,0x3e,0x07,0xe2,
|
|
||||||
0x18,0x3a,0x16,0x04,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x1e,0x00,0x1d,0x20,0xfd,0x15,
|
|
||||||
0x20,0xf2,0xc9,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,0x05,0x20,
|
|
||||||
0xf5,0x22,0x23,0x22,0x23,0xc9,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x00,0x00,
|
|
||||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x01,0xe0,0x50,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
18
bsnes/gb/Makefile
Executable file
18
bsnes/gb/Makefile
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
options += gameboy
|
||||||
|
|
||||||
|
gb_objects := gb-interface gb-system gb-scheduler
|
||||||
|
gb_objects += gb-memory gb-cartridge
|
||||||
|
gb_objects += gb-cpu gb-apu gb-lcd
|
||||||
|
gb_objects += gb-cheat gb-video
|
||||||
|
objects += $(gb_objects)
|
||||||
|
|
||||||
|
obj/gb-interface.o: $(gb)/interface/interface.cpp $(call rwildcard,$(gb)/interface/)
|
||||||
|
obj/gb-system.o: $(gb)/system/system.cpp $(call rwildcard,$(gb)/system/)
|
||||||
|
obj/gb-scheduler.o: $(gb)/scheduler/scheduler.cpp $(call rwildcard,$(gb)/scheduler/)
|
||||||
|
obj/gb-cartridge.o: $(gb)/cartridge/cartridge.cpp $(call rwildcard,$(gb)/cartridge/)
|
||||||
|
obj/gb-memory.o: $(gb)/memory/memory.cpp $(call rwildcard,$(gb)/memory/)
|
||||||
|
obj/gb-cpu.o: $(gb)/cpu/cpu.cpp $(call rwildcard,$(gb)/cpu/)
|
||||||
|
obj/gb-apu.o: $(gb)/apu/apu.cpp $(call rwildcard,$(gb)/apu/)
|
||||||
|
obj/gb-lcd.o: $(gb)/lcd/lcd.cpp $(call rwildcard,$(gb)/lcd/)
|
||||||
|
obj/gb-cheat.o: $(gb)/cheat/cheat.cpp $(call rwildcard,$(gb)/cheat/)
|
||||||
|
obj/gb-video.o: $(gb)/video/video.cpp $(call rwildcard,$(gb)/video/)
|
@@ -1,7 +1,7 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#define APU_CPP
|
#define APU_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
#include "square1/square1.cpp"
|
#include "square1/square1.cpp"
|
||||||
#include "square2/square2.cpp"
|
#include "square2/square2.cpp"
|
||||||
@@ -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 && scheduler.sync != Scheduler::SynchronizeMode::All) 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];
|
||||||
}
|
}
|
||||||
|
|
@@ -1,4 +1,4 @@
|
|||||||
struct APU : Processor, MMIO {
|
struct APU : Thread, MMIO {
|
||||||
#include "square1/square1.hpp"
|
#include "square1/square1.hpp"
|
||||||
#include "square2/square2.hpp"
|
#include "square2/square2.hpp"
|
||||||
#include "wave/wave.hpp"
|
#include "wave/wave.hpp"
|
101
bsnes/gb/apu/master/master.cpp
Executable file
101
bsnes/gb/apu/master/master.cpp
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#ifdef APU_CPP
|
||||||
|
|
||||||
|
void APU::Master::run() {
|
||||||
|
if(enable == false) {
|
||||||
|
center = 0;
|
||||||
|
left = 0;
|
||||||
|
right = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed sample = 0;
|
||||||
|
sample += apu.square1.output;
|
||||||
|
sample += apu.square2.output;
|
||||||
|
sample += apu.wave.output;
|
||||||
|
sample += apu.noise.output;
|
||||||
|
center = (sample * 512) - 16384;
|
||||||
|
|
||||||
|
sample = 0;
|
||||||
|
if(channel1_left_enable) sample += apu.square1.output;
|
||||||
|
if(channel2_left_enable) sample += apu.square2.output;
|
||||||
|
if(channel3_left_enable) sample += apu.wave.output;
|
||||||
|
if(channel4_left_enable) sample += apu.noise.output;
|
||||||
|
sample = (sample * 512) - 16384;
|
||||||
|
sample = (sample * (left_volume + 1)) / 8;
|
||||||
|
left = sample;
|
||||||
|
|
||||||
|
sample = 0;
|
||||||
|
if(channel1_right_enable) sample += apu.square1.output;
|
||||||
|
if(channel2_right_enable) sample += apu.square2.output;
|
||||||
|
if(channel3_right_enable) sample += apu.wave.output;
|
||||||
|
if(channel4_right_enable) sample += apu.noise.output;
|
||||||
|
sample = (sample * 512) - 16384;
|
||||||
|
sample = (sample * (right_volume + 1)) / 8;
|
||||||
|
right = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Master::write(unsigned r, uint8 data) {
|
||||||
|
if(r == 0) { //$ff24 NR50
|
||||||
|
left_in_enable = data & 0x80;
|
||||||
|
left_volume = (data >> 4) & 7;
|
||||||
|
right_in_enable = data & 0x08;
|
||||||
|
right_volume = (data >> 0) & 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(r == 1) { //$ff25 NR51
|
||||||
|
channel4_left_enable = data & 0x80;
|
||||||
|
channel3_left_enable = data & 0x40;
|
||||||
|
channel2_left_enable = data & 0x20;
|
||||||
|
channel1_left_enable = data & 0x10;
|
||||||
|
channel4_right_enable = data & 0x08;
|
||||||
|
channel3_right_enable = data & 0x04;
|
||||||
|
channel2_right_enable = data & 0x02;
|
||||||
|
channel1_right_enable = data & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(r == 2) { //$ff26 NR52
|
||||||
|
enable = data & 0x80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Master::power() {
|
||||||
|
left_in_enable = 0;
|
||||||
|
left_volume = 0;
|
||||||
|
right_in_enable = 0;
|
||||||
|
right_volume = 0;
|
||||||
|
channel4_left_enable = 0;
|
||||||
|
channel3_left_enable = 0;
|
||||||
|
channel2_left_enable = 0;
|
||||||
|
channel1_left_enable = 0;
|
||||||
|
channel4_right_enable = 0;
|
||||||
|
channel3_right_enable = 0;
|
||||||
|
channel2_right_enable = 0;
|
||||||
|
channel1_right_enable = 0;
|
||||||
|
enable = 0;
|
||||||
|
|
||||||
|
center = 0;
|
||||||
|
left = 0;
|
||||||
|
right = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Master::serialize(serializer &s) {
|
||||||
|
s.integer(left_in_enable);
|
||||||
|
s.integer(left_volume);
|
||||||
|
s.integer(right_in_enable);
|
||||||
|
s.integer(right_volume);
|
||||||
|
s.integer(channel4_left_enable);
|
||||||
|
s.integer(channel3_left_enable);
|
||||||
|
s.integer(channel2_left_enable);
|
||||||
|
s.integer(channel1_left_enable);
|
||||||
|
s.integer(channel4_right_enable);
|
||||||
|
s.integer(channel3_right_enable);
|
||||||
|
s.integer(channel2_right_enable);
|
||||||
|
s.integer(channel1_right_enable);
|
||||||
|
s.integer(enable);
|
||||||
|
|
||||||
|
s.integer(center);
|
||||||
|
s.integer(left);
|
||||||
|
s.integer(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -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,35 @@
|
|||||||
#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;
|
||||||
|
//}
|
||||||
|
if(enable && counter) {
|
||||||
|
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 +37,43 @@ 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 = 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 +91,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;
|
uint6 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) {
|
||||||
|
Thread::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,48 @@ 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;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if(counter && enable) {
|
||||||
|
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,49 +65,57 @@ 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 = 64 - (data & 0x3f);
|
||||||
length = data & 0x3f;
|
length = 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();
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
period = 4 * (2048 - frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +131,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 +157,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);
|
||||||
}
|
}
|
||||||
|
|
36
bsnes/gb/apu/square1/square1.hpp
Executable file
36
bsnes/gb/apu/square1/square1.hpp
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
struct Square1 {
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
uint3 sweep_frequency;
|
||||||
|
bool sweep_direction;
|
||||||
|
uint3 sweep_shift;
|
||||||
|
bool sweep_negate;
|
||||||
|
uint2 duty;
|
||||||
|
uint6 length;
|
||||||
|
uint4 envelope_volume;
|
||||||
|
bool envelope_direction;
|
||||||
|
uint3 envelope_frequency;
|
||||||
|
uint11 frequency;
|
||||||
|
bool counter;
|
||||||
|
|
||||||
|
int16 output;
|
||||||
|
bool duty_output;
|
||||||
|
uint3 phase;
|
||||||
|
unsigned period;
|
||||||
|
uint3 envelope_period;
|
||||||
|
uint3 sweep_period;
|
||||||
|
signed frequency_shadow;
|
||||||
|
bool sweep_enable;
|
||||||
|
uint4 volume;
|
||||||
|
|
||||||
|
bool dac_enable();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void sweep(bool update);
|
||||||
|
void clock_length();
|
||||||
|
void clock_sweep();
|
||||||
|
void clock_envelope();
|
||||||
|
void write(unsigned r, uint8 data);
|
||||||
|
void power();
|
||||||
|
void serialize(serializer&);
|
||||||
|
};
|
@@ -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,24 @@ 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;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if(counter && enable) {
|
||||||
|
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,36 +41,41 @@ 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);
|
||||||
|
length = (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();
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
envelope_period = envelope_frequency;
|
envelope_period = envelope_frequency;
|
||||||
volume = envelope_volume;
|
volume = envelope_volume;
|
||||||
|
//if(length == 0) length = 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
period = 4 * (2048 - frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +93,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);
|
27
bsnes/gb/apu/square2/square2.hpp
Executable file
27
bsnes/gb/apu/square2/square2.hpp
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
struct Square2 {
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
uint2 duty;
|
||||||
|
uint6 length;
|
||||||
|
uint4 envelope_volume;
|
||||||
|
bool envelope_direction;
|
||||||
|
uint3 envelope_frequency;
|
||||||
|
uint11 frequency;
|
||||||
|
bool counter;
|
||||||
|
|
||||||
|
int16 output;
|
||||||
|
bool duty_output;
|
||||||
|
uint3 phase;
|
||||||
|
unsigned period;
|
||||||
|
uint3 envelope_period;
|
||||||
|
uint4 volume;
|
||||||
|
|
||||||
|
bool dac_enable();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void clock_length();
|
||||||
|
void clock_envelope();
|
||||||
|
void write(unsigned r, uint8 data);
|
||||||
|
void power();
|
||||||
|
void serialize(serializer&);
|
||||||
|
};
|
@@ -3,61 +3,60 @@
|
|||||||
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;
|
||||||
|
//}
|
||||||
|
if(enable && counter) {
|
||||||
|
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 = 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;
|
||||||
|
period = 2 * (2048 - frequency);
|
||||||
pattern_offset = 0;
|
pattern_offset = 0;
|
||||||
length = initial_length;
|
//if(length == 0) length = 256;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
period = 2 * (2048 - frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
||||||
@@ -67,17 +66,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 +84,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;
|
uint8 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();
|
@@ -1,9 +1,7 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#include <nall/crc32.hpp>
|
|
||||||
|
|
||||||
#define CARTRIDGE_CPP
|
#define CARTRIDGE_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
#include "mbc0/mbc0.cpp"
|
#include "mbc0/mbc0.cpp"
|
||||||
#include "mbc1/mbc1.cpp"
|
#include "mbc1/mbc1.cpp"
|
||||||
@@ -16,61 +14,41 @@ 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);
|
information.markup = markup;
|
||||||
//print("CRC32 = ", hex<4>(crc), "\n");
|
information.mapper = Mapper::Unknown;
|
||||||
|
information.ram = false;
|
||||||
|
information.battery = false;
|
||||||
|
information.rtc = false;
|
||||||
|
information.rumble = false;
|
||||||
|
|
||||||
info.mapper = Mapper::Unknown;
|
information.romsize = 0;
|
||||||
info.ram = false;
|
information.ramsize = 0;
|
||||||
info.battery = false;
|
|
||||||
info.rtc = false;
|
|
||||||
info.rumble = false;
|
|
||||||
|
|
||||||
info.romsize = 0;
|
XML::Document document(markup);
|
||||||
info.ramsize = 0;
|
|
||||||
|
|
||||||
xml_element document = xml_parse(xml);
|
auto &mapperid = document["cartridge"]["mapper"].data;
|
||||||
foreach(head, document.element) {
|
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
|
||||||
if(head.name == "cartridge") {
|
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
|
||||||
foreach(attr, head.attribute) {
|
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
|
||||||
if(attr.name == "mapper") {
|
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
|
||||||
if(attr.content == "none") info.mapper = Mapper::MBC0;
|
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
|
||||||
if(attr.content == "MBC1") info.mapper = Mapper::MBC1;
|
if(mapperid == "MMM01") information.mapper = Mapper::MMM01;
|
||||||
if(attr.content == "MBC2") info.mapper = Mapper::MBC2;
|
if(mapperid == "HuC1" ) information.mapper = Mapper::HuC1;
|
||||||
if(attr.content == "MBC3") info.mapper = Mapper::MBC3;
|
if(mapperid == "HuC3" ) information.mapper = Mapper::HuC3;
|
||||||
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);
|
information.rtc = document["cartridge"]["rtc"].data == "true";
|
||||||
if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false);
|
information.rumble = document["cartridge"]["rumble"].data == "true";
|
||||||
}
|
|
||||||
|
|
||||||
foreach(elem, head.element) {
|
information.romsize = numeral(document["cartridge"]["rom"]["size"].data);
|
||||||
if(elem.name == "rom") {
|
information.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
|
||||||
foreach(attr, elem.attribute) {
|
information.battery = document["cartridge"]["ram"]["nonvolatile"].data == "true";
|
||||||
if(attr.name == "size") info.romsize = hex(attr.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(elem.name == "ram") {
|
switch(information.mapper) { default:
|
||||||
info.ram = true;
|
|
||||||
foreach(attr, elem.attribute) {
|
|
||||||
if(attr.name == "size") info.ramsize = hex(attr.content);
|
|
||||||
if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(info.mapper) { default:
|
|
||||||
case Mapper::MBC0: mapper = &mbc0; break;
|
case Mapper::MBC0: mapper = &mbc0; break;
|
||||||
case Mapper::MBC1: mapper = &mbc1; break;
|
case Mapper::MBC1: mapper = &mbc1; break;
|
||||||
case Mapper::MBC2: mapper = &mbc2; break;
|
case Mapper::MBC2: mapper = &mbc2; break;
|
||||||
@@ -81,9 +59,11 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
|||||||
case Mapper::HuC3: mapper = &huc3; break;
|
case Mapper::HuC3: mapper = &huc3; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ramdata = new uint8_t[ramsize = info.ramsize]();
|
ramdata = new uint8_t[ramsize = information.ramsize]();
|
||||||
system.load();
|
system.load(revision);
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
sha256 = nall::sha256(romdata, romsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::unload() {
|
void Cartridge::unload() {
|
||||||
@@ -117,12 +97,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);
|
||||||
}
|
}
|
||||||
|
|
@@ -21,7 +21,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Information {
|
struct Information {
|
||||||
string xml;
|
string markup;
|
||||||
|
|
||||||
Mapper mapper;
|
Mapper mapper;
|
||||||
bool ram;
|
bool ram;
|
||||||
@@ -31,9 +31,10 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||||||
|
|
||||||
unsigned romsize;
|
unsigned romsize;
|
||||||
unsigned ramsize;
|
unsigned ramsize;
|
||||||
} info;
|
} information;
|
||||||
|
|
||||||
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);
|
54
bsnes/gb/cartridge/huc1/huc1.cpp
Executable file
54
bsnes/gb/cartridge/huc1/huc1.cpp
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
|
uint8 Cartridge::HuC1::mmio_read(uint16 addr) {
|
||||||
|
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||||
|
return cartridge.rom_read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||||
|
return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff));
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
|
return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) {
|
||||||
|
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||||
|
ram_writable = (data & 0x0f) == 0x0a;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||||
|
rom_select = data;
|
||||||
|
if(rom_select == 0) rom_select = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||||
|
ram_select = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||||
|
model = data & 0x01;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||||
|
if(ram_writable == false) return;
|
||||||
|
return cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cartridge::HuC1::power() {
|
||||||
|
ram_writable = false;
|
||||||
|
rom_select = 0x01;
|
||||||
|
ram_select = 0x00;
|
||||||
|
model = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
10
bsnes/gb/cartridge/huc1/huc1.hpp
Executable file
10
bsnes/gb/cartridge/huc1/huc1.hpp
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
struct HuC1 : MMIO {
|
||||||
|
bool ram_writable; //$0000-1fff
|
||||||
|
uint8 rom_select; //$2000-3fff
|
||||||
|
uint8 ram_select; //$4000-5fff
|
||||||
|
bool model; //$6000-7fff
|
||||||
|
|
||||||
|
uint8 mmio_read(uint16 addr);
|
||||||
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
void power();
|
||||||
|
} huc1;
|
@@ -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);
|
10
bsnes/gb/cartridge/mbc1/mbc1.hpp
Executable file
10
bsnes/gb/cartridge/mbc1/mbc1.hpp
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
struct MBC1 : MMIO {
|
||||||
|
bool ram_enable; //$0000-1fff
|
||||||
|
uint8 rom_select; //$2000-3fff
|
||||||
|
uint8 ram_select; //$4000-5fff
|
||||||
|
bool mode_select; //$6000-7fff
|
||||||
|
|
||||||
|
uint8 mmio_read(uint16 addr);
|
||||||
|
void mmio_write(uint16 addr, uint8 data);
|
||||||
|
void power();
|
||||||
|
} mbc1;
|
@@ -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;
|
@@ -1,7 +1,7 @@
|
|||||||
#ifdef CARTRIDGE_CPP
|
#ifdef CARTRIDGE_CPP
|
||||||
|
|
||||||
void Cartridge::serialize(serializer &s) {
|
void Cartridge::serialize(serializer &s) {
|
||||||
if(info.battery) s.array(ramdata, ramsize);
|
if(information.battery) s.array(ramdata, ramsize);
|
||||||
s.integer(bootrom_enable);
|
s.integer(bootrom_enable);
|
||||||
|
|
||||||
s.integer(mbc1.ram_enable);
|
s.integer(mbc1.ram_enable);
|
||||||
@@ -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/gb/cheat/cheat.cpp
Executable file
91
bsnes/gb/cheat/cheat.cpp
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
|
namespace GB {
|
||||||
|
|
||||||
|
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/gb/cheat/cheat.hpp
Executable file
14
bsnes/gb/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
|
@@ -1,7 +1,7 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#define CPU_CPP
|
#define CPU_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
#include "core/core.cpp"
|
#include "core/core.cpp"
|
||||||
#include "mmio/mmio.cpp"
|
#include "mmio/mmio.cpp"
|
||||||
@@ -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;
|
@@ -1,4 +1,4 @@
|
|||||||
struct CPU : Processor, MMIO {
|
struct CPU : Thread, MMIO {
|
||||||
#include "core/core.hpp"
|
#include "core/core.hpp"
|
||||||
#include "mmio/mmio.hpp"
|
#include "mmio/mmio.hpp"
|
||||||
#include "timing/timing.hpp"
|
#include "timing/timing.hpp"
|
||||||
@@ -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();
|
271
bsnes/gb/cpu/mmio/mmio.cpp
Executable file
271
bsnes/gb/cpu/mmio/mmio.cpp
Executable file
@@ -0,0 +1,271 @@
|
|||||||
|
#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() {
|
||||||
|
unsigned button = 0, dpad = 0;
|
||||||
|
|
||||||
|
button |= interface->inputPoll((unsigned)Input::Start) << 3;
|
||||||
|
button |= interface->inputPoll((unsigned)Input::Select) << 2;
|
||||||
|
button |= interface->inputPoll((unsigned)Input::B) << 1;
|
||||||
|
button |= interface->inputPoll((unsigned)Input::A) << 0;
|
||||||
|
|
||||||
|
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
|
||||||
|
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
|
||||||
|
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
|
||||||
|
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
|
||||||
|
|
||||||
|
status.joyp = 0x0f;
|
||||||
|
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||||
|
if(status.p15 == 0) status.joyp &= button ^ 0x0f;
|
||||||
|
if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;
|
||||||
|
if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 CPU::mmio_read(uint16 addr) {
|
||||||
|
if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)];
|
||||||
|
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
|
||||||
|
|
||||||
|
if(addr == 0xff00) { //JOYP
|
||||||
|
return (status.p15 << 5)
|
||||||
|
| (status.p14 << 4)
|
||||||
|
| (status.joyp << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff01) { //SB
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff02) { //SC
|
||||||
|
return (status.serial_transfer << 7)
|
||||||
|
| (status.serial_clock << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff04) { //DIV
|
||||||
|
return status.div;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff05) { //TIMA
|
||||||
|
return status.tima;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff06) { //TMA
|
||||||
|
return status.tma;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff07) { //TAC
|
||||||
|
return (status.timer_enable << 2)
|
||||||
|
| (status.timer_clock << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff0f) { //IF
|
||||||
|
return (status.interrupt_request_joypad << 4)
|
||||||
|
| (status.interrupt_request_serial << 3)
|
||||||
|
| (status.interrupt_request_timer << 2)
|
||||||
|
| (status.interrupt_request_stat << 1)
|
||||||
|
| (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
|
||||||
|
return (status.interrupt_enable_joypad << 4)
|
||||||
|
| (status.interrupt_enable_serial << 3)
|
||||||
|
| (status.interrupt_enable_timer << 2)
|
||||||
|
| (status.interrupt_enable_stat << 1)
|
||||||
|
| (status.interrupt_enable_vblank << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::mmio_write(uint16 addr, uint8 data) {
|
||||||
|
if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; }
|
||||||
|
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
|
||||||
|
|
||||||
|
if(addr == 0xff00) { //JOYP
|
||||||
|
status.p15 = data & 0x20;
|
||||||
|
status.p14 = data & 0x10;
|
||||||
|
interface->joypWrite(status.p15, status.p14);
|
||||||
|
mmio_joyp_poll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff01) { //SB
|
||||||
|
status.serial_data = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff02) { //SC
|
||||||
|
status.serial_transfer = data & 0x80;
|
||||||
|
status.serial_clock = data & 0x01;
|
||||||
|
if(status.serial_transfer) status.serial_bits = 8;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff04) { //DIV
|
||||||
|
status.div = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff05) { //TIMA
|
||||||
|
status.tima = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff06) { //TMA
|
||||||
|
status.tma = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff07) { //TAC
|
||||||
|
status.timer_enable = data & 0x04;
|
||||||
|
status.timer_clock = data & 0x03;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addr == 0xff0f) { //IF
|
||||||
|
status.interrupt_request_joypad = data & 0x10;
|
||||||
|
status.interrupt_request_serial = data & 0x08;
|
||||||
|
status.interrupt_request_timer = data & 0x04;
|
||||||
|
status.interrupt_request_stat = data & 0x02;
|
||||||
|
status.interrupt_request_vblank = data & 0x01;
|
||||||
|
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
|
||||||
|
status.interrupt_enable_joypad = data & 0x10;
|
||||||
|
status.interrupt_enable_serial = data & 0x08;
|
||||||
|
status.interrupt_enable_timer = data & 0x04;
|
||||||
|
status.interrupt_enable_stat = data & 0x02;
|
||||||
|
status.interrupt_enable_vblank = data & 0x01;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -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) {
|
||||||
|
Thread::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();
|
59
bsnes/gb/gb.hpp
Executable file
59
bsnes/gb/gb.hpp
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef GB_HPP
|
||||||
|
#define GB_HPP
|
||||||
|
|
||||||
|
#include <base/base.hpp>
|
||||||
|
|
||||||
|
namespace GB {
|
||||||
|
namespace Info {
|
||||||
|
static const char Name[] = "bgbc";
|
||||||
|
static const unsigned SerializerVersion = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bgbc - Game Boy, Super Game Boy, and Game Boy Color emulator
|
||||||
|
author: byuu
|
||||||
|
license: GPLv3
|
||||||
|
project started: 2010-12-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libco/libco.h>
|
||||||
|
|
||||||
|
namespace GB {
|
||||||
|
struct Thread {
|
||||||
|
cothread_t thread;
|
||||||
|
unsigned frequency;
|
||||||
|
int64 clock;
|
||||||
|
|
||||||
|
inline void create(void (*entrypoint)(), unsigned frequency) {
|
||||||
|
if(thread) co_delete(thread);
|
||||||
|
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||||
|
this->frequency = frequency;
|
||||||
|
clock = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void serialize(serializer &s) {
|
||||||
|
s.integer(frequency);
|
||||||
|
s.integer(clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Thread() : thread(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~Thread() {
|
||||||
|
if(thread) co_delete(thread);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <gb/memory/memory.hpp>
|
||||||
|
#include <gb/system/system.hpp>
|
||||||
|
#include <gb/scheduler/scheduler.hpp>
|
||||||
|
#include <gb/cartridge/cartridge.hpp>
|
||||||
|
#include <gb/cpu/cpu.hpp>
|
||||||
|
#include <gb/apu/apu.hpp>
|
||||||
|
#include <gb/lcd/lcd.hpp>
|
||||||
|
#include <gb/cheat/cheat.hpp>
|
||||||
|
#include <gb/video/video.hpp>
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
27
bsnes/gb/interface/interface.cpp
Executable file
27
bsnes/gb/interface/interface.cpp
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
|
namespace GB {
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
bsnes/gb/interface/interface.hpp
Executable file
12
bsnes/gb/interface/interface.hpp
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
struct Interface {
|
||||||
|
virtual void lcdScanline();
|
||||||
|
virtual void joypWrite(bool p15, bool p14);
|
||||||
|
|
||||||
|
virtual void videoRefresh(const uint16_t *data);
|
||||||
|
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
|
virtual bool inputPoll(unsigned id);
|
||||||
|
|
||||||
|
virtual void message(const string &text);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Interface *interface;
|
185
bsnes/gb/lcd/cgb.cpp
Executable file
185
bsnes/gb/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/gb/lcd/dmg.cpp
Executable file
145
bsnes/gb/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
|
158
bsnes/gb/lcd/lcd.cpp
Executable file
158
bsnes/gb/lcd/lcd.cpp
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
|
//LY = 0-153
|
||||||
|
//Raster = 0-143
|
||||||
|
//Vblank = 144-153
|
||||||
|
|
||||||
|
//LX = 0-455
|
||||||
|
|
||||||
|
#define LCD_CPP
|
||||||
|
namespace GB {
|
||||||
|
|
||||||
|
#include "dmg.cpp"
|
||||||
|
#include "cgb.cpp"
|
||||||
|
#include "mmio/mmio.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
LCD lcd;
|
||||||
|
|
||||||
|
void LCD::Main() {
|
||||||
|
lcd.main();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::main() {
|
||||||
|
while(true) {
|
||||||
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_clocks(4);
|
||||||
|
status.lx += 4;
|
||||||
|
if(status.lx >= 456) scanline();
|
||||||
|
|
||||||
|
if(status.display_enable && status.lx == 0) {
|
||||||
|
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status.display_enable && status.lx == 252) {
|
||||||
|
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
|
cpu.hblank();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::add_clocks(unsigned clocks) {
|
||||||
|
clock += clocks * cpu.frequency;
|
||||||
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||||
|
co_switch(scheduler.active_thread = cpu.thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::scanline() {
|
||||||
|
status.lx -= 456;
|
||||||
|
if(++status.ly == 154) frame();
|
||||||
|
|
||||||
|
if(status.display_enable && status.interrupt_lyc == true) {
|
||||||
|
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status.ly < 144) {
|
||||||
|
system.cgb() == false ? dmg_render() : cgb_render();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status.display_enable && status.ly == 144) {
|
||||||
|
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
||||||
|
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::frame() {
|
||||||
|
interface->videoRefresh(screen);
|
||||||
|
cpu.mmio_joyp_poll();
|
||||||
|
|
||||||
|
status.ly = 0;
|
||||||
|
status.wyc = 0;
|
||||||
|
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned LCD::hflip(unsigned data) const {
|
||||||
|
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
|
||||||
|
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
|
||||||
|
| ((data & 0x0808) << 1) | ((data & 0x0404) << 3)
|
||||||
|
| ((data & 0x0202) << 5) | ((data & 0x0101) << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LCD::power() {
|
||||||
|
create(Main, 4 * 1024 * 1024);
|
||||||
|
|
||||||
|
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
||||||
|
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
|
||||||
|
|
||||||
|
bus.mmio[0xff40] = this; //LCDC
|
||||||
|
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
|
||||||
|
|
||||||
|
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.wyc = 0;
|
||||||
|
|
||||||
|
status.display_enable = 0;
|
||||||
|
status.window_tilemap_select = 0;
|
||||||
|
status.window_display_enable = 0;
|
||||||
|
status.bg_tiledata_select = 0;
|
||||||
|
status.bg_tilemap_select = 0;
|
||||||
|
status.ob_size = 0;
|
||||||
|
status.ob_enable = 0;
|
||||||
|
status.bg_enable = 0;
|
||||||
|
|
||||||
|
status.interrupt_lyc = 0;
|
||||||
|
status.interrupt_oam = 0;
|
||||||
|
status.interrupt_vblank = 0;
|
||||||
|
status.interrupt_hblank = 0;
|
||||||
|
|
||||||
|
status.scy = 0;
|
||||||
|
status.scx = 0;
|
||||||
|
status.ly = 0;
|
||||||
|
status.lyc = 0;
|
||||||
|
status.wy = 0;
|
||||||
|
status.wx = 0;
|
||||||
|
|
||||||
|
status.vram_bank = 0;
|
||||||
|
|
||||||
|
status.bgpi_increment = 0;
|
||||||
|
status.bgpi = 0;
|
||||||
|
|
||||||
|
status.obpi_increment = 0;
|
||||||
|
status.obpi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LCD::LCD() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
bsnes/gb/lcd/lcd.hpp
Executable file
94
bsnes/gb/lcd/lcd.hpp
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
struct LCD : Thread, MMIO {
|
||||||
|
#include "mmio/mmio.hpp"
|
||||||
|
|
||||||
|
struct Status {
|
||||||
|
unsigned lx;
|
||||||
|
unsigned wyc;
|
||||||
|
|
||||||
|
//$ff40 LCDC
|
||||||
|
bool display_enable;
|
||||||
|
bool window_tilemap_select;
|
||||||
|
bool window_display_enable;
|
||||||
|
bool bg_tiledata_select;
|
||||||
|
bool bg_tilemap_select;
|
||||||
|
bool ob_size;
|
||||||
|
bool ob_enable;
|
||||||
|
bool bg_enable;
|
||||||
|
|
||||||
|
//$ff41 STAT
|
||||||
|
bool interrupt_lyc;
|
||||||
|
bool interrupt_oam;
|
||||||
|
bool interrupt_vblank;
|
||||||
|
bool interrupt_hblank;
|
||||||
|
|
||||||
|
//$ff42 SCY
|
||||||
|
uint8 scy;
|
||||||
|
|
||||||
|
//$ff43 SCX
|
||||||
|
uint8 scx;
|
||||||
|
|
||||||
|
//$ff44 LY
|
||||||
|
uint8 ly;
|
||||||
|
|
||||||
|
//$ff45 LYC
|
||||||
|
uint8 lyc;
|
||||||
|
|
||||||
|
//$ff4a WY
|
||||||
|
uint8 wy;
|
||||||
|
|
||||||
|
//$ff4b WX
|
||||||
|
uint8 wx;
|
||||||
|
|
||||||
|
//$ff4f VBK
|
||||||
|
bool vram_bank;
|
||||||
|
|
||||||
|
//$ff68 BGPI
|
||||||
|
bool bgpi_increment;
|
||||||
|
uint6 bgpi;
|
||||||
|
|
||||||
|
//$ff6a OBPI
|
||||||
|
bool obpi_increment;
|
||||||
|
uint8 obpi;
|
||||||
|
} status;
|
||||||
|
|
||||||
|
uint16 screen[160 * 144];
|
||||||
|
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 bgp[4];
|
||||||
|
uint8 obp[2][4];
|
||||||
|
uint8 bgpd[64];
|
||||||
|
uint8 obpd[64];
|
||||||
|
|
||||||
|
static void Main();
|
||||||
|
void main();
|
||||||
|
void add_clocks(unsigned clocks);
|
||||||
|
void scanline();
|
||||||
|
void frame();
|
||||||
|
|
||||||
|
unsigned hflip(unsigned data) const;
|
||||||
|
|
||||||
|
//dmg.cpp
|
||||||
|
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 serialize(serializer&);
|
||||||
|
LCD();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern LCD lcd;
|
@@ -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) {
|
||||||
|
Thread::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
|
@@ -1,7 +1,7 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#define MEMORY_CPP
|
#define MEMORY_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
Unmapped unmapped;
|
Unmapped unmapped;
|
||||||
Bus bus;
|
Bus bus;
|
||||||
@@ -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) {
|
@@ -1,7 +1,7 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#define SCHEDULER_CPP
|
#define SCHEDULER_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
@@ -16,11 +16,6 @@ void Scheduler::exit(ExitReason reason) {
|
|||||||
co_switch(host_thread);
|
co_switch(host_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::swapto(Processor &p) {
|
|
||||||
active_thread = p.thread;
|
|
||||||
co_switch(active_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::init() {
|
void Scheduler::init() {
|
||||||
host_thread = co_active();
|
host_thread = co_active();
|
||||||
active_thread = cpu.thread;
|
active_thread = cpu.thread;
|
@@ -8,7 +8,6 @@ struct Scheduler : property<Scheduler> {
|
|||||||
|
|
||||||
void enter();
|
void enter();
|
||||||
void exit(ExitReason);
|
void exit(ExitReason);
|
||||||
void swapto(Processor&);
|
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
Scheduler();
|
Scheduler();
|
@@ -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;
|
||||||
}
|
}
|
@@ -1,10 +1,8 @@
|
|||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
#define SYSTEM_CPP
|
#define SYSTEM_CPP
|
||||||
namespace GameBoy {
|
namespace GB {
|
||||||
|
|
||||||
#include "bootrom-dmg.cpp"
|
|
||||||
#include "bootrom-sgb.cpp"
|
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
System system;
|
System system;
|
||||||
|
|
||||||
@@ -20,8 +18,15 @@ void System::runtosave() {
|
|||||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||||
runthreadtosave();
|
runthreadtosave();
|
||||||
|
|
||||||
|
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||||
scheduler.active_thread = lcd.thread;
|
scheduler.active_thread = lcd.thread;
|
||||||
runthreadtosave();
|
runthreadtosave();
|
||||||
|
|
||||||
|
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||||
|
scheduler.active_thread = apu.thread;
|
||||||
|
runthreadtosave();
|
||||||
|
|
||||||
|
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::runthreadtosave() {
|
void System::runthreadtosave() {
|
||||||
@@ -33,11 +38,17 @@ void System::runthreadtosave() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Interface *interface_) {
|
void System::init() {
|
||||||
interface = interface_;
|
file fp;
|
||||||
|
fp.open("/home/byuu/Desktop/boot.rom", file::mode::write);
|
||||||
|
fp.write(bootROM.sgb, 256);
|
||||||
|
fp.close();
|
||||||
|
|
||||||
|
assert(interface != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load(Revision revision) {
|
||||||
|
this->revision = revision;
|
||||||
serialize_init();
|
serialize_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,4 +63,10 @@ void System::power() {
|
|||||||
clocks_executed = 0;
|
clocks_executed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System::System() {
|
||||||
|
for(auto &byte : bootROM.dmg) byte = 0;
|
||||||
|
for(auto &byte : bootROM.sgb) byte = 0;
|
||||||
|
for(auto &byte : bootROM.cgb) byte = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
49
bsnes/gb/system/system.hpp
Executable file
49
bsnes/gb/system/system.hpp
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
class Interface;
|
||||||
|
|
||||||
|
enum class Input : unsigned {
|
||||||
|
Up, Down, Left, Right, B, A, Select, Start,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
uint8 dmg[ 256];
|
||||||
|
uint8 sgb[ 256];
|
||||||
|
uint8 cgb[2048];
|
||||||
|
} bootROM;
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void runtosave();
|
||||||
|
void runthreadtosave();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void load(Revision);
|
||||||
|
void power();
|
||||||
|
|
||||||
|
unsigned clocks_executed;
|
||||||
|
|
||||||
|
//serialization.cpp
|
||||||
|
unsigned serialize_size;
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
|
void serialize_all(serializer&);
|
||||||
|
void serialize_init();
|
||||||
|
|
||||||
|
System();
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <gb/interface/interface.hpp>
|
||||||
|
|
||||||
|
extern System system;
|
82
bsnes/gb/video/video.cpp
Executable file
82
bsnes/gb/video/video.cpp
Executable file
@@ -0,0 +1,82 @@
|
|||||||
|
#include <gb/gb.hpp>
|
||||||
|
|
||||||
|
#define VIDEO_CPP
|
||||||
|
namespace GB {
|
||||||
|
|
||||||
|
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/gb/video/video.hpp
Executable file
17
bsnes/gb/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;
|
14
bsnes/gba/Makefile
Executable file
14
bsnes/gba/Makefile
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
gba_objects := gba-memory gba-interface gba-scheduler gba-system
|
||||||
|
gba_objects += gba-video gba-cartridge
|
||||||
|
gba_objects += gba-cpu gba-ppu gba-apu
|
||||||
|
objects += $(gba_objects)
|
||||||
|
|
||||||
|
obj/gba-memory.o: $(gba)/memory/memory.cpp $(call rwildcard,$(gba)/memory)
|
||||||
|
obj/gba-interface.o: $(gba)/interface/interface.cpp $(call rwildcard,$(gba)/interface)
|
||||||
|
obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/scheduler)
|
||||||
|
obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system)
|
||||||
|
obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video)
|
||||||
|
obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge)
|
||||||
|
obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu)
|
||||||
|
obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu)
|
||||||
|
obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu)
|
98
bsnes/gba/apu/apu.cpp
Executable file
98
bsnes/gba/apu/apu.cpp
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
#include <gba/gba.hpp>
|
||||||
|
|
||||||
|
namespace GBA {
|
||||||
|
|
||||||
|
#include "registers.cpp"
|
||||||
|
#include "mmio.cpp"
|
||||||
|
#include "square.cpp"
|
||||||
|
#include "square1.cpp"
|
||||||
|
#include "square2.cpp"
|
||||||
|
#include "wave.cpp"
|
||||||
|
#include "noise.cpp"
|
||||||
|
#include "sequencer.cpp"
|
||||||
|
#include "fifo.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
APU apu;
|
||||||
|
|
||||||
|
void APU::Enter() {
|
||||||
|
while(true) {
|
||||||
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
apu.main();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::main() {
|
||||||
|
for(unsigned n = 0; n < 128; n++) {
|
||||||
|
runsequencer();
|
||||||
|
}
|
||||||
|
|
||||||
|
signed lsample = regs.bias.level - 0x0200;
|
||||||
|
signed rsample = regs.bias.level - 0x0200;
|
||||||
|
|
||||||
|
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
||||||
|
if(sequencer.masterenable) {
|
||||||
|
signed lsequence = 0;
|
||||||
|
if(sequencer.lenable[0]) lsequence += square1.output;
|
||||||
|
if(sequencer.lenable[1]) lsequence += square2.output;
|
||||||
|
if(sequencer.lenable[2]) lsequence += wave.output;
|
||||||
|
if(sequencer.lenable[3]) lsequence += noise.output;
|
||||||
|
|
||||||
|
signed rsequence = 0;
|
||||||
|
if(sequencer.renable[0]) rsequence += square1.output;
|
||||||
|
if(sequencer.renable[1]) rsequence += square2.output;
|
||||||
|
if(sequencer.renable[2]) rsequence += wave.output;
|
||||||
|
if(sequencer.renable[3]) rsequence += noise.output;
|
||||||
|
|
||||||
|
if(sequencer.volume < 3) {
|
||||||
|
lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume);
|
||||||
|
rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
||||||
|
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||||
|
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||||
|
|
||||||
|
if(fifo[0].lenable) lsample += fifo0;
|
||||||
|
if(fifo[1].lenable) lsample += fifo1;
|
||||||
|
|
||||||
|
if(fifo[0].renable) rsample += fifo0;
|
||||||
|
if(fifo[1].renable) rsample += fifo1;
|
||||||
|
|
||||||
|
lsample = sclamp<10>(lsample);
|
||||||
|
rsample = sclamp<10>(rsample);
|
||||||
|
|
||||||
|
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3;
|
||||||
|
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
|
||||||
|
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||||
|
|
||||||
|
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
|
||||||
|
interface->audioSample(sclamp<16>(lsample << 7), sclamp<16>(rsample << 7)); //should be <<5, use <<7 for added volume
|
||||||
|
step(512);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::step(unsigned clocks) {
|
||||||
|
clock += clocks;
|
||||||
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::power() {
|
||||||
|
create(APU::Enter, 16777216);
|
||||||
|
|
||||||
|
square1.power();
|
||||||
|
square2.power();
|
||||||
|
wave.power();
|
||||||
|
noise.power();
|
||||||
|
sequencer.power();
|
||||||
|
fifo[0].power();
|
||||||
|
fifo[1].power();
|
||||||
|
|
||||||
|
regs.bias = 0x0200;
|
||||||
|
|
||||||
|
for(unsigned n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
bsnes/gba/apu/apu.hpp
Executable file
17
bsnes/gba/apu/apu.hpp
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
struct APU : Thread, MMIO {
|
||||||
|
#include "registers.hpp"
|
||||||
|
|
||||||
|
static void Enter();
|
||||||
|
void main();
|
||||||
|
void step(unsigned clocks);
|
||||||
|
|
||||||
|
uint8 read(uint32 addr);
|
||||||
|
void write(uint32 addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
|
||||||
|
void runsequencer();
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern APU apu;
|
28
bsnes/gba/apu/fifo.cpp
Executable file
28
bsnes/gba/apu/fifo.cpp
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
void APU::FIFO::read() {
|
||||||
|
if(size == 0) return;
|
||||||
|
size--;
|
||||||
|
output = sample[rdoffset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::FIFO::write(int8 byte) {
|
||||||
|
if(size == 32) return;
|
||||||
|
size++;
|
||||||
|
sample[wroffset++] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::FIFO::reset() {
|
||||||
|
for(auto &byte : sample) byte = 0;
|
||||||
|
output = 0;
|
||||||
|
|
||||||
|
rdoffset = 0;
|
||||||
|
wroffset = 0;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::FIFO::power() {
|
||||||
|
reset();
|
||||||
|
|
||||||
|
lenable = 0;
|
||||||
|
renable = 0;
|
||||||
|
timer = 0;
|
||||||
|
}
|
209
bsnes/gba/apu/mmio.cpp
Executable file
209
bsnes/gba/apu/mmio.cpp
Executable file
@@ -0,0 +1,209 @@
|
|||||||
|
uint8 APU::read(uint32 addr) {
|
||||||
|
switch(addr) {
|
||||||
|
|
||||||
|
//NR10
|
||||||
|
case 0x04000060: return square1.read(0);
|
||||||
|
case 0x04000061: return 0u;
|
||||||
|
|
||||||
|
//NR11 + NR12
|
||||||
|
case 0x04000062: return square1.read(1);
|
||||||
|
case 0x04000063: return square1.read(2);
|
||||||
|
|
||||||
|
//NR13 + NR14
|
||||||
|
case 0x04000064: return square1.read(3);
|
||||||
|
case 0x04000065: return square1.read(4);
|
||||||
|
|
||||||
|
//NR21 + NR22
|
||||||
|
case 0x04000068: return square2.read(1);
|
||||||
|
case 0x04000069: return square2.read(2);
|
||||||
|
|
||||||
|
//NR23 + NR24
|
||||||
|
case 0x0400006c: return square2.read(3);
|
||||||
|
case 0x0400006d: return square2.read(4);
|
||||||
|
|
||||||
|
//NR30
|
||||||
|
case 0x04000070: return wave.read(0);
|
||||||
|
case 0x04000071: return 0u;
|
||||||
|
|
||||||
|
//NR31 + NR32
|
||||||
|
case 0x04000072: return wave.read(1);
|
||||||
|
case 0x04000073: return wave.read(2);
|
||||||
|
|
||||||
|
//NR33 + NR34
|
||||||
|
case 0x04000074: return wave.read(3);
|
||||||
|
case 0x04000075: return wave.read(4);
|
||||||
|
|
||||||
|
//NR41 + NR42
|
||||||
|
case 0x04000078: return noise.read(1);
|
||||||
|
case 0x04000079: return noise.read(2);
|
||||||
|
|
||||||
|
//NR43 + NR44
|
||||||
|
case 0x0400007c: return noise.read(3);
|
||||||
|
case 0x0400007d: return noise.read(4);
|
||||||
|
|
||||||
|
//NR50 + NR51
|
||||||
|
case 0x04000080: return sequencer.read(0);
|
||||||
|
case 0x04000081: return sequencer.read(1);
|
||||||
|
|
||||||
|
//NR52
|
||||||
|
case 0x04000084: return sequencer.read(2);
|
||||||
|
case 0x04000085: return 0u;
|
||||||
|
|
||||||
|
//SOUNDBIAS
|
||||||
|
case 0x04000088: return regs.bias >> 0;
|
||||||
|
case 0x04000089: return regs.bias >> 8;
|
||||||
|
|
||||||
|
//WAVE_RAM0_L
|
||||||
|
case 0x04000090: return wave.readram( 0);
|
||||||
|
case 0x04000091: return wave.readram( 1);
|
||||||
|
|
||||||
|
//WAVE_RAM0_H
|
||||||
|
case 0x04000092: return wave.readram( 2);
|
||||||
|
case 0x04000093: return wave.readram( 3);
|
||||||
|
|
||||||
|
//WAVE_RAM1_L
|
||||||
|
case 0x04000094: return wave.readram( 4);
|
||||||
|
case 0x04000095: return wave.readram( 5);
|
||||||
|
|
||||||
|
//WAVE_RAM1_H
|
||||||
|
case 0x04000096: return wave.readram( 6);
|
||||||
|
case 0x04000097: return wave.readram( 7);
|
||||||
|
|
||||||
|
//WAVE_RAM2_L
|
||||||
|
case 0x04000098: return wave.readram( 8);
|
||||||
|
case 0x04000099: return wave.readram( 9);
|
||||||
|
|
||||||
|
//WAVE_RAM2_H
|
||||||
|
case 0x0400009a: return wave.readram(10);
|
||||||
|
case 0x0400009b: return wave.readram(11);
|
||||||
|
|
||||||
|
//WAVE_RAM3_L
|
||||||
|
case 0x0400009c: return wave.readram(12);
|
||||||
|
case 0x0400009d: return wave.readram(13);
|
||||||
|
|
||||||
|
//WAVE_RAM3_H
|
||||||
|
case 0x0400009e: return wave.readram(14);
|
||||||
|
case 0x0400009f: return wave.readram(15);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::write(uint32 addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
|
||||||
|
//NR10
|
||||||
|
case 0x04000060: return square1.write(0, byte);
|
||||||
|
case 0x04000061: return;
|
||||||
|
|
||||||
|
//NR11 + NR12
|
||||||
|
case 0x04000062: return square1.write(1, byte);
|
||||||
|
case 0x04000063: return square1.write(2, byte);
|
||||||
|
|
||||||
|
//NR13 + NR14
|
||||||
|
case 0x04000064: return square1.write(3, byte);
|
||||||
|
case 0x04000065: return square1.write(4, byte);
|
||||||
|
|
||||||
|
//NR21 + NR22
|
||||||
|
case 0x04000068: return square2.write(1, byte);
|
||||||
|
case 0x04000069: return square2.write(2, byte);
|
||||||
|
|
||||||
|
//NR23 + NR24
|
||||||
|
case 0x0400006c: return square2.write(3, byte);
|
||||||
|
case 0x0400006d: return square2.write(4, byte);
|
||||||
|
|
||||||
|
//NR30
|
||||||
|
case 0x04000070: return wave.write(0, byte);
|
||||||
|
case 0x04000071: return;
|
||||||
|
|
||||||
|
//NR31 + NR32
|
||||||
|
case 0x04000072: return wave.write(1, byte);
|
||||||
|
case 0x04000073: return wave.write(2, byte);
|
||||||
|
|
||||||
|
//NR33 + NR34
|
||||||
|
case 0x04000074: return wave.write(3, byte);
|
||||||
|
case 0x04000075: return wave.write(4, byte);
|
||||||
|
|
||||||
|
//NR41 + NR42
|
||||||
|
case 0x04000078: return noise.write(1, byte);
|
||||||
|
case 0x04000079: return noise.write(2, byte);
|
||||||
|
|
||||||
|
//NR43 + NR44
|
||||||
|
case 0x0400007c: return noise.write(3, byte);
|
||||||
|
case 0x0400007d: return noise.write(4, byte);
|
||||||
|
|
||||||
|
//NR50 + NR51
|
||||||
|
case 0x04000080: return sequencer.write(0, byte);
|
||||||
|
case 0x04000081: return sequencer.write(1, byte);
|
||||||
|
|
||||||
|
//SOUND_CNT_H
|
||||||
|
case 0x04000082:
|
||||||
|
sequencer.volume = byte >> 0;
|
||||||
|
fifo[0].volume = byte >> 2;
|
||||||
|
fifo[1].volume = byte >> 3;
|
||||||
|
return;
|
||||||
|
case 0x04000083:
|
||||||
|
fifo[0].renable = byte >> 0;
|
||||||
|
fifo[0].lenable = byte >> 1;
|
||||||
|
fifo[0].timer = byte >> 2;
|
||||||
|
if(byte & 1 << 3) fifo[0].reset();
|
||||||
|
fifo[1].renable = byte >> 4;
|
||||||
|
fifo[1].lenable = byte >> 5;
|
||||||
|
fifo[1].timer = byte >> 6;
|
||||||
|
if(byte & 1 << 7) fifo[1].reset();
|
||||||
|
return;
|
||||||
|
|
||||||
|
//NR52
|
||||||
|
case 0x04000084: return sequencer.write(2, byte);
|
||||||
|
case 0x04000085: return;
|
||||||
|
|
||||||
|
//SOUNDBIAS
|
||||||
|
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
|
||||||
|
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
|
||||||
|
|
||||||
|
//WAVE_RAM0_L
|
||||||
|
case 0x04000090: return wave.writeram( 0, byte);
|
||||||
|
case 0x04000091: return wave.writeram( 1, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM0_H
|
||||||
|
case 0x04000092: return wave.writeram( 2, byte);
|
||||||
|
case 0x04000093: return wave.writeram( 3, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM1_L
|
||||||
|
case 0x04000094: return wave.writeram( 4, byte);
|
||||||
|
case 0x04000095: return wave.writeram( 5, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM1_H
|
||||||
|
case 0x04000096: return wave.writeram( 6, byte);
|
||||||
|
case 0x04000097: return wave.writeram( 7, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM2_L
|
||||||
|
case 0x04000098: return wave.writeram( 8, byte);
|
||||||
|
case 0x04000099: return wave.writeram( 9, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM2_H
|
||||||
|
case 0x0400009a: return wave.writeram(10, byte);
|
||||||
|
case 0x0400009b: return wave.writeram(11, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM3_L
|
||||||
|
case 0x0400009c: return wave.writeram(12, byte);
|
||||||
|
case 0x0400009d: return wave.writeram(13, byte);
|
||||||
|
|
||||||
|
//WAVE_RAM3_H
|
||||||
|
case 0x0400009e: return wave.writeram(14, byte);
|
||||||
|
case 0x0400009f: return wave.writeram(15, byte);
|
||||||
|
|
||||||
|
//FIFO_A_L
|
||||||
|
//FIFO_A_H
|
||||||
|
case 0x040000a0: case 0x040000a1:
|
||||||
|
case 0x040000a2: case 0x040000a3:
|
||||||
|
return fifo[0].write(byte);
|
||||||
|
|
||||||
|
//FIFO_B_L
|
||||||
|
//FIFO_B_H
|
||||||
|
case 0x040000a4: case 0x040000a5:
|
||||||
|
case 0x040000a6: case 0x040000a7:
|
||||||
|
return fifo[1].write(byte);
|
||||||
|
}
|
||||||
|
}
|
93
bsnes/gba/apu/noise.cpp
Executable file
93
bsnes/gba/apu/noise.cpp
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
unsigned APU::Noise::divider() const {
|
||||||
|
if(divisor == 0) return 8;
|
||||||
|
return divisor * 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Noise::run() {
|
||||||
|
if(period && --period == 0) {
|
||||||
|
period = divider() << frequency;
|
||||||
|
if(frequency < 14) {
|
||||||
|
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||||
|
lfsr = (lfsr >> 1) ^ (bit << (narrowlfsr ? 6 : 14));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output = volume;
|
||||||
|
if(enable == false || (lfsr & 1)) output = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Noise::clocklength() {
|
||||||
|
if(enable && counter) {
|
||||||
|
if(++length == 0) enable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Noise::clockenvelope() {
|
||||||
|
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||||
|
envelope.period = envelope.frequency;
|
||||||
|
if(envelope.direction == 0 && volume > 0) volume--;
|
||||||
|
if(envelope.direction == 1 && volume < 15) volume++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 APU::Noise::read(unsigned addr) const {
|
||||||
|
switch(addr) {
|
||||||
|
case 1: return (length << 0);
|
||||||
|
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||||
|
case 3: return (divisor << 0) | (narrowlfsr << 3) | (frequency << 4);
|
||||||
|
case 4: return (counter << 6) | (initialize << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Noise::write(unsigned addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
case 1: //NR41
|
||||||
|
length = byte >> 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //NR42
|
||||||
|
envelope.frequency = byte >> 0;
|
||||||
|
envelope.direction = byte >> 3;
|
||||||
|
envelope.volume = byte >> 4;
|
||||||
|
if(envelope.dacenable() == false) enable = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //NR43
|
||||||
|
divisor = byte >> 0;
|
||||||
|
narrowlfsr = byte >> 3;
|
||||||
|
frequency = byte >> 4;
|
||||||
|
period = divider() << frequency;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: //NR44
|
||||||
|
counter = byte >> 6;
|
||||||
|
initialize = byte >> 7;
|
||||||
|
|
||||||
|
if(initialize) {
|
||||||
|
enable = envelope.dacenable();
|
||||||
|
lfsr = ~0u;
|
||||||
|
envelope.period = envelope.frequency;
|
||||||
|
volume = envelope.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Noise::power() {
|
||||||
|
envelope.frequency = 0;
|
||||||
|
envelope.direction = 0;
|
||||||
|
envelope.volume = 0;
|
||||||
|
envelope.period = 0;
|
||||||
|
length = 0;
|
||||||
|
divisor = 0;
|
||||||
|
narrowlfsr = 0;
|
||||||
|
frequency = 0;
|
||||||
|
counter = 0;
|
||||||
|
initialize = 0;
|
||||||
|
enable = 0;
|
||||||
|
lfsr = 0;
|
||||||
|
output = 0;
|
||||||
|
period = 0;
|
||||||
|
volume = 0;
|
||||||
|
}
|
12
bsnes/gba/apu/registers.cpp
Executable file
12
bsnes/gba/apu/registers.cpp
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
APU::Registers::SoundBias::operator uint16() const {
|
||||||
|
return (
|
||||||
|
(level << 0)
|
||||||
|
| (amplitude << 14)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 APU::Registers::SoundBias::operator=(uint16 source) {
|
||||||
|
level = (source >> 0) & 1023;
|
||||||
|
amplitude = (source >> 14) & 3;
|
||||||
|
return operator uint16();
|
||||||
|
}
|
158
bsnes/gba/apu/registers.hpp
Executable file
158
bsnes/gba/apu/registers.hpp
Executable file
@@ -0,0 +1,158 @@
|
|||||||
|
struct Registers {
|
||||||
|
struct SoundBias {
|
||||||
|
uint10 level;
|
||||||
|
uint2 amplitude;
|
||||||
|
|
||||||
|
operator uint16() const;
|
||||||
|
uint16 operator=(uint16 source);
|
||||||
|
SoundBias& operator=(const SoundBias&) = delete;
|
||||||
|
} bias;
|
||||||
|
|
||||||
|
unsigned clock;
|
||||||
|
} regs;
|
||||||
|
|
||||||
|
struct Sweep {
|
||||||
|
uint3 shift;
|
||||||
|
uint1 direction;
|
||||||
|
uint3 frequency;
|
||||||
|
|
||||||
|
uint1 enable;
|
||||||
|
uint1 negate;
|
||||||
|
uint3 period;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Envelope {
|
||||||
|
uint3 frequency;
|
||||||
|
uint1 direction;
|
||||||
|
uint4 volume;
|
||||||
|
|
||||||
|
uint3 period;
|
||||||
|
|
||||||
|
inline bool dacenable() const { return volume || direction; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Square {
|
||||||
|
Envelope envelope;
|
||||||
|
uint1 enable;
|
||||||
|
uint6 length;
|
||||||
|
uint2 duty;
|
||||||
|
uint11 frequency;
|
||||||
|
uint1 counter;
|
||||||
|
uint1 initialize;
|
||||||
|
|
||||||
|
signed shadowfrequency;
|
||||||
|
uint1 signal;
|
||||||
|
uint4 output;
|
||||||
|
unsigned period;
|
||||||
|
uint3 phase;
|
||||||
|
uint4 volume;
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void clocklength();
|
||||||
|
void clockenvelope();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Square1 : Square {
|
||||||
|
Sweep sweep;
|
||||||
|
|
||||||
|
void runsweep(bool update);
|
||||||
|
void clocksweep();
|
||||||
|
uint8 read(unsigned addr) const;
|
||||||
|
void write(unsigned addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
} square1;
|
||||||
|
|
||||||
|
struct Square2 : Square {
|
||||||
|
uint8 read(unsigned addr) const;
|
||||||
|
void write(unsigned addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
} square2;
|
||||||
|
|
||||||
|
struct Wave {
|
||||||
|
uint1 mode;
|
||||||
|
uint1 bank;
|
||||||
|
uint1 dacenable;
|
||||||
|
uint8 length;
|
||||||
|
uint3 volume;
|
||||||
|
uint11 frequency;
|
||||||
|
uint1 counter;
|
||||||
|
uint1 initialize;
|
||||||
|
uint4 pattern[32];
|
||||||
|
|
||||||
|
uint1 enable;
|
||||||
|
uint4 output;
|
||||||
|
uint4 patternaddr;
|
||||||
|
uint1 patternbank;
|
||||||
|
uint4 patternsample;
|
||||||
|
unsigned period;
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void clocklength();
|
||||||
|
uint8 read(unsigned addr) const;
|
||||||
|
void write(unsigned addr, uint8 byte);
|
||||||
|
uint8 readram(unsigned addr) const;
|
||||||
|
void writeram(unsigned addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
} wave;
|
||||||
|
|
||||||
|
struct Noise {
|
||||||
|
Envelope envelope;
|
||||||
|
uint6 length;
|
||||||
|
uint3 divisor;
|
||||||
|
uint1 narrowlfsr;
|
||||||
|
uint4 frequency;
|
||||||
|
uint1 counter;
|
||||||
|
uint1 initialize;
|
||||||
|
|
||||||
|
uint1 enable;
|
||||||
|
uint15 lfsr;
|
||||||
|
uint4 output;
|
||||||
|
unsigned period;
|
||||||
|
uint4 volume;
|
||||||
|
|
||||||
|
unsigned divider() const;
|
||||||
|
void run();
|
||||||
|
void clocklength();
|
||||||
|
void clockenvelope();
|
||||||
|
uint8 read(unsigned addr) const;
|
||||||
|
void write(unsigned addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
} noise;
|
||||||
|
|
||||||
|
struct Sequencer {
|
||||||
|
uint2 volume;
|
||||||
|
uint3 lvolume;
|
||||||
|
uint3 rvolume;
|
||||||
|
uint1 lenable[4];
|
||||||
|
uint1 renable[4];
|
||||||
|
uint1 enable[4];
|
||||||
|
uint1 masterenable;
|
||||||
|
|
||||||
|
uint13 base;
|
||||||
|
uint3 step;
|
||||||
|
int16 lsample;
|
||||||
|
int16 rsample;
|
||||||
|
|
||||||
|
uint8 read(unsigned addr) const;
|
||||||
|
void write(unsigned addr, uint8 byte);
|
||||||
|
void power();
|
||||||
|
} sequencer;
|
||||||
|
|
||||||
|
struct FIFO {
|
||||||
|
int8 sample[32];
|
||||||
|
int8 output;
|
||||||
|
|
||||||
|
uint5 rdoffset;
|
||||||
|
uint5 wroffset;
|
||||||
|
uint6 size;
|
||||||
|
|
||||||
|
uint1 volume; //0 = 50%, 1 = 100%
|
||||||
|
uint1 lenable;
|
||||||
|
uint1 renable;
|
||||||
|
uint1 timer;
|
||||||
|
|
||||||
|
void read();
|
||||||
|
void write(int8 byte);
|
||||||
|
void reset();
|
||||||
|
void power();
|
||||||
|
} fifo[2];
|
91
bsnes/gba/apu/sequencer.cpp
Executable file
91
bsnes/gba/apu/sequencer.cpp
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
void APU::runsequencer() {
|
||||||
|
auto &r = sequencer;
|
||||||
|
|
||||||
|
if(r.base == 0) { //512hz
|
||||||
|
if(r.step == 0 || r.step == 2 || r.step == 4 || r.step == 6) { //256hz
|
||||||
|
square1.clocklength();
|
||||||
|
square2.clocklength();
|
||||||
|
wave.clocklength();
|
||||||
|
noise.clocklength();
|
||||||
|
}
|
||||||
|
if(r.step == 2 || r.step == 6) { //128hz
|
||||||
|
square1.clocksweep();
|
||||||
|
}
|
||||||
|
if(r.step == 7) { //64hz
|
||||||
|
square1.clockenvelope();
|
||||||
|
square2.clockenvelope();
|
||||||
|
noise.clockenvelope();
|
||||||
|
}
|
||||||
|
r.step++;
|
||||||
|
}
|
||||||
|
r.base++;
|
||||||
|
|
||||||
|
if(r.enable[0]) square1.run();
|
||||||
|
if(r.enable[1]) square2.run();
|
||||||
|
if(r.enable[2]) wave.run();
|
||||||
|
if(r.enable[3]) noise.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 APU::Sequencer::read(unsigned addr) const {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: return (rvolume << 0) | (lvolume << 4);
|
||||||
|
case 1: return (
|
||||||
|
(renable[0] << 0)
|
||||||
|
| (renable[1] << 1)
|
||||||
|
| (renable[2] << 2)
|
||||||
|
| (renable[3] << 3)
|
||||||
|
| (lenable[0] << 4)
|
||||||
|
| (lenable[1] << 5)
|
||||||
|
| (lenable[2] << 6)
|
||||||
|
| (lenable[3] << 7)
|
||||||
|
);
|
||||||
|
case 2: return (
|
||||||
|
(enable[0] << 0)
|
||||||
|
| (enable[1] << 1)
|
||||||
|
| (enable[2] << 2)
|
||||||
|
| (enable[3] << 3)
|
||||||
|
| (masterenable << 7)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Sequencer::write(unsigned addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: //NR50
|
||||||
|
rvolume = byte >> 0;
|
||||||
|
lvolume = byte >> 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: //NR51
|
||||||
|
renable[0] = byte >> 0;
|
||||||
|
renable[1] = byte >> 1;
|
||||||
|
renable[2] = byte >> 2;
|
||||||
|
renable[3] = byte >> 3;
|
||||||
|
lenable[0] = byte >> 4;
|
||||||
|
lenable[1] = byte >> 5;
|
||||||
|
lenable[2] = byte >> 6;
|
||||||
|
lenable[3] = byte >> 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //NR52
|
||||||
|
enable[0] = byte >> 0;
|
||||||
|
enable[1] = byte >> 1;
|
||||||
|
enable[2] = byte >> 2;
|
||||||
|
enable[3] = byte >> 3;
|
||||||
|
masterenable = byte >> 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Sequencer::power() {
|
||||||
|
lvolume = 0;
|
||||||
|
rvolume = 0;
|
||||||
|
for(auto &n : lenable) n = 0;
|
||||||
|
for(auto &n : renable) n = 0;
|
||||||
|
for(auto &n : enable) n = 0;
|
||||||
|
masterenable = 0;
|
||||||
|
base = 0;
|
||||||
|
step = 0;
|
||||||
|
lsample = 0;
|
||||||
|
rsample = 0;
|
||||||
|
}
|
107
bsnes/gba/apu/serialization.cpp
Executable file
107
bsnes/gba/apu/serialization.cpp
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
void APU::serialize(serializer &s) {
|
||||||
|
Thread::serialize(s);
|
||||||
|
|
||||||
|
s.integer(regs.bias.level);
|
||||||
|
s.integer(regs.bias.amplitude);
|
||||||
|
s.integer(regs.clock);
|
||||||
|
|
||||||
|
s.integer(square1.sweep.shift);
|
||||||
|
s.integer(square1.sweep.direction);
|
||||||
|
s.integer(square1.sweep.frequency);
|
||||||
|
s.integer(square1.sweep.enable);
|
||||||
|
s.integer(square1.sweep.negate);
|
||||||
|
s.integer(square1.sweep.period);
|
||||||
|
|
||||||
|
s.integer(square1.envelope.frequency);
|
||||||
|
s.integer(square1.envelope.direction);
|
||||||
|
s.integer(square1.envelope.volume);
|
||||||
|
s.integer(square1.envelope.period);
|
||||||
|
|
||||||
|
s.integer(square1.enable);
|
||||||
|
s.integer(square1.length);
|
||||||
|
s.integer(square1.duty);
|
||||||
|
s.integer(square1.frequency);
|
||||||
|
s.integer(square1.counter);
|
||||||
|
s.integer(square1.initialize);
|
||||||
|
s.integer(square1.shadowfrequency);
|
||||||
|
s.integer(square1.signal);
|
||||||
|
s.integer(square1.output);
|
||||||
|
s.integer(square1.period);
|
||||||
|
s.integer(square1.phase);
|
||||||
|
s.integer(square1.volume);
|
||||||
|
|
||||||
|
s.integer(square2.envelope.frequency);
|
||||||
|
s.integer(square2.envelope.direction);
|
||||||
|
s.integer(square2.envelope.volume);
|
||||||
|
s.integer(square2.envelope.period);
|
||||||
|
|
||||||
|
s.integer(square2.enable);
|
||||||
|
s.integer(square2.length);
|
||||||
|
s.integer(square2.duty);
|
||||||
|
s.integer(square2.frequency);
|
||||||
|
s.integer(square2.counter);
|
||||||
|
s.integer(square2.initialize);
|
||||||
|
s.integer(square2.shadowfrequency);
|
||||||
|
s.integer(square2.signal);
|
||||||
|
s.integer(square2.output);
|
||||||
|
s.integer(square2.period);
|
||||||
|
s.integer(square2.phase);
|
||||||
|
s.integer(square2.volume);
|
||||||
|
|
||||||
|
s.integer(wave.mode);
|
||||||
|
s.integer(wave.bank);
|
||||||
|
s.integer(wave.dacenable);
|
||||||
|
s.integer(wave.length);
|
||||||
|
s.integer(wave.volume);
|
||||||
|
s.integer(wave.frequency);
|
||||||
|
s.integer(wave.counter);
|
||||||
|
s.integer(wave.initialize);
|
||||||
|
for(auto &value : wave.pattern) s.integer(value);
|
||||||
|
s.integer(wave.enable);
|
||||||
|
s.integer(wave.output);
|
||||||
|
s.integer(wave.patternaddr);
|
||||||
|
s.integer(wave.patternbank);
|
||||||
|
s.integer(wave.patternsample);
|
||||||
|
s.integer(wave.period);
|
||||||
|
|
||||||
|
s.integer(noise.envelope.frequency);
|
||||||
|
s.integer(noise.envelope.direction);
|
||||||
|
s.integer(noise.envelope.volume);
|
||||||
|
s.integer(noise.envelope.period);
|
||||||
|
|
||||||
|
s.integer(noise.length);
|
||||||
|
s.integer(noise.divisor);
|
||||||
|
s.integer(noise.narrowlfsr);
|
||||||
|
s.integer(noise.frequency);
|
||||||
|
s.integer(noise.counter);
|
||||||
|
s.integer(noise.initialize);
|
||||||
|
s.integer(noise.enable);
|
||||||
|
s.integer(noise.lfsr);
|
||||||
|
s.integer(noise.output);
|
||||||
|
s.integer(noise.period);
|
||||||
|
s.integer(noise.volume);
|
||||||
|
|
||||||
|
s.integer(sequencer.volume);
|
||||||
|
s.integer(sequencer.lvolume);
|
||||||
|
s.integer(sequencer.rvolume);
|
||||||
|
for(auto &flag : sequencer.lenable) s.integer(flag);
|
||||||
|
for(auto &flag : sequencer.renable) s.integer(flag);
|
||||||
|
for(auto &flag : sequencer.enable) s.integer(flag);
|
||||||
|
s.integer(sequencer.masterenable);
|
||||||
|
s.integer(sequencer.base);
|
||||||
|
s.integer(sequencer.step);
|
||||||
|
s.integer(sequencer.lsample);
|
||||||
|
s.integer(sequencer.rsample);
|
||||||
|
|
||||||
|
for(auto &f : fifo) {
|
||||||
|
for(auto &value : f.sample) s.integer(value);
|
||||||
|
s.integer(f.output);
|
||||||
|
s.integer(f.rdoffset);
|
||||||
|
s.integer(f.wroffset);
|
||||||
|
s.integer(f.size);
|
||||||
|
s.integer(f.volume);
|
||||||
|
s.integer(f.lenable);
|
||||||
|
s.integer(f.renable);
|
||||||
|
s.integer(f.timer);
|
||||||
|
}
|
||||||
|
}
|
30
bsnes/gba/apu/square.cpp
Executable file
30
bsnes/gba/apu/square.cpp
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
void APU::Square::run() {
|
||||||
|
if(period && --period == 0) {
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
|
phase++;
|
||||||
|
switch(duty) {
|
||||||
|
case 0: signal = (phase == 6); break; //_____-_
|
||||||
|
case 1: signal = (phase >= 6); break; //______--
|
||||||
|
case 2: signal = (phase >= 4); break; //____----
|
||||||
|
case 3: signal = (phase <= 5); break; //------__
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint4 sample = volume;
|
||||||
|
if(enable == false || signal == false) sample = 0;
|
||||||
|
output = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square::clocklength() {
|
||||||
|
if(enable && counter) {
|
||||||
|
if(++length == 0) enable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square::clockenvelope() {
|
||||||
|
if(enable && envelope.frequency && --envelope.period == 0) {
|
||||||
|
envelope.period = envelope.frequency;
|
||||||
|
if(envelope.direction == 0 && volume > 0) volume--;
|
||||||
|
if(envelope.direction == 1 && volume < 15) volume++;
|
||||||
|
}
|
||||||
|
}
|
104
bsnes/gba/apu/square1.cpp
Executable file
104
bsnes/gba/apu/square1.cpp
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
void APU::Square1::runsweep(bool update) {
|
||||||
|
if(sweep.enable == false) return;
|
||||||
|
|
||||||
|
sweep.negate = sweep.direction;
|
||||||
|
unsigned delta = shadowfrequency >> sweep.shift;
|
||||||
|
signed updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta);
|
||||||
|
|
||||||
|
if(updatefrequency > 2047) {
|
||||||
|
enable = false;
|
||||||
|
} else if(sweep.shift && update) {
|
||||||
|
shadowfrequency = updatefrequency;
|
||||||
|
frequency = updatefrequency;
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square1::clocksweep() {
|
||||||
|
if(enable && sweep.frequency && --sweep.period == 0) {
|
||||||
|
sweep.period = sweep.frequency;
|
||||||
|
runsweep(1);
|
||||||
|
runsweep(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 APU::Square1::read(unsigned addr) const {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4);
|
||||||
|
case 1: return (length << 0) | (duty << 6);
|
||||||
|
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||||
|
case 3: return (frequency << 0);
|
||||||
|
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square1::write(unsigned addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: //NR10
|
||||||
|
if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false;
|
||||||
|
sweep.shift = byte >> 0;
|
||||||
|
sweep.direction = byte >> 3;
|
||||||
|
sweep.frequency = byte >> 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: //NR11
|
||||||
|
length = byte >> 0;
|
||||||
|
duty = byte >> 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //NR12
|
||||||
|
envelope.frequency = byte >> 0;
|
||||||
|
envelope.direction = byte >> 3;
|
||||||
|
envelope.volume = byte >> 4;
|
||||||
|
if(envelope.dacenable() == false) enable = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //NR13
|
||||||
|
frequency = (frequency & 0xff00) | (byte << 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: //NR14
|
||||||
|
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||||
|
counter = byte >> 6;
|
||||||
|
initialize = byte >> 7;
|
||||||
|
|
||||||
|
if(initialize) {
|
||||||
|
enable = envelope.dacenable();
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
|
envelope.period = envelope.frequency;
|
||||||
|
volume = envelope.volume;
|
||||||
|
shadowfrequency = frequency;
|
||||||
|
sweep.period = sweep.frequency;
|
||||||
|
sweep.enable = sweep.period || sweep.shift;
|
||||||
|
sweep.negate = false;
|
||||||
|
if(sweep.shift) runsweep(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square1::power() {
|
||||||
|
envelope.frequency = 0;
|
||||||
|
envelope.direction = 0;
|
||||||
|
envelope.direction = 0;
|
||||||
|
envelope.period = 0;
|
||||||
|
sweep.shift = 0;
|
||||||
|
sweep.direction = 0;
|
||||||
|
sweep.frequency = 0;
|
||||||
|
sweep.enable = 0;
|
||||||
|
sweep.negate = 0;
|
||||||
|
sweep.period = 0;
|
||||||
|
enable = 0;
|
||||||
|
length = 0;
|
||||||
|
duty = 0;
|
||||||
|
frequency = 0;
|
||||||
|
counter = 0;
|
||||||
|
initialize = 0;
|
||||||
|
shadowfrequency = 0;
|
||||||
|
signal = 0;
|
||||||
|
output = 0;
|
||||||
|
period = 0;
|
||||||
|
phase = 0;
|
||||||
|
volume = 0;
|
||||||
|
}
|
61
bsnes/gba/apu/square2.cpp
Executable file
61
bsnes/gba/apu/square2.cpp
Executable file
@@ -0,0 +1,61 @@
|
|||||||
|
uint8 APU::Square2::read(unsigned addr) const {
|
||||||
|
switch(addr) {
|
||||||
|
case 1: return (length << 0) | (duty << 6);
|
||||||
|
case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4);
|
||||||
|
case 3: return (frequency >> 0);
|
||||||
|
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square2::write(unsigned addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
case 1: //NR21
|
||||||
|
length = byte >> 0;
|
||||||
|
duty = byte >> 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //NR22
|
||||||
|
envelope.frequency = byte >> 0;
|
||||||
|
envelope.direction = byte >> 3;
|
||||||
|
envelope.volume = byte >> 4;
|
||||||
|
if(envelope.dacenable() == false) enable = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //NR23
|
||||||
|
frequency = (frequency & 0xff00) | (byte << 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: //NR24
|
||||||
|
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||||
|
counter = byte >> 6;
|
||||||
|
initialize = byte >> 7;
|
||||||
|
|
||||||
|
if(initialize) {
|
||||||
|
enable = envelope.dacenable();
|
||||||
|
period = 4 * (2048 - frequency);
|
||||||
|
envelope.period = envelope.frequency;
|
||||||
|
volume = envelope.volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Square2::power() {
|
||||||
|
envelope.frequency = 0;
|
||||||
|
envelope.direction = 0;
|
||||||
|
envelope.direction = 0;
|
||||||
|
envelope.period = 0;
|
||||||
|
enable = 0;
|
||||||
|
length = 0;
|
||||||
|
duty = 0;
|
||||||
|
frequency = 0;
|
||||||
|
counter = 0;
|
||||||
|
initialize = 0;
|
||||||
|
shadowfrequency = 0;
|
||||||
|
signal = 0;
|
||||||
|
output = 0;
|
||||||
|
period = 0;
|
||||||
|
phase = 0;
|
||||||
|
volume = 0;
|
||||||
|
}
|
95
bsnes/gba/apu/wave.cpp
Executable file
95
bsnes/gba/apu/wave.cpp
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
void APU::Wave::run() {
|
||||||
|
if(period && --period == 0) {
|
||||||
|
period = 2 * (2048 - frequency);
|
||||||
|
patternsample = pattern[patternbank * 16 + patternaddr++];
|
||||||
|
if(patternaddr == 0) patternbank ^= mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = patternsample;
|
||||||
|
static unsigned multiplier[] = { 0, 4, 2, 1, 3, 3, 3, 3};
|
||||||
|
output = (output * multiplier[volume]) / 4;
|
||||||
|
if(enable == false) output = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Wave::clocklength() {
|
||||||
|
if(enable && counter) {
|
||||||
|
if(++length == 0) enable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 APU::Wave::read(unsigned addr) const {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: return (mode << 5) | (bank << 6) | (dacenable << 7);
|
||||||
|
case 1: return (length << 0);
|
||||||
|
case 2: return (volume << 5);
|
||||||
|
case 3: return (frequency >> 0);
|
||||||
|
case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Wave::write(unsigned addr, uint8 byte) {
|
||||||
|
switch(addr) {
|
||||||
|
case 0: //NR30
|
||||||
|
mode = byte >> 5;
|
||||||
|
bank = byte >> 6;
|
||||||
|
dacenable = byte >> 7;
|
||||||
|
if(dacenable == false) enable = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: //NR31
|
||||||
|
length = byte >> 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //NR32
|
||||||
|
volume = byte >> 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //NR33
|
||||||
|
frequency = (frequency & 0xff00) | (byte << 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: //NR34
|
||||||
|
frequency = (frequency & 0x00ff) | (byte << 8);
|
||||||
|
counter = byte >> 6;
|
||||||
|
initialize = byte >> 7;
|
||||||
|
|
||||||
|
if(initialize) {
|
||||||
|
enable = dacenable;
|
||||||
|
period = 2 * (2048 - frequency);
|
||||||
|
patternaddr = 0;
|
||||||
|
patternbank = mode ? (uint1)0 : bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 APU::Wave::readram(unsigned addr) const {
|
||||||
|
uint8 byte = 0;
|
||||||
|
byte |= pattern[addr * 2 + 0] << 0;
|
||||||
|
byte |= pattern[addr * 2 + 1] << 4;
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Wave::writeram(unsigned addr, uint8 byte) {
|
||||||
|
pattern[addr * 2 + 0] = byte >> 0;
|
||||||
|
pattern[addr * 2 + 1] = byte >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APU::Wave::power() {
|
||||||
|
mode = 0;
|
||||||
|
bank = 0;
|
||||||
|
dacenable = 0;
|
||||||
|
length = 0;
|
||||||
|
volume = 0;
|
||||||
|
frequency = 0;
|
||||||
|
counter = 0;
|
||||||
|
initialize = 0;
|
||||||
|
for(auto &sample : pattern) sample = 0;
|
||||||
|
enable = 0;
|
||||||
|
output = 0;
|
||||||
|
patternaddr = 0;
|
||||||
|
patternbank = 0;
|
||||||
|
patternsample = 0;
|
||||||
|
period = 0;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user