mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-18 14:31:26 +02:00
Compare commits
147 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
27660505c8 | ||
|
702b657e75 | ||
|
0253db8685 | ||
|
2a4eb1cfc8 | ||
|
bd628de3cf | ||
|
2c53d5fbc0 | ||
|
f2a416aea9 | ||
|
78d49d3873 | ||
|
65a3306ad5 | ||
|
a219f9c121 | ||
|
6adfe71836 | ||
|
41c478ac4a | ||
|
40f4b91000 | ||
|
6d9f43a37b | ||
|
d1ffd59c29 | ||
|
0fe55e3f5b | ||
|
b42ab2fcb3 | ||
|
8476a12deb | ||
|
b0e862613b | ||
|
b113ecb5a3 | ||
|
bc5ad4a1cd | ||
|
1a90e206e0 | ||
|
483fc81356 | ||
|
0c87bdabed | ||
|
c45633550e | ||
|
7081f46e45 | ||
|
213879771e | ||
|
4344b916b6 | ||
|
0271d6a12b | ||
|
1b0b54a690 | ||
|
092cac9073 | ||
|
ecb35cac33 | ||
|
28a14198cb | ||
|
7ff7f64482 | ||
|
4c9266d18f | ||
|
169e400437 | ||
|
ea02f1e36a | ||
|
310ff4fa3b | ||
|
83f684c66c | ||
|
e0815b55b9 | ||
|
20cc6148cb | ||
|
a21ff570ee | ||
|
bb3c69a30d | ||
|
f0c17ffc0d | ||
|
314aee8c5c | ||
|
7bf4cff946 | ||
|
99b2b4b57c | ||
|
4e0223d590 | ||
|
458775a481 | ||
|
fc8eba133d | ||
|
39ca8a2fab | ||
|
c335ee9d80 | ||
|
2eb50fd70b | ||
|
89d578bc7f | ||
|
b4ba95242f | ||
|
a1b2fb0124 | ||
|
4a069761f9 | ||
|
80c1c9c2ef | ||
|
a512d14628 | ||
|
1a7bc6bb87 | ||
|
ecc651c88b | ||
|
3016e595f0 | ||
|
423a6c6bf8 | ||
|
10e2a6d497 | ||
|
187ba0eec6 | ||
|
c54be74832 | ||
|
04986d2bf7 | ||
|
10464b8c54 | ||
|
fe85679321 | ||
|
2b81b630cb | ||
|
3ce1d19f7a | ||
|
73be2e729c | ||
|
84fab07756 | ||
|
926a39d701 | ||
|
1361820dd8 | ||
|
0f78acffd7 | ||
|
35f1605829 | ||
|
ed4e87f65e | ||
|
b4f18c3b47 | ||
|
68eaf53691 | ||
|
8c0b0fa4ad | ||
|
66f136718e | ||
|
4e2eb23835 | ||
|
c74865e171 | ||
|
29ea5bd599 | ||
|
75dab443b4 | ||
|
177e222ca7 | ||
|
0d75524791 | ||
|
5b4bbf5045 | ||
|
fdd3ea490e | ||
|
b7c212de7e | ||
|
d9400084c2 | ||
|
bbc33fe05f | ||
|
65c4011bec | ||
|
a7c35a65b4 | ||
|
ba660600ad | ||
|
b6575ca02a | ||
|
8d88337e28 | ||
|
6ac67c260b | ||
|
032e924495 | ||
|
b389d17c9a | ||
|
d59ae34e12 | ||
|
85f2e9a6d4 | ||
|
019fc1a2c6 | ||
|
84e98833ca | ||
|
d4751c5244 | ||
|
ab345ff20c | ||
|
c495c132a7 | ||
|
ef746bbda4 | ||
|
94b2538af5 | ||
|
7f404e6edb | ||
|
47dffcae85 | ||
|
be625cc0fb | ||
|
4cb8b51606 | ||
|
87cb164f7c | ||
|
c1318961d8 | ||
|
791e64951b | ||
|
27af50099f | ||
|
fbd52c7e5f | ||
|
36795e8061 | ||
|
ec8350794a | ||
|
4545e7c62d | ||
|
3302398907 | ||
|
189e707594 | ||
|
9a8a54c75e | ||
|
d418eda97c | ||
|
5dbd5f4d0f | ||
|
d6001a2df4 | ||
|
bd61432322 | ||
|
0611fefefa | ||
|
6ac7b733bd | ||
|
a1e4c67a05 | ||
|
73ebe093b8 | ||
|
c3f9d421da | ||
|
689fc49047 | ||
|
cb97d98ad2 | ||
|
3cb04b101b | ||
|
5d273c5265 | ||
|
8703d57030 | ||
|
9ad8b7eaac | ||
|
76553756a2 | ||
|
4fd20f0ae0 | ||
|
bb4db22a7d | ||
|
67c13f749f | ||
|
616372e96b | ||
|
bba597fc6f | ||
|
abe639ea91 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
purify/*.o
|
||||
purify/purify
|
||||
purify/analyze-gba
|
||||
ananke/libananke.so
|
||||
icarus/icarus
|
||||
|
105
GNUmakefile
Normal file
105
GNUmakefile
Normal file
@@ -0,0 +1,105 @@
|
||||
include nall/GNUmakefile
|
||||
|
||||
fc := fc
|
||||
sfc := sfc
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := tomoko
|
||||
# console := true
|
||||
|
||||
# compiler
|
||||
flags += -I. -O3
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
# pgo := optimize
|
||||
|
||||
ifeq ($(pgo),instrument)
|
||||
flags += -fprofile-generate
|
||||
link += -lgcov
|
||||
else ifeq ($(pgo),optimize)
|
||||
flags += -fprofile-use
|
||||
endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),windows)
|
||||
ifeq ($(console),true)
|
||||
link += -mconsole
|
||||
else
|
||||
link += -mwindows
|
||||
endif
|
||||
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
|
||||
link += -Wl,-enable-auto-import
|
||||
link += -Wl,-enable-runtime-pseudo-reloc
|
||||
else ifeq ($(platform),macosx)
|
||||
flags += -march=native
|
||||
else ifeq ($(platform),linux)
|
||||
flags += -march=native -fopenmp
|
||||
link += -fopenmp
|
||||
link += -Wl,-export-dynamic
|
||||
link += -lX11 -lXext
|
||||
else ifeq ($(platform),bsd)
|
||||
flags += -march=native -fopenmp
|
||||
link += -fopenmp
|
||||
link += -Wl,-export-dynamic
|
||||
link += -lX11 -lXext
|
||||
else
|
||||
$(error unsupported platform.)
|
||||
endif
|
||||
|
||||
ui := target-$(target)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
|
||||
$(if $(filter %.cpp,$<), \
|
||||
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
%.o: $<; $(call compile)
|
||||
|
||||
all: build;
|
||||
|
||||
obj/libco.o: libco/libco.c libco/*
|
||||
|
||||
include $(ui)/GNUmakefile
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# targets
|
||||
clean:
|
||||
-@$(call delete,out/*)
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,obj/*.a)
|
||||
-@$(call delete,obj/*.so)
|
||||
-@$(call delete,obj/*.dylib)
|
||||
-@$(call delete,obj/*.dll)
|
||||
|
||||
archive:
|
||||
if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
|
||||
tar -cJf higan.tar.xz `ls`
|
||||
|
||||
sync:
|
||||
ifeq ($(shell id -un),byuu)
|
||||
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 ./hiro ]; then rm -r ./hiro; fi
|
||||
cp -r ../libco ./libco
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../hiro ./hiro
|
||||
rm -r libco/doc
|
||||
rm -r libco/-test
|
||||
rm -r nall/-test
|
||||
rm -r ruby/-test
|
||||
rm -r hiro/-test
|
||||
endif
|
||||
|
||||
help:;
|
100
bsnes/Makefile
100
bsnes/Makefile
@@ -1,100 +0,0 @@
|
||||
include nall/Makefile
|
||||
|
||||
nes := nes
|
||||
snes := snes
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := ui
|
||||
|
||||
# options += console
|
||||
|
||||
# compiler
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
flags := -I. -O3 -fomit-frame-pointer
|
||||
link := -s
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
# pgo := optimize
|
||||
|
||||
ifeq ($(pgo),instrument)
|
||||
flags += -fprofile-generate
|
||||
link += -lgcov
|
||||
else ifeq ($(pgo),optimize)
|
||||
flags += -fprofile-use
|
||||
endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
flags += -march=native
|
||||
link += -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
else ifeq ($(platform),win)
|
||||
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
||||
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
else
|
||||
unknown_platform: help;
|
||||
endif
|
||||
|
||||
ui := target-$(target)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
$(c) $(flags) $1 -c $< -o $@, \
|
||||
$(if $(filter %.cpp,$<), \
|
||||
$(cpp) $(flags) $1 -c $< -o $@ \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
%.o: $<; $(call compile)
|
||||
|
||||
all: build;
|
||||
|
||||
obj/libco.o: libco/libco.c libco/*
|
||||
|
||||
include $(ui)/Makefile
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# targets
|
||||
clean:
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,obj/*.a)
|
||||
-@$(call delete,obj/*.so)
|
||||
-@$(call delete,obj/*.dylib)
|
||||
-@$(call delete,obj/*.dll)
|
||||
-@$(call delete,*.res)
|
||||
-@$(call delete,*.pgd)
|
||||
-@$(call delete,*.pgc)
|
||||
-@$(call delete,*.ilk)
|
||||
-@$(call delete,*.pdb)
|
||||
-@$(call delete,*.manifest)
|
||||
|
||||
sync:
|
||||
if [ -d ./libco ]; then rm -r ./libco; fi
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./ruby ]; then rm -r ./ruby; fi
|
||||
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||
cp -r ../libco ./libco
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r libco/doc
|
||||
rm -r libco/test
|
||||
rm -r nall/test
|
||||
rm -r ruby/_test
|
||||
rm -r phoenix/nall
|
||||
rm -r phoenix/test
|
||||
|
||||
archive:
|
||||
if [ -f bsnes.tar.bz2 ]; then rm bsnes.tar.bz2; fi
|
||||
tar -cjf bsnes.tar.bz2 `ls`
|
||||
|
||||
help:;
|
@@ -1,130 +0,0 @@
|
||||
#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
|
@@ -1,2 +0,0 @@
|
||||
@mingw32-make -j 8
|
||||
@pause
|
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity type="win32" name="bsnes" version="1.0.0.0" processorArchitecture="*"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
@@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=bsnes
|
||||
Comment=SNES emulator
|
||||
Exec=bsnes
|
||||
Icon=bsnes
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Game;Emulator;
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
112663
bsnes/data/cheats.xml
112663
bsnes/data/cheats.xml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
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,107 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define APU_CPP
|
||||
namespace GB {
|
||||
|
||||
#include "square1/square1.cpp"
|
||||
#include "square2/square2.cpp"
|
||||
#include "wave/wave.cpp"
|
||||
#include "noise/noise.cpp"
|
||||
#include "master/master.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
void APU::Main() {
|
||||
apu.main();
|
||||
}
|
||||
|
||||
void APU::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(sequencer_base == 0) { //512hz
|
||||
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
|
||||
square1.clock_length();
|
||||
square2.clock_length();
|
||||
wave.clock_length();
|
||||
noise.clock_length();
|
||||
}
|
||||
if(sequencer_step == 2 || sequencer_step == 6) { //128hz
|
||||
square1.clock_sweep();
|
||||
}
|
||||
if(sequencer_step == 7) { //64hz
|
||||
square1.clock_envelope();
|
||||
square2.clock_envelope();
|
||||
noise.clock_envelope();
|
||||
}
|
||||
sequencer_step++;
|
||||
}
|
||||
sequencer_base++;
|
||||
|
||||
square1.run();
|
||||
square2.run();
|
||||
wave.run();
|
||||
noise.run();
|
||||
master.run();
|
||||
|
||||
interface->audioSample(master.center, master.left, master.right);
|
||||
|
||||
clock += 1 * cpu.frequency;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
create(Main, 4 * 1024 * 1024);
|
||||
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
||||
for(auto &n : mmio_data) n = 0x00;
|
||||
sequencer_base = 0;
|
||||
sequencer_step = 0;
|
||||
|
||||
square1.power();
|
||||
square2.power();
|
||||
wave.power();
|
||||
noise.power();
|
||||
master.power();
|
||||
}
|
||||
|
||||
uint8 APU::mmio_read(uint16 addr) {
|
||||
static const uint8 table[48] = {
|
||||
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
|
||||
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
|
||||
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
|
||||
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
|
||||
0x00, 0x00, 0x70, //master
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
|
||||
};
|
||||
|
||||
if(addr == 0xff26) {
|
||||
uint8 data = master.enable << 7;
|
||||
if(square1.enable) data |= 0x01;
|
||||
if(square2.enable) data |= 0x02;
|
||||
if( wave.enable) data |= 0x04;
|
||||
if( noise.enable) data |= 0x08;
|
||||
return data | table[addr - 0xff10];
|
||||
}
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void APU::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
|
||||
|
||||
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
|
||||
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
|
||||
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
|
||||
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
|
||||
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
|
||||
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
|
||||
}
|
||||
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
struct APU : Thread, MMIO {
|
||||
#include "square1/square1.hpp"
|
||||
#include "square2/square2.hpp"
|
||||
#include "wave/wave.hpp"
|
||||
#include "noise/noise.hpp"
|
||||
#include "master/master.hpp"
|
||||
|
||||
uint8 mmio_data[48];
|
||||
uint13 sequencer_base;
|
||||
uint3 sequencer_step;
|
||||
|
||||
Square1 square1;
|
||||
Square2 square2;
|
||||
Wave wave;
|
||||
Noise noise;
|
||||
Master master;
|
||||
|
||||
static void Main();
|
||||
void main();
|
||||
void power();
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern APU apu;
|
@@ -1,24 +0,0 @@
|
||||
struct Master {
|
||||
bool left_in_enable;
|
||||
uint3 left_volume;
|
||||
bool right_in_enable;
|
||||
uint3 right_volume;
|
||||
bool channel4_left_enable;
|
||||
bool channel3_left_enable;
|
||||
bool channel2_left_enable;
|
||||
bool channel1_left_enable;
|
||||
bool channel4_right_enable;
|
||||
bool channel3_right_enable;
|
||||
bool channel2_right_enable;
|
||||
bool channel1_right_enable;
|
||||
bool enable;
|
||||
|
||||
int16 center;
|
||||
int16 left;
|
||||
int16 right;
|
||||
|
||||
void run();
|
||||
void write(unsigned r, uint8 data);
|
||||
void power();
|
||||
void serialize(serializer&);
|
||||
};
|
@@ -1,112 +0,0 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Noise::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Noise::run() {
|
||||
if(period && --period == 0) {
|
||||
period = divisor << frequency;
|
||||
if(frequency < 14) {
|
||||
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
|
||||
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Noise::clock_length() {
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
if(enable && counter) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::clock_envelope() {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::write(unsigned r, uint8 data) {
|
||||
if(r == 1) { //$ff20 NR41
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff21 NR42
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff22 NR43
|
||||
frequency = data >> 4;
|
||||
narrow_lfsr = data & 0x08;
|
||||
divisor = (data & 0x07) << 4;
|
||||
if(divisor == 0) divisor = 8;
|
||||
period = divisor << frequency;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff34 NR44
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
lfsr = ~0U;
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Noise::power() {
|
||||
enable = 0;
|
||||
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
narrow_lfsr = 0;
|
||||
divisor = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
length = 0;
|
||||
envelope_period = 0;
|
||||
volume = 0;
|
||||
period = 0;
|
||||
lfsr = 0;
|
||||
}
|
||||
|
||||
void APU::Noise::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(narrow_lfsr);
|
||||
s.integer(divisor);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(length);
|
||||
s.integer(envelope_period);
|
||||
s.integer(volume);
|
||||
s.integer(period);
|
||||
s.integer(lfsr);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,27 +0,0 @@
|
||||
struct Noise {
|
||||
bool enable;
|
||||
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
uint4 frequency;
|
||||
bool narrow_lfsr;
|
||||
unsigned divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
uint6 length;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
unsigned period;
|
||||
uint15 lfsr;
|
||||
|
||||
bool dac_enable();
|
||||
|
||||
void run();
|
||||
void clock_length();
|
||||
void clock_envelope();
|
||||
void write(unsigned r, uint8 data);
|
||||
void power();
|
||||
void serialize(serializer&);
|
||||
};
|
@@ -1,17 +0,0 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
void APU::serialize(serializer &s) {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(mmio_data);
|
||||
s.integer(sequencer_base);
|
||||
s.integer(sequencer_step);
|
||||
|
||||
square1.serialize(s);
|
||||
square2.serialize(s);
|
||||
wave.serialize(s);
|
||||
noise.serialize(s);
|
||||
master.serialize(s);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,164 +0,0 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Square1::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Square1::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 4 * (2048 - frequency);
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
case 2: duty_output = (phase >= 4); break; //____----
|
||||
case 3: duty_output = (phase <= 5); break; //------__
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square1::sweep(bool update) {
|
||||
if(sweep_enable == false) return;
|
||||
|
||||
sweep_negate = sweep_direction;
|
||||
unsigned delta = frequency_shadow >> sweep_shift;
|
||||
signed freq = frequency_shadow + (sweep_negate ? -delta : delta);
|
||||
|
||||
if(freq > 2047) {
|
||||
enable = false;
|
||||
} else if(sweep_shift && update) {
|
||||
frequency_shadow = freq;
|
||||
frequency = freq & 2047;
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_length() {
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_sweep() {
|
||||
if(enable && sweep_frequency && --sweep_period == 0) {
|
||||
sweep_period = sweep_frequency;
|
||||
sweep(1);
|
||||
sweep(0);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::clock_envelope() {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::write(unsigned r, uint8 data) {
|
||||
if(r == 0) { //$ff10 NR10
|
||||
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
|
||||
sweep_frequency = (data >> 4) & 7;
|
||||
sweep_direction = data & 0x08;
|
||||
sweep_shift = data & 0x07;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff11 NR11
|
||||
duty = data >> 6;
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = data & 0x3f;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff12 NR12
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff13 NR13
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff14 NR14
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
frequency_shadow = frequency;
|
||||
sweep_period = sweep_frequency;
|
||||
sweep_enable = sweep_period || sweep_shift;
|
||||
sweep_negate = false;
|
||||
if(sweep_shift) sweep(0);
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
enable = 0;
|
||||
|
||||
sweep_frequency = 0;
|
||||
sweep_direction = 0;
|
||||
sweep_shift = 0;
|
||||
sweep_negate = 0;
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
duty_output = 0;
|
||||
phase = 0;
|
||||
period = 0;
|
||||
envelope_period = 0;
|
||||
sweep_period = 0;
|
||||
frequency_shadow = 0;
|
||||
sweep_enable = 0;
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
void APU::Square1::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(sweep_frequency);
|
||||
s.integer(sweep_direction);
|
||||
s.integer(sweep_shift);
|
||||
s.integer(sweep_negate);
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(duty_output);
|
||||
s.integer(phase);
|
||||
s.integer(period);
|
||||
s.integer(envelope_period);
|
||||
s.integer(sweep_period);
|
||||
s.integer(frequency_shadow);
|
||||
s.integer(sweep_enable);
|
||||
s.integer(volume);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,36 +0,0 @@
|
||||
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,114 +0,0 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
bool APU::Square2::dac_enable() {
|
||||
return (envelope_volume || envelope_direction);
|
||||
}
|
||||
|
||||
void APU::Square2::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 4 * (2048 - frequency);
|
||||
phase++;
|
||||
switch(duty) {
|
||||
case 0: duty_output = (phase == 6); break; //______-_
|
||||
case 1: duty_output = (phase >= 6); break; //______--
|
||||
case 2: duty_output = (phase >= 4); break; //____----
|
||||
case 3: duty_output = (phase <= 5); break; //------__
|
||||
}
|
||||
}
|
||||
|
||||
uint4 sample = (duty_output ? volume : (uint4)0);
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Square2::clock_length() {
|
||||
//if(counter && length) {
|
||||
// if(--length == 0) enable = false;
|
||||
//}
|
||||
|
||||
if(counter && enable) {
|
||||
if(++length == 0) enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::clock_envelope() {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::write(unsigned r, uint8 data) {
|
||||
if(r == 1) { //$ff16 NR21
|
||||
duty = data >> 6;
|
||||
//length = 64 - (data & 0x3f);
|
||||
length = (data & 0x3f);
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff17 NR22
|
||||
envelope_volume = data >> 4;
|
||||
envelope_direction = data & 0x08;
|
||||
envelope_frequency = data & 0x07;
|
||||
if(dac_enable() == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff18 NR23
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff19 NR24
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
enable = 0;
|
||||
|
||||
duty = 0;
|
||||
length = 0;
|
||||
envelope_volume = 0;
|
||||
envelope_direction = 0;
|
||||
envelope_frequency = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
output = 0;
|
||||
duty_output = 0;
|
||||
phase = 0;
|
||||
period = 0;
|
||||
envelope_period = 0;
|
||||
volume = 0;
|
||||
}
|
||||
|
||||
void APU::Square2::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(duty);
|
||||
s.integer(length);
|
||||
s.integer(envelope_volume);
|
||||
s.integer(envelope_direction);
|
||||
s.integer(envelope_frequency);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(duty_output);
|
||||
s.integer(phase);
|
||||
s.integer(period);
|
||||
s.integer(envelope_period);
|
||||
s.integer(volume);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,27 +0,0 @@
|
||||
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&);
|
||||
};
|
@@ -1,102 +0,0 @@
|
||||
#ifdef APU_CPP
|
||||
|
||||
void APU::Wave::run() {
|
||||
if(period && --period == 0) {
|
||||
period = 2 * (2048 - frequency);
|
||||
pattern_sample = pattern[++pattern_offset];
|
||||
}
|
||||
|
||||
uint4 sample = pattern_sample >> volume_shift;
|
||||
if(enable == false) sample = 0;
|
||||
|
||||
output = sample;
|
||||
}
|
||||
|
||||
void APU::Wave::clock_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) {
|
||||
if(r == 0) { //$ff1a NR30
|
||||
dac_enable = data & 0x80;
|
||||
if(dac_enable == false) enable = false;
|
||||
}
|
||||
|
||||
if(r == 1) { //$ff1b NR31
|
||||
//length = 256 - data;
|
||||
length = data;
|
||||
}
|
||||
|
||||
if(r == 2) { //$ff1c NR32
|
||||
switch((data >> 5) & 3) {
|
||||
case 0: volume_shift = 4; break; // 0%
|
||||
case 1: volume_shift = 0; break; //100%
|
||||
case 2: volume_shift = 1; break; // 50%
|
||||
case 3: volume_shift = 2; break; // 25%
|
||||
}
|
||||
}
|
||||
|
||||
if(r == 3) { //$ff1d NR33
|
||||
frequency = (frequency & 0x0700) | data;
|
||||
}
|
||||
|
||||
if(r == 4) { //$ff1e NR34
|
||||
bool initialize = data & 0x80;
|
||||
counter = data & 0x40;
|
||||
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
|
||||
|
||||
if(initialize) {
|
||||
enable = dac_enable;
|
||||
period = 2 * (2048 - frequency);
|
||||
pattern_offset = 0;
|
||||
//if(length == 0) length = 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
||||
p <<= 1;
|
||||
pattern[p + 0] = (data >> 4) & 15;
|
||||
pattern[p + 1] = (data >> 0) & 15;
|
||||
}
|
||||
|
||||
void APU::Wave::power() {
|
||||
enable = 0;
|
||||
|
||||
dac_enable = 0;
|
||||
volume_shift = 0;
|
||||
frequency = 0;
|
||||
counter = 0;
|
||||
|
||||
random_lfsr r;
|
||||
for(auto &n : pattern) n = r() & 15;
|
||||
|
||||
output = 0;
|
||||
length = 0;
|
||||
period = 0;
|
||||
pattern_offset = 0;
|
||||
pattern_sample = 0;
|
||||
}
|
||||
|
||||
void APU::Wave::serialize(serializer &s) {
|
||||
s.integer(enable);
|
||||
|
||||
s.integer(dac_enable);
|
||||
s.integer(volume_shift);
|
||||
s.integer(frequency);
|
||||
s.integer(counter);
|
||||
s.array(pattern);
|
||||
|
||||
s.integer(output);
|
||||
s.integer(length);
|
||||
s.integer(period);
|
||||
s.integer(pattern_offset);
|
||||
s.integer(pattern_sample);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,22 +0,0 @@
|
||||
struct Wave {
|
||||
bool enable;
|
||||
|
||||
bool dac_enable;
|
||||
unsigned volume_shift;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[32];
|
||||
|
||||
int16 output;
|
||||
uint8 length;
|
||||
unsigned period;
|
||||
uint5 pattern_offset;
|
||||
uint4 pattern_sample;
|
||||
|
||||
void run();
|
||||
void clock_length();
|
||||
void write(unsigned r, uint8 data);
|
||||
void write_pattern(unsigned p, uint8 data);
|
||||
void power();
|
||||
void serialize(serializer&);
|
||||
};
|
@@ -1,152 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define CARTRIDGE_CPP
|
||||
namespace GB {
|
||||
|
||||
#include "mbc0/mbc0.cpp"
|
||||
#include "mbc1/mbc1.cpp"
|
||||
#include "mbc2/mbc2.cpp"
|
||||
#include "mbc3/mbc3.cpp"
|
||||
#include "mbc5/mbc5.cpp"
|
||||
#include "mmm01/mmm01.cpp"
|
||||
#include "huc1/huc1.cpp"
|
||||
#include "huc3/huc3.cpp"
|
||||
#include "serialization.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
|
||||
if(size == 0) size = 32768;
|
||||
romdata = allocate<uint8>(romsize = size, 0xff);
|
||||
if(data) memcpy(romdata, data, size);
|
||||
|
||||
information.markup = markup;
|
||||
information.mapper = Mapper::Unknown;
|
||||
information.ram = false;
|
||||
information.battery = false;
|
||||
information.rtc = false;
|
||||
information.rumble = false;
|
||||
|
||||
information.romsize = 0;
|
||||
information.ramsize = 0;
|
||||
|
||||
XML::Document document(markup);
|
||||
|
||||
auto &mapperid = document["cartridge"]["mapper"].data;
|
||||
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
|
||||
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
|
||||
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
|
||||
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
|
||||
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
|
||||
if(mapperid == "MMM01") information.mapper = Mapper::MMM01;
|
||||
if(mapperid == "HuC1" ) information.mapper = Mapper::HuC1;
|
||||
if(mapperid == "HuC3" ) information.mapper = Mapper::HuC3;
|
||||
|
||||
information.rtc = document["cartridge"]["rtc"].data == "true";
|
||||
information.rumble = document["cartridge"]["rumble"].data == "true";
|
||||
|
||||
information.romsize = numeral(document["cartridge"]["rom"]["size"].data);
|
||||
information.ramsize = numeral(document["cartridge"]["ram"]["size"].data);
|
||||
information.battery = document["cartridge"]["ram"]["nonvolatile"].data == "true";
|
||||
|
||||
switch(information.mapper) { default:
|
||||
case Mapper::MBC0: mapper = &mbc0; break;
|
||||
case Mapper::MBC1: mapper = &mbc1; break;
|
||||
case Mapper::MBC2: mapper = &mbc2; break;
|
||||
case Mapper::MBC3: mapper = &mbc3; break;
|
||||
case Mapper::MBC5: mapper = &mbc5; break;
|
||||
case Mapper::MMM01: mapper = &mmm01; break;
|
||||
case Mapper::HuC1: mapper = &huc1; break;
|
||||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
}
|
||||
|
||||
ramdata = new uint8_t[ramsize = information.ramsize]();
|
||||
system.load(revision);
|
||||
|
||||
loaded = true;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(loaded == false) return;
|
||||
|
||||
if(romdata) { delete[] romdata; romdata = 0; }
|
||||
if(ramdata) { delete[] ramdata; ramdata = 0; }
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
uint8 Cartridge::rom_read(unsigned addr) {
|
||||
if(addr >= romsize) addr %= romsize;
|
||||
return romdata[addr];
|
||||
}
|
||||
|
||||
void Cartridge::rom_write(unsigned addr, uint8 data) {
|
||||
if(addr >= romsize) addr %= romsize;
|
||||
romdata[addr] = data;
|
||||
}
|
||||
|
||||
uint8 Cartridge::ram_read(unsigned addr) {
|
||||
if(ramsize == 0) return 0x00;
|
||||
if(addr >= ramsize) addr %= ramsize;
|
||||
return ramdata[addr];
|
||||
}
|
||||
|
||||
void Cartridge::ram_write(unsigned addr, uint8 data) {
|
||||
if(ramsize == 0) return;
|
||||
if(addr >= ramsize) addr %= ramsize;
|
||||
ramdata[addr] = data;
|
||||
}
|
||||
|
||||
uint8 Cartridge::mmio_read(uint16 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);
|
||||
}
|
||||
|
||||
void Cartridge::mmio_write(uint16 addr, uint8 data) {
|
||||
if(bootrom_enable && addr == 0xff50) {
|
||||
bootrom_enable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mapper->mmio_write(addr, data);
|
||||
}
|
||||
|
||||
void Cartridge::power() {
|
||||
bootrom_enable = true;
|
||||
|
||||
mbc0.power();
|
||||
mbc1.power();
|
||||
mbc2.power();
|
||||
mbc3.power();
|
||||
mbc5.power();
|
||||
mmm01.power();
|
||||
huc1.power();
|
||||
huc3.power();
|
||||
|
||||
for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
|
||||
for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
|
||||
bus.mmio[0xff50] = this;
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
loaded = false;
|
||||
romdata = 0;
|
||||
ramdata = 0;
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge() {
|
||||
unload();
|
||||
}
|
||||
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
struct Cartridge : MMIO, property<Cartridge> {
|
||||
#include "mbc0/mbc0.hpp"
|
||||
#include "mbc1/mbc1.hpp"
|
||||
#include "mbc2/mbc2.hpp"
|
||||
#include "mbc3/mbc3.hpp"
|
||||
#include "mbc5/mbc5.hpp"
|
||||
#include "mmm01/mmm01.hpp"
|
||||
#include "huc1/huc1.hpp"
|
||||
#include "huc3/huc3.hpp"
|
||||
|
||||
enum Mapper : unsigned {
|
||||
MBC0,
|
||||
MBC1,
|
||||
MBC2,
|
||||
MBC3,
|
||||
MBC5,
|
||||
MMM01,
|
||||
HuC1,
|
||||
HuC3,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct Information {
|
||||
string markup;
|
||||
|
||||
Mapper mapper;
|
||||
bool ram;
|
||||
bool battery;
|
||||
bool rtc;
|
||||
bool rumble;
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
} information;
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
uint8_t *romdata;
|
||||
unsigned romsize;
|
||||
|
||||
uint8_t *ramdata;
|
||||
unsigned ramsize;
|
||||
|
||||
MMIO *mapper;
|
||||
bool bootrom_enable;
|
||||
|
||||
void load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
|
||||
uint8 rom_read(unsigned addr);
|
||||
void rom_write(unsigned addr, uint8 data);
|
||||
uint8 ram_read(unsigned addr);
|
||||
void ram_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
|
||||
void power();
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
@@ -1,10 +0,0 @@
|
||||
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,9 +0,0 @@
|
||||
struct HuC3 : 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();
|
||||
} huc3;
|
@@ -1,25 +0,0 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
uint8 Cartridge::MBC0::mmio_read(uint16 addr) {
|
||||
if((addr & 0x8000) == 0x0000) { //$0000-7fff
|
||||
return cartridge.rom_read(addr);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.ram_read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
cartridge.ram_write(addr & 0x1fff, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::MBC0::power() {
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,5 +0,0 @@
|
||||
struct MBC0 : MMIO {
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
void power();
|
||||
} mbc0;
|
@@ -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,8 +0,0 @@
|
||||
struct MBC2 : MMIO {
|
||||
bool ram_enable; //$0000-1fff
|
||||
uint8 rom_select; //$2000-3fff
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
void power();
|
||||
} mbc2;
|
@@ -1,24 +0,0 @@
|
||||
struct MBC3 : MMIO {
|
||||
bool ram_enable; //$0000-1fff
|
||||
uint8 rom_select; //$2000-3fff
|
||||
uint8 ram_select; //$4000-5fff
|
||||
bool rtc_latch; //$6000-7fff
|
||||
|
||||
bool rtc_halt;
|
||||
unsigned rtc_second;
|
||||
unsigned rtc_minute;
|
||||
unsigned rtc_hour;
|
||||
unsigned rtc_day;
|
||||
bool rtc_day_carry;
|
||||
|
||||
unsigned rtc_latch_second;
|
||||
unsigned rtc_latch_minute;
|
||||
unsigned rtc_latch_hour;
|
||||
unsigned rtc_latch_day;
|
||||
unsigned rtc_latch_day_carry;
|
||||
|
||||
void second();
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
void power();
|
||||
} mbc3;
|
@@ -1,9 +0,0 @@
|
||||
struct MBC5 : MMIO {
|
||||
bool ram_enable; //$0000-1fff
|
||||
uint16 rom_select; //$2000-2fff + $3000-3fff
|
||||
uint8 ram_select; //$4000-5fff
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
void power();
|
||||
} mbc5;
|
@@ -1,12 +0,0 @@
|
||||
struct MMM01 : MMIO {
|
||||
bool rom_mode;
|
||||
uint8 rom_base;
|
||||
|
||||
bool ram_enable;
|
||||
uint8 rom_select;
|
||||
uint8 ram_select;
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
void power();
|
||||
} mmm01;
|
@@ -1,54 +0,0 @@
|
||||
#ifdef CARTRIDGE_CPP
|
||||
|
||||
void Cartridge::serialize(serializer &s) {
|
||||
if(information.battery) s.array(ramdata, ramsize);
|
||||
s.integer(bootrom_enable);
|
||||
|
||||
s.integer(mbc1.ram_enable);
|
||||
s.integer(mbc1.rom_select);
|
||||
s.integer(mbc1.ram_select);
|
||||
s.integer(mbc1.mode_select);
|
||||
|
||||
s.integer(mbc2.ram_enable);
|
||||
s.integer(mbc2.rom_select);
|
||||
|
||||
s.integer(mbc3.ram_enable);
|
||||
s.integer(mbc3.rom_select);
|
||||
s.integer(mbc3.ram_select);
|
||||
s.integer(mbc3.rtc_latch);
|
||||
|
||||
s.integer(mbc3.rtc_halt);
|
||||
s.integer(mbc3.rtc_second);
|
||||
s.integer(mbc3.rtc_minute);
|
||||
s.integer(mbc3.rtc_hour);
|
||||
s.integer(mbc3.rtc_day);
|
||||
s.integer(mbc3.rtc_day_carry);
|
||||
|
||||
s.integer(mbc3.rtc_latch_second);
|
||||
s.integer(mbc3.rtc_latch_minute);
|
||||
s.integer(mbc3.rtc_latch_hour);
|
||||
s.integer(mbc3.rtc_latch_day);
|
||||
s.integer(mbc3.rtc_latch_day_carry);
|
||||
|
||||
s.integer(mbc5.ram_enable);
|
||||
s.integer(mbc5.rom_select);
|
||||
s.integer(mbc5.ram_select);
|
||||
|
||||
s.integer(mmm01.rom_mode);
|
||||
s.integer(mmm01.rom_base);
|
||||
|
||||
s.integer(mmm01.ram_enable);
|
||||
s.integer(mmm01.rom_select);
|
||||
s.integer(mmm01.ram_select);
|
||||
|
||||
s.integer(huc1.ram_writable);
|
||||
s.integer(huc1.rom_select);
|
||||
s.integer(huc1.ram_select);
|
||||
s.integer(huc1.model);
|
||||
|
||||
s.integer(huc3.ram_enable);
|
||||
s.integer(huc3.rom_select);
|
||||
s.integer(huc3.ram_select);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,91 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
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;
|
@@ -1,682 +0,0 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
#include "table.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
void CPU::op_xx() {
|
||||
}
|
||||
|
||||
void CPU::op_cb() {
|
||||
uint8 opcode = op_read(r[PC]++);
|
||||
(this->*opcode_table_cb[opcode])();
|
||||
}
|
||||
|
||||
//8-bit load commands
|
||||
|
||||
template<unsigned x, unsigned y> void CPU::op_ld_r_r() {
|
||||
r[x] = r[y];
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_ld_r_n() {
|
||||
r[x] = op_read(r[PC]++);
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_ld_r_hl() {
|
||||
r[x] = op_read(r[HL]);
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_ld_hl_r() {
|
||||
op_write(r[HL], r[x]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_hl_n() {
|
||||
op_write(r[HL], op_read(r[PC]++));
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_ld_a_rr() {
|
||||
r[A] = op_read(r[x]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_a_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
r[A] = op_read((hi << 8) | (lo << 0));
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_ld_rr_a() {
|
||||
op_write(r[x], r[A]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_nn_a() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
op_write((hi << 8) | (lo << 0), r[A]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_a_ffn() {
|
||||
r[A] = op_read(0xff00 + op_read(r[PC]++));
|
||||
}
|
||||
|
||||
void CPU::op_ld_ffn_a() {
|
||||
op_write(0xff00 + op_read(r[PC]++), r[A]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_a_ffc() {
|
||||
r[A] = op_read(0xff00 + r[C]);
|
||||
}
|
||||
|
||||
void CPU::op_ld_ffc_a() {
|
||||
op_write(0xff00 + r[C], r[A]);
|
||||
}
|
||||
|
||||
void CPU::op_ldi_hl_a() {
|
||||
op_write(r[HL], r[A]);
|
||||
r[HL]++;
|
||||
}
|
||||
|
||||
void CPU::op_ldi_a_hl() {
|
||||
r[A] = op_read(r[HL]);
|
||||
r[HL]++;
|
||||
}
|
||||
|
||||
void CPU::op_ldd_hl_a() {
|
||||
op_write(r[HL], r[A]);
|
||||
r[HL]--;
|
||||
}
|
||||
|
||||
void CPU::op_ldd_a_hl() {
|
||||
r[A] = op_read(r[HL]);
|
||||
r[HL]--;
|
||||
}
|
||||
|
||||
//16-bit load commands
|
||||
|
||||
template<unsigned x> void CPU::op_ld_rr_nn() {
|
||||
r[x] = op_read(r[PC]++) << 0;
|
||||
r[x] |= op_read(r[PC]++) << 8;
|
||||
}
|
||||
|
||||
void CPU::op_ld_nn_sp() {
|
||||
uint16 addr = op_read(r[PC]++) << 0;
|
||||
addr |= op_read(r[PC]++) << 8;
|
||||
op_write(addr + 0, r[SP] >> 0);
|
||||
op_write(addr + 1, r[SP] >> 8);
|
||||
}
|
||||
|
||||
void CPU::op_ld_sp_hl() {
|
||||
r[SP] = r[HL];
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_push_rr() {
|
||||
op_write(--r[SP], r[x] >> 8);
|
||||
op_write(--r[SP], r[x] >> 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_pop_rr() {
|
||||
r[x] = op_read(r[SP]++) << 0;
|
||||
r[x] |= op_read(r[SP]++) << 8;
|
||||
}
|
||||
|
||||
//8-bit arithmetic commands
|
||||
|
||||
void CPU::opi_add_a(uint8 x) {
|
||||
uint16 rh = r[A] + x;
|
||||
uint16 rl = (r[A] & 0x0f) + (x & 0x0f);
|
||||
r[A] = rh;
|
||||
r.f.z = (uint8)rh == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = rl > 0x0f;
|
||||
r.f.c = rh > 0xff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_add_a_r() { opi_add_a(r[x]); }
|
||||
void CPU::op_add_a_n() { opi_add_a(op_read(r[PC]++)); }
|
||||
void CPU::op_add_a_hl() { opi_add_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_adc_a(uint8 x) {
|
||||
uint16 rh = r[A] + x + r.f.c;
|
||||
uint16 rl = (r[A] & 0x0f) + (x & 0x0f) + r.f.c;
|
||||
r[A] = rh;
|
||||
r.f.z = (uint8)rh == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = rl > 0x0f;
|
||||
r.f.c = rh > 0xff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_adc_a_r() { opi_adc_a(r[x]); }
|
||||
void CPU::op_adc_a_n() { opi_adc_a(op_read(r[PC]++)); }
|
||||
void CPU::op_adc_a_hl() { opi_adc_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_sub_a(uint8 x) {
|
||||
uint16 rh = r[A] - x;
|
||||
uint16 rl = (r[A] & 0x0f) - (x & 0x0f);
|
||||
r[A] = rh;
|
||||
r.f.z = (uint8)rh == 0;
|
||||
r.f.n = 1;
|
||||
r.f.h = rl > 0x0f;
|
||||
r.f.c = rh > 0xff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_sub_a_r() { opi_sub_a(r[x]); }
|
||||
void CPU::op_sub_a_n() { opi_sub_a(op_read(r[PC]++)); }
|
||||
void CPU::op_sub_a_hl() { opi_sub_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_sbc_a(uint8 x) {
|
||||
uint16 rh = r[A] - x - r.f.c;
|
||||
uint16 rl = (r[A] & 0x0f) - (x & 0x0f) - r.f.c;
|
||||
r[A] = rh;
|
||||
r.f.z = (uint8)rh == 0;
|
||||
r.f.n = 1;
|
||||
r.f.h = rl > 0x0f;
|
||||
r.f.c = rh > 0xff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_sbc_a_r() { opi_sbc_a(r[x]); }
|
||||
void CPU::op_sbc_a_n() { opi_sbc_a(op_read(r[PC]++)); }
|
||||
void CPU::op_sbc_a_hl() { opi_sbc_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_and_a(uint8 x) {
|
||||
r[A] &= x;
|
||||
r.f.z = r[A] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 1;
|
||||
r.f.c = 0;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_and_a_r() { opi_and_a(r[x]); }
|
||||
void CPU::op_and_a_n() { opi_and_a(op_read(r[PC]++)); }
|
||||
void CPU::op_and_a_hl() { opi_and_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_xor_a(uint8 x) {
|
||||
r[A] ^= x;
|
||||
r.f.z = r[A] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = 0;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_xor_a_r() { opi_xor_a(r[x]); }
|
||||
void CPU::op_xor_a_n() { opi_xor_a(op_read(r[PC]++)); }
|
||||
void CPU::op_xor_a_hl() { opi_xor_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_or_a(uint8 x) {
|
||||
r[A] |= x;
|
||||
r.f.z = r[A] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = 0;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_or_a_r() { opi_or_a(r[x]); }
|
||||
void CPU::op_or_a_n() { opi_or_a(op_read(r[PC]++)); }
|
||||
void CPU::op_or_a_hl() { opi_or_a(op_read(r[HL])); }
|
||||
|
||||
void CPU::opi_cp_a(uint8 x) {
|
||||
uint16 rh = r[A] - x;
|
||||
uint16 rl = (r[A] & 0x0f) - (x & 0x0f);
|
||||
r.f.z = (uint8)rh == 0;
|
||||
r.f.n = 1;
|
||||
r.f.h = rl > 0x0f;
|
||||
r.f.c = rh > 0xff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_cp_a_r() { opi_cp_a(r[x]); }
|
||||
void CPU::op_cp_a_n() { opi_cp_a(op_read(r[PC]++)); }
|
||||
void CPU::op_cp_a_hl() { opi_cp_a(op_read(r[HL])); }
|
||||
|
||||
template<unsigned x> void CPU::op_inc_r() {
|
||||
r[x]++;
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = (r[x] & 0x0f) == 0x00;
|
||||
}
|
||||
|
||||
void CPU::op_inc_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
op_write(r[HL], ++n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = (n & 0x0f) == 0x00;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_dec_r() {
|
||||
r[x]--;
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 1;
|
||||
r.f.h = (r[x] & 0x0f) == 0x0f;
|
||||
}
|
||||
|
||||
void CPU::op_dec_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
op_write(r[HL], --n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 1;
|
||||
r.f.h = (n & 0x0f) == 0x0f;
|
||||
}
|
||||
|
||||
void CPU::op_daa() {
|
||||
uint16 a = r[A];
|
||||
if(r.f.n == 0) {
|
||||
if(r.f.h || (a & 0x0f) > 0x09) a += 0x06;
|
||||
if(r.f.c || (a ) > 0x9f) a += 0x60;
|
||||
} else {
|
||||
if(r.f.h) {
|
||||
a -= 0x06;
|
||||
if(r.f.c == 0) a &= 0xff;
|
||||
}
|
||||
if(r.f.c) a -= 0x60;
|
||||
}
|
||||
r[A] = a;
|
||||
r.f.z = r[A] == 0;
|
||||
r.f.h = 0;
|
||||
r.f.c |= a & 0x100;
|
||||
}
|
||||
|
||||
void CPU::op_cpl() {
|
||||
r[A] ^= 0xff;
|
||||
r.f.n = 1;
|
||||
r.f.h = 1;
|
||||
}
|
||||
|
||||
//16-bit arithmetic commands
|
||||
|
||||
template<unsigned x> void CPU::op_add_hl_rr() {
|
||||
op_io();
|
||||
uint32 rb = (r[HL] + r[x]);
|
||||
uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff);
|
||||
r[HL] = rb;
|
||||
r.f.n = 0;
|
||||
r.f.h = rn > 0x0fff;
|
||||
r.f.c = rb > 0xffff;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_inc_rr() {
|
||||
op_io();
|
||||
r[x]++;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_dec_rr() {
|
||||
op_io();
|
||||
r[x]--;
|
||||
}
|
||||
|
||||
void CPU::op_add_sp_n() {
|
||||
op_io();
|
||||
op_io();
|
||||
signed n = (int8)op_read(r[PC]++);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
|
||||
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
|
||||
r[SP] += n;
|
||||
}
|
||||
|
||||
void CPU::op_ld_hl_sp_n() {
|
||||
op_io();
|
||||
signed n = (int8)op_read(r[PC]++);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
|
||||
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
|
||||
r[HL] = r[SP] + n;
|
||||
}
|
||||
|
||||
//rotate/shift commands
|
||||
|
||||
void CPU::op_rlca() {
|
||||
r[A] = (r[A] << 1) | (r[A] >> 7);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = r[A] & 0x01;
|
||||
}
|
||||
|
||||
void CPU::op_rla() {
|
||||
bool c = r[A] & 0x80;
|
||||
r[A] = (r[A] << 1) | (r.f.c << 0);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_rrca() {
|
||||
r[A] = (r[A] >> 1) | (r[A] << 7);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = r[A] & 0x80;
|
||||
}
|
||||
|
||||
void CPU::op_rra() {
|
||||
bool c = r[A] & 0x01;
|
||||
r[A] = (r[A] >> 1) | (r.f.c << 7);
|
||||
r.f.z = 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_rlc_r() {
|
||||
r[x] = (r[x] << 1) | (r[x] >> 7);
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = r[x] & 0x01;
|
||||
}
|
||||
|
||||
void CPU::op_rlc_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
n = (n << 1) | (n >> 7);
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = n & 0x01;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_rl_r() {
|
||||
bool c = r[x] & 0x80;
|
||||
r[x] = (r[x] << 1) | (r.f.c << 0);
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_rl_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
bool c = n & 0x80;
|
||||
n = (n << 1) | (r.f.c << 0);
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_rrc_r() {
|
||||
r[x] = (r[x] >> 1) | (r[x] << 7);
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = r[x] & 0x80;
|
||||
}
|
||||
|
||||
void CPU::op_rrc_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
n = (n >> 1) | (n << 7);
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = n & 0x80;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_rr_r() {
|
||||
bool c = r[x] & 0x01;
|
||||
r[x] = (r[x] >> 1) | (r.f.c << 7);
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_rr_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
bool c = n & 0x01;
|
||||
n = (n >> 1) | (r.f.c << 7);
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_sla_r() {
|
||||
bool c = r[x] & 0x80;
|
||||
r[x] <<= 1;
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_sla_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
bool c = n & 0x80;
|
||||
n <<= 1;
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_swap_r() {
|
||||
r[x] = (r[x] << 4) | (r[x] >> 4);
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = 0;
|
||||
}
|
||||
|
||||
void CPU::op_swap_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
n = (n << 4) | (n >> 4);
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = 0;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_sra_r() {
|
||||
bool c = r[x] & 0x01;
|
||||
r[x] = (int8)r[x] >> 1;
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_sra_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
bool c = n & 0x01;
|
||||
n = (int8)n >> 1;
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
template<unsigned x> void CPU::op_srl_r() {
|
||||
bool c = r[x] & 0x01;
|
||||
r[x] >>= 1;
|
||||
r.f.z = r[x] == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
void CPU::op_srl_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
bool c = n & 0x01;
|
||||
n >>= 1;
|
||||
op_write(r[HL], n);
|
||||
r.f.z = n == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = c;
|
||||
}
|
||||
|
||||
//single-bit commands
|
||||
|
||||
template<unsigned b, unsigned x> void CPU::op_bit_n_r() {
|
||||
r.f.z = (r[x] & (1 << b)) == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 1;
|
||||
}
|
||||
|
||||
template<unsigned b> void CPU::op_bit_n_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
r.f.z = (n & (1 << b)) == 0;
|
||||
r.f.n = 0;
|
||||
r.f.h = 1;
|
||||
}
|
||||
|
||||
template<unsigned b, unsigned x> void CPU::op_set_n_r() {
|
||||
r[x] |= 1 << b;
|
||||
}
|
||||
|
||||
template<unsigned b> void CPU::op_set_n_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
n |= 1 << b;
|
||||
op_write(r[HL], n);
|
||||
}
|
||||
|
||||
template<unsigned b, unsigned x> void CPU::op_res_n_r() {
|
||||
r[x] &= ~(1 << b);
|
||||
}
|
||||
|
||||
template<unsigned b> void CPU::op_res_n_hl() {
|
||||
uint8 n = op_read(r[HL]);
|
||||
n &= ~(1 << b);
|
||||
op_write(r[HL], n);
|
||||
}
|
||||
|
||||
//control commands
|
||||
|
||||
void CPU::op_ccf() {
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = !r.f.c;
|
||||
}
|
||||
|
||||
void CPU::op_scf() {
|
||||
r.f.n = 0;
|
||||
r.f.h = 0;
|
||||
r.f.c = 1;
|
||||
}
|
||||
|
||||
void CPU::op_nop() {
|
||||
}
|
||||
|
||||
void CPU::op_halt() {
|
||||
status.halt = true;
|
||||
while(status.halt == true) op_io();
|
||||
}
|
||||
|
||||
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;
|
||||
while(status.stop == true) op_io();
|
||||
}
|
||||
|
||||
void CPU::op_di() {
|
||||
status.ime = 0;
|
||||
}
|
||||
|
||||
void CPU::op_ei() {
|
||||
status.ei = true;
|
||||
//status.ime = 1;
|
||||
}
|
||||
|
||||
//jump commands
|
||||
|
||||
void CPU::op_jp_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
void CPU::op_jp_hl() {
|
||||
r[PC] = r[HL];
|
||||
}
|
||||
|
||||
template<unsigned x, bool y> void CPU::op_jp_f_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
if(r.f[x] == y) {
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::op_jr_n() {
|
||||
int8 n = op_read(r[PC]++);
|
||||
r[PC] += n;
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<unsigned x, bool y> void CPU::op_jr_f_n() {
|
||||
int8 n = op_read(r[PC]++);
|
||||
if(r.f[x] == y) {
|
||||
r[PC] += n;
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::op_call_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<unsigned x, bool y> void CPU::op_call_f_nn() {
|
||||
uint8 lo = op_read(r[PC]++);
|
||||
uint8 hi = op_read(r[PC]++);
|
||||
if(r.f[x] == y) {
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::op_ret() {
|
||||
uint8 lo = op_read(r[SP]++);
|
||||
uint8 hi = op_read(r[SP]++);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
|
||||
template<unsigned x, bool y> void CPU::op_ret_f() {
|
||||
op_io();
|
||||
if(r.f[x] == y) {
|
||||
uint8 lo = op_read(r[SP]++);
|
||||
uint8 hi = op_read(r[SP]++);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::op_reti() {
|
||||
uint8 lo = op_read(r[SP]++);
|
||||
uint8 hi = op_read(r[SP]++);
|
||||
r[PC] = (hi << 8) | (lo << 0);
|
||||
op_io();
|
||||
status.ime = 1;
|
||||
}
|
||||
|
||||
template<unsigned n> void CPU::op_rst_n() {
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = n;
|
||||
op_io();
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,145 +0,0 @@
|
||||
#include "registers.hpp"
|
||||
void (CPU::*opcode_table[256])();
|
||||
void (CPU::*opcode_table_cb[256])();
|
||||
void initialize_opcode_table();
|
||||
|
||||
void op_xx();
|
||||
void op_cb();
|
||||
|
||||
//8-bit load commands
|
||||
template<unsigned x, unsigned y> void op_ld_r_r();
|
||||
template<unsigned x> void op_ld_r_n();
|
||||
template<unsigned x> void op_ld_r_hl();
|
||||
template<unsigned x> void op_ld_hl_r();
|
||||
void op_ld_hl_n();
|
||||
template<unsigned x> void op_ld_a_rr();
|
||||
void op_ld_a_nn();
|
||||
template<unsigned x> void op_ld_rr_a();
|
||||
void op_ld_nn_a();
|
||||
void op_ld_a_ffn();
|
||||
void op_ld_ffn_a();
|
||||
void op_ld_a_ffc();
|
||||
void op_ld_ffc_a();
|
||||
void op_ldi_hl_a();
|
||||
void op_ldi_a_hl();
|
||||
void op_ldd_hl_a();
|
||||
void op_ldd_a_hl();
|
||||
|
||||
//16-bit load commands
|
||||
template<unsigned x> void op_ld_rr_nn();
|
||||
void op_ld_nn_sp();
|
||||
void op_ld_sp_hl();
|
||||
template<unsigned x> void op_push_rr();
|
||||
template<unsigned x> void op_pop_rr();
|
||||
|
||||
//8-bit arithmetic commands
|
||||
void opi_add_a(uint8 x);
|
||||
template<unsigned x> void op_add_a_r();
|
||||
void op_add_a_n();
|
||||
void op_add_a_hl();
|
||||
|
||||
void opi_adc_a(uint8 x);
|
||||
template<unsigned x> void op_adc_a_r();
|
||||
void op_adc_a_n();
|
||||
void op_adc_a_hl();
|
||||
|
||||
void opi_sub_a(uint8 x);
|
||||
template<unsigned x> void op_sub_a_r();
|
||||
void op_sub_a_n();
|
||||
void op_sub_a_hl();
|
||||
|
||||
void opi_sbc_a(uint8 x);
|
||||
template<unsigned x> void op_sbc_a_r();
|
||||
void op_sbc_a_n();
|
||||
void op_sbc_a_hl();
|
||||
|
||||
void opi_and_a(uint8 x);
|
||||
template<unsigned x> void op_and_a_r();
|
||||
void op_and_a_n();
|
||||
void op_and_a_hl();
|
||||
|
||||
void opi_xor_a(uint8 x);
|
||||
template<unsigned x> void op_xor_a_r();
|
||||
void op_xor_a_n();
|
||||
void op_xor_a_hl();
|
||||
|
||||
void opi_or_a(uint8 x);
|
||||
template<unsigned x> void op_or_a_r();
|
||||
void op_or_a_n();
|
||||
void op_or_a_hl();
|
||||
|
||||
void opi_cp_a(uint8 x);
|
||||
template<unsigned x> void op_cp_a_r();
|
||||
void op_cp_a_n();
|
||||
void op_cp_a_hl();
|
||||
|
||||
template<unsigned x> void op_inc_r();
|
||||
void op_inc_hl();
|
||||
template<unsigned x> void op_dec_r();
|
||||
void op_dec_hl();
|
||||
void op_daa();
|
||||
void op_cpl();
|
||||
|
||||
//16-bit arithmetic commands
|
||||
template<unsigned x> void op_add_hl_rr();
|
||||
template<unsigned x> void op_inc_rr();
|
||||
template<unsigned x> void op_dec_rr();
|
||||
void op_add_sp_n();
|
||||
void op_ld_hl_sp_n();
|
||||
|
||||
//rotate/shift commands
|
||||
void op_rlca();
|
||||
void op_rla();
|
||||
void op_rrca();
|
||||
void op_rra();
|
||||
template<unsigned x> void op_rlc_r();
|
||||
void op_rlc_hl();
|
||||
template<unsigned x> void op_rl_r();
|
||||
void op_rl_hl();
|
||||
template<unsigned x> void op_rrc_r();
|
||||
void op_rrc_hl();
|
||||
template<unsigned x> void op_rr_r();
|
||||
void op_rr_hl();
|
||||
template<unsigned x> void op_sla_r();
|
||||
void op_sla_hl();
|
||||
template<unsigned x> void op_swap_r();
|
||||
void op_swap_hl();
|
||||
template<unsigned x> void op_sra_r();
|
||||
void op_sra_hl();
|
||||
template<unsigned x> void op_srl_r();
|
||||
void op_srl_hl();
|
||||
|
||||
//single-bit commands
|
||||
template<unsigned b, unsigned x> void op_bit_n_r();
|
||||
template<unsigned b> void op_bit_n_hl();
|
||||
template<unsigned b, unsigned x> void op_set_n_r();
|
||||
template<unsigned b> void op_set_n_hl();
|
||||
template<unsigned b, unsigned x> void op_res_n_r();
|
||||
template<unsigned b> void op_res_n_hl();
|
||||
|
||||
//control commands
|
||||
void op_ccf();
|
||||
void op_scf();
|
||||
void op_nop();
|
||||
void op_halt();
|
||||
void op_stop();
|
||||
void op_di();
|
||||
void op_ei();
|
||||
|
||||
//jump commands
|
||||
void op_jp_nn();
|
||||
void op_jp_hl();
|
||||
template<unsigned x, bool y> void op_jp_f_nn();
|
||||
void op_jr_n();
|
||||
template<unsigned x, bool y> void op_jr_f_n();
|
||||
void op_call_nn();
|
||||
template<unsigned x, bool y> void op_call_f_nn();
|
||||
void op_ret();
|
||||
template<unsigned x, bool y> void op_ret_f();
|
||||
void op_reti();
|
||||
template<unsigned n> void op_rst_n();
|
||||
|
||||
//disassembler.cpp
|
||||
string disassemble(uint16 pc);
|
||||
string disassemble_opcode(uint16 pc);
|
||||
string disassemble_opcode_cb(uint16 pc);
|
@@ -1,560 +0,0 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
string CPU::disassemble(uint16 pc) {
|
||||
char output[80];
|
||||
memset(output, ' ', sizeof output);
|
||||
output[79] = 0;
|
||||
|
||||
string opcode = disassemble_opcode(pc);
|
||||
string registers = {
|
||||
" AF:", hex<4>(r[AF]),
|
||||
" BC:", hex<4>(r[BC]),
|
||||
" DE:", hex<4>(r[DE]),
|
||||
" HL:", hex<4>(r[HL]),
|
||||
" SP:", hex<4>(r[SP])
|
||||
};
|
||||
|
||||
memcpy(output + 0, hex<4>(pc), 4);
|
||||
memcpy(output + 6, opcode, opcode.length());
|
||||
memcpy(output + 23, registers, registers.length());
|
||||
output[63] = 0;
|
||||
return output;
|
||||
}
|
||||
|
||||
string CPU::disassemble_opcode(uint16 pc) {
|
||||
uint8 opcode = bus.read(pc);
|
||||
uint8 p0 = bus.read(pc + 1);
|
||||
uint8 p1 = bus.read(pc + 2);
|
||||
uint8 p2 = bus.read(pc + 3);
|
||||
|
||||
switch(opcode) {
|
||||
case 0x00: return { "nop" };
|
||||
case 0x01: return { "ld bc,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0x02: return { "ld (bc),a" };
|
||||
case 0x03: return { "inc bc" };
|
||||
case 0x04: return { "inc b" };
|
||||
case 0x05: return { "dec b" };
|
||||
case 0x06: return { "ld b,$", hex<2>(p0) };
|
||||
case 0x07: return { "rlc a" };
|
||||
case 0x08: return { "ld ($", hex<2>(p1), hex<2>(p0), "),sp" };
|
||||
case 0x09: return { "add hl,bc" };
|
||||
case 0x0a: return { "ld a,(bc)" };
|
||||
case 0x0b: return { "dec bc" };
|
||||
case 0x0c: return { "inc c" };
|
||||
case 0x0d: return { "dec c" };
|
||||
case 0x0e: return { "ld c,$", hex<2>(p0) };
|
||||
case 0x0f: return { "rrc a" };
|
||||
case 0x10: return { "stop" };
|
||||
case 0x11: return { "ld de,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0x12: return { "ld (de),a" };
|
||||
case 0x13: return { "inc de" };
|
||||
case 0x14: return { "inc d" };
|
||||
case 0x15: return { "dec d" };
|
||||
case 0x16: return { "ld d,$", hex<2>(p0) };
|
||||
case 0x17: return { "rl a" };
|
||||
case 0x18: return { "jr $", hex<4>(r[PC] + 2 + (int8)p0) };
|
||||
case 0x19: return { "add hl,de" };
|
||||
case 0x1a: return { "ld a,(de)" };
|
||||
case 0x1b: return { "dec de" };
|
||||
case 0x1c: return { "inc e" };
|
||||
case 0x1d: return { "dec e" };
|
||||
case 0x1e: return { "ld e,$", hex<2>(p0) };
|
||||
case 0x1f: return { "rr a" };
|
||||
case 0x20: return { "jr nz,$", hex<4>(r[PC] + 2 + (int8)p0) };
|
||||
case 0x21: return { "ld hl,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0x22: return { "ldi (hl),a" };
|
||||
case 0x23: return { "inc hl" };
|
||||
case 0x24: return { "inc h" };
|
||||
case 0x25: return { "dec h" };
|
||||
case 0x26: return { "ld h,$", hex<2>(p0) };
|
||||
case 0x27: return { "daa" };
|
||||
case 0x28: return { "jr z,$", hex<4>(r[PC] + 2 + (int8)p0) };
|
||||
case 0x29: return { "add hl,hl" };
|
||||
case 0x2a: return { "ldi a,(hl)" };
|
||||
case 0x2b: return { "dec hl" };
|
||||
case 0x2c: return { "inc l" };
|
||||
case 0x2d: return { "dec l" };
|
||||
case 0x2e: return { "ld l,$", hex<2>(p0) };
|
||||
case 0x2f: return { "cpl" };
|
||||
case 0x30: return { "jr nc,$", hex<4>(r[PC] + 2 + (int8)p0) };
|
||||
case 0x31: return { "ld sp,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0x32: return { "ldd (hl),a" };
|
||||
case 0x33: return { "inc sp" };
|
||||
case 0x34: return { "inc (hl)" };
|
||||
case 0x35: return { "dec (hl)" };
|
||||
case 0x36: return { "ld (hl),$", hex<2>(p0) };
|
||||
case 0x37: return { "scf" };
|
||||
case 0x38: return { "jr c,$", hex<4>(r[PC] + 2 + (int8)p0) };
|
||||
case 0x39: return { "add hl,sp" };
|
||||
case 0x3a: return { "ldd a,(hl)" };
|
||||
case 0x3b: return { "dec sp" };
|
||||
case 0x3c: return { "inc a" };
|
||||
case 0x3d: return { "dec a" };
|
||||
case 0x3e: return { "ld a,$", hex<2>(p0) };
|
||||
case 0x3f: return { "ccf" };
|
||||
case 0x40: return { "ld b,b" };
|
||||
case 0x41: return { "ld b,c" };
|
||||
case 0x42: return { "ld b,d" };
|
||||
case 0x43: return { "ld b,e" };
|
||||
case 0x44: return { "ld b,h" };
|
||||
case 0x45: return { "ld b,l" };
|
||||
case 0x46: return { "ld b,(hl)" };
|
||||
case 0x47: return { "ld b,a" };
|
||||
case 0x48: return { "ld c,b" };
|
||||
case 0x49: return { "ld c,c" };
|
||||
case 0x4a: return { "ld c,d" };
|
||||
case 0x4b: return { "ld c,e" };
|
||||
case 0x4c: return { "ld c,h" };
|
||||
case 0x4d: return { "ld c,l" };
|
||||
case 0x4e: return { "ld c,(hl)" };
|
||||
case 0x4f: return { "ld c,a" };
|
||||
case 0x50: return { "ld d,b" };
|
||||
case 0x51: return { "ld d,c" };
|
||||
case 0x52: return { "ld d,d" };
|
||||
case 0x53: return { "ld d,e" };
|
||||
case 0x54: return { "ld d,h" };
|
||||
case 0x55: return { "ld d,l" };
|
||||
case 0x56: return { "ld d,(hl)" };
|
||||
case 0x57: return { "ld d,a" };
|
||||
case 0x58: return { "ld e,b" };
|
||||
case 0x59: return { "ld e,c" };
|
||||
case 0x5a: return { "ld e,d" };
|
||||
case 0x5b: return { "ld e,e" };
|
||||
case 0x5c: return { "ld e,h" };
|
||||
case 0x5d: return { "ld e,l" };
|
||||
case 0x5e: return { "ld e,(hl)" };
|
||||
case 0x5f: return { "ld e,a" };
|
||||
case 0x60: return { "ld h,b" };
|
||||
case 0x61: return { "ld h,c" };
|
||||
case 0x62: return { "ld h,d" };
|
||||
case 0x63: return { "ld h,e" };
|
||||
case 0x64: return { "ld h,h" };
|
||||
case 0x65: return { "ld h,l" };
|
||||
case 0x66: return { "ld h,(hl)" };
|
||||
case 0x67: return { "ld h,a" };
|
||||
case 0x68: return { "ld l,b" };
|
||||
case 0x69: return { "ld l,c" };
|
||||
case 0x6a: return { "ld l,d" };
|
||||
case 0x6b: return { "ld l,e" };
|
||||
case 0x6c: return { "ld l,h" };
|
||||
case 0x6d: return { "ld l,l" };
|
||||
case 0x6e: return { "ld l,(hl)" };
|
||||
case 0x6f: return { "ld l,a" };
|
||||
case 0x70: return { "ld (hl),b" };
|
||||
case 0x71: return { "ld (hl),c" };
|
||||
case 0x72: return { "ld (hl),d" };
|
||||
case 0x73: return { "ld (hl),e" };
|
||||
case 0x74: return { "ld (hl),h" };
|
||||
case 0x75: return { "ld (hl),l" };
|
||||
case 0x76: return { "halt" };
|
||||
case 0x77: return { "ld (hl),a" };
|
||||
case 0x78: return { "ld a,b" };
|
||||
case 0x79: return { "ld a,c" };
|
||||
case 0x7a: return { "ld a,d" };
|
||||
case 0x7b: return { "ld a,e" };
|
||||
case 0x7c: return { "ld a,h" };
|
||||
case 0x7d: return { "ld a,l" };
|
||||
case 0x7e: return { "ld a,(hl)" };
|
||||
case 0x7f: return { "ld a,a" };
|
||||
case 0x80: return { "add a,b" };
|
||||
case 0x81: return { "add a,c" };
|
||||
case 0x82: return { "add a,d" };
|
||||
case 0x83: return { "add a,e" };
|
||||
case 0x84: return { "add a,h" };
|
||||
case 0x85: return { "add a,l" };
|
||||
case 0x86: return { "add a,(hl)" };
|
||||
case 0x87: return { "add a,a" };
|
||||
case 0x88: return { "adc a,b" };
|
||||
case 0x89: return { "adc a,c" };
|
||||
case 0x8a: return { "adc a,d" };
|
||||
case 0x8b: return { "adc a,e" };
|
||||
case 0x8c: return { "adc a,h" };
|
||||
case 0x8d: return { "adc a,l" };
|
||||
case 0x8e: return { "adc a,(hl)" };
|
||||
case 0x8f: return { "adc a,a" };
|
||||
case 0x90: return { "sub a,b" };
|
||||
case 0x91: return { "sub a,c" };
|
||||
case 0x92: return { "sub a,d" };
|
||||
case 0x93: return { "sub a,e" };
|
||||
case 0x94: return { "sub a,h" };
|
||||
case 0x95: return { "sub a,l" };
|
||||
case 0x96: return { "sub a,(hl)" };
|
||||
case 0x97: return { "sub a,a" };
|
||||
case 0x98: return { "sbc a,b" };
|
||||
case 0x99: return { "sbc a,c" };
|
||||
case 0x9a: return { "sbc a,d" };
|
||||
case 0x9b: return { "sbc a,e" };
|
||||
case 0x9c: return { "sbc a,h" };
|
||||
case 0x9d: return { "sbc a,l" };
|
||||
case 0x9e: return { "sbc a,(hl)" };
|
||||
case 0x9f: return { "sbc a,a" };
|
||||
case 0xa0: return { "and a,b" };
|
||||
case 0xa1: return { "and a,c" };
|
||||
case 0xa2: return { "and a,d" };
|
||||
case 0xa3: return { "and a,e" };
|
||||
case 0xa4: return { "and a,h" };
|
||||
case 0xa5: return { "and a,l" };
|
||||
case 0xa6: return { "and a,(hl)" };
|
||||
case 0xa7: return { "and a,a" };
|
||||
case 0xa8: return { "xor a,b" };
|
||||
case 0xa9: return { "xor a,c" };
|
||||
case 0xaa: return { "xor a,d" };
|
||||
case 0xab: return { "xor a,e" };
|
||||
case 0xac: return { "xor a,h" };
|
||||
case 0xad: return { "xor a,l" };
|
||||
case 0xae: return { "xor a,(hl)" };
|
||||
case 0xaf: return { "xor a,a" };
|
||||
case 0xb0: return { "or a,b" };
|
||||
case 0xb1: return { "or a,c" };
|
||||
case 0xb2: return { "or a,d" };
|
||||
case 0xb3: return { "or a,e" };
|
||||
case 0xb4: return { "or a,h" };
|
||||
case 0xb5: return { "or a,l" };
|
||||
case 0xb6: return { "or a,(hl)" };
|
||||
case 0xb7: return { "or a,a" };
|
||||
case 0xb8: return { "cp a,b" };
|
||||
case 0xb9: return { "cp a,c" };
|
||||
case 0xba: return { "cp a,d" };
|
||||
case 0xbb: return { "cp a,e" };
|
||||
case 0xbc: return { "cp a,h" };
|
||||
case 0xbd: return { "cp a,l" };
|
||||
case 0xbe: return { "cp a,(hl)" };
|
||||
case 0xbf: return { "cp a,a" };
|
||||
case 0xc0: return { "ret nz" };
|
||||
case 0xc1: return { "pop bc" };
|
||||
case 0xc2: return { "jp nz,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xc3: return { "jp $", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xc4: return { "call nz,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xc5: return { "push bc" };
|
||||
case 0xc6: return { "add a,$", hex<2>(p0) };
|
||||
case 0xc7: return { "rst $0000" };
|
||||
case 0xc8: return { "ret z" };
|
||||
case 0xc9: return { "ret" };
|
||||
case 0xca: return { "jp z,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xcb: return disassemble_opcode_cb(pc + 1);
|
||||
case 0xcc: return { "call z,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xcd: return { "call $", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xce: return { "adc a,$", hex<2>(p0) };
|
||||
case 0xcf: return { "rst $0008" };
|
||||
case 0xd0: return { "ret nc" };
|
||||
case 0xd1: return { "pop de" };
|
||||
case 0xd2: return { "jp nc,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xd3: return { "xx" };
|
||||
case 0xd4: return { "call nc,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xd5: return { "push de" };
|
||||
case 0xd6: return { "sub a,$", hex<2>(p0) };
|
||||
case 0xd7: return { "rst $0010" };
|
||||
case 0xd8: return { "ret c" };
|
||||
case 0xd9: return { "reti" };
|
||||
case 0xda: return { "jp c,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xdb: return { "xx" };
|
||||
case 0xdc: return { "call c,$", hex<2>(p1), hex<2>(p0) };
|
||||
case 0xdd: return { "xx" };
|
||||
case 0xde: return { "sbc a,$", hex<2>(p0) };
|
||||
case 0xdf: return { "rst $0018" };
|
||||
case 0xe0: return { "ld ($ff", hex<2>(p0), "),a" };
|
||||
case 0xe1: return { "pop hl" };
|
||||
case 0xe2: return { "ld ($ff00+c),a" };
|
||||
case 0xe3: return { "xx" };
|
||||
case 0xe4: return { "xx" };
|
||||
case 0xe5: return { "push hl" };
|
||||
case 0xe6: return { "and a,$", hex<2>(p0) };
|
||||
case 0xe7: return { "rst $0020" };
|
||||
case 0xe8: return { "add sp,$", hex<4>((int8)p0) };
|
||||
case 0xe9: return { "jp hl" };
|
||||
case 0xea: return { "ld ($", hex<2>(p1), hex<2>(p0), "),a" };
|
||||
case 0xeb: return { "xx" };
|
||||
case 0xec: return { "xx" };
|
||||
case 0xed: return { "xx" };
|
||||
case 0xee: return { "xor a,$", hex<2>(p0) };
|
||||
case 0xef: return { "rst $0028" };
|
||||
case 0xf0: return { "ld a,($ff", hex<2>(p0), ")" };
|
||||
case 0xf1: return { "pop af" };
|
||||
case 0xf2: return { "ld a,($ff00+c)" };
|
||||
case 0xf3: return { "di" };
|
||||
case 0xf4: return { "xx" };
|
||||
case 0xf5: return { "push af" };
|
||||
case 0xf6: return { "or a,$", hex<2>(p0) };
|
||||
case 0xf7: return { "rst $0030" };
|
||||
case 0xf8: return { "ld hl,sp+$", hex<4>((int8)p0) };
|
||||
case 0xf9: return { "ld sp,hl" };
|
||||
case 0xfa: return { "ld a,($", hex<2>(p1), hex<2>(p0), ")" };
|
||||
case 0xfb: return { "ei" };
|
||||
case 0xfc: return { "xx" };
|
||||
case 0xfd: return { "xx" };
|
||||
case 0xfe: return { "cp a,$", hex<2>(p0) };
|
||||
case 0xff: return { "rst $0038" };
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
string CPU::disassemble_opcode_cb(uint16 pc) {
|
||||
uint8 opcode = bus.read(pc);
|
||||
uint8 p0 = bus.read(pc + 1);
|
||||
uint8 p1 = bus.read(pc + 2);
|
||||
uint8 p2 = bus.read(pc + 3);
|
||||
|
||||
switch(opcode) {
|
||||
case 0x00: return { "rlc b" };
|
||||
case 0x01: return { "rlc c" };
|
||||
case 0x02: return { "rlc d" };
|
||||
case 0x03: return { "rlc e" };
|
||||
case 0x04: return { "rlc h" };
|
||||
case 0x05: return { "rlc l" };
|
||||
case 0x06: return { "rlc (hl)" };
|
||||
case 0x07: return { "rlc a" };
|
||||
case 0x08: return { "rrc b" };
|
||||
case 0x09: return { "rrc c" };
|
||||
case 0x0a: return { "rrc d" };
|
||||
case 0x0b: return { "rrc e" };
|
||||
case 0x0c: return { "rrc h" };
|
||||
case 0x0d: return { "rrc l" };
|
||||
case 0x0e: return { "rrc (hl)" };
|
||||
case 0x0f: return { "rrc a" };
|
||||
case 0x10: return { "rl b" };
|
||||
case 0x11: return { "rl c" };
|
||||
case 0x12: return { "rl d" };
|
||||
case 0x13: return { "rl e" };
|
||||
case 0x14: return { "rl h" };
|
||||
case 0x15: return { "rl l" };
|
||||
case 0x16: return { "rl (hl)" };
|
||||
case 0x17: return { "rl a" };
|
||||
case 0x18: return { "rr b" };
|
||||
case 0x19: return { "rr c" };
|
||||
case 0x1a: return { "rr d" };
|
||||
case 0x1b: return { "rr e" };
|
||||
case 0x1c: return { "rr h" };
|
||||
case 0x1d: return { "rr l" };
|
||||
case 0x1e: return { "rr (hl)" };
|
||||
case 0x1f: return { "rr a" };
|
||||
case 0x20: return { "sla b" };
|
||||
case 0x21: return { "sla c" };
|
||||
case 0x22: return { "sla d" };
|
||||
case 0x23: return { "sla e" };
|
||||
case 0x24: return { "sla h" };
|
||||
case 0x25: return { "sla l" };
|
||||
case 0x26: return { "sla (hl)" };
|
||||
case 0x27: return { "sla a" };
|
||||
case 0x28: return { "sra b" };
|
||||
case 0x29: return { "sra c" };
|
||||
case 0x2a: return { "sra d" };
|
||||
case 0x2b: return { "sra e" };
|
||||
case 0x2c: return { "sra h" };
|
||||
case 0x2d: return { "sra l" };
|
||||
case 0x2e: return { "sra (hl)" };
|
||||
case 0x2f: return { "sra a" };
|
||||
case 0x30: return { "swap b" };
|
||||
case 0x31: return { "swap c" };
|
||||
case 0x32: return { "swap d" };
|
||||
case 0x33: return { "swap e" };
|
||||
case 0x34: return { "swap h" };
|
||||
case 0x35: return { "swap l" };
|
||||
case 0x36: return { "swap (hl)" };
|
||||
case 0x37: return { "swap a" };
|
||||
case 0x38: return { "srl b" };
|
||||
case 0x39: return { "srl c" };
|
||||
case 0x3a: return { "srl d" };
|
||||
case 0x3b: return { "srl e" };
|
||||
case 0x3c: return { "srl h" };
|
||||
case 0x3d: return { "srl l" };
|
||||
case 0x3e: return { "srl (hl)" };
|
||||
case 0x3f: return { "srl a" };
|
||||
case 0x40: return { "bit 0,b" };
|
||||
case 0x41: return { "bit 0,c" };
|
||||
case 0x42: return { "bit 0,d" };
|
||||
case 0x43: return { "bit 0,e" };
|
||||
case 0x44: return { "bit 0,h" };
|
||||
case 0x45: return { "bit 0,l" };
|
||||
case 0x46: return { "bit 0,(hl)" };
|
||||
case 0x47: return { "bit 0,a" };
|
||||
case 0x48: return { "bit 1,b" };
|
||||
case 0x49: return { "bit 1,c" };
|
||||
case 0x4a: return { "bit 1,d" };
|
||||
case 0x4b: return { "bit 1,e" };
|
||||
case 0x4c: return { "bit 1,h" };
|
||||
case 0x4d: return { "bit 1,l" };
|
||||
case 0x4e: return { "bit 1,(hl)" };
|
||||
case 0x4f: return { "bit 1,a" };
|
||||
case 0x50: return { "bit 2,b" };
|
||||
case 0x51: return { "bit 2,c" };
|
||||
case 0x52: return { "bit 2,d" };
|
||||
case 0x53: return { "bit 2,e" };
|
||||
case 0x54: return { "bit 2,h" };
|
||||
case 0x55: return { "bit 2,l" };
|
||||
case 0x56: return { "bit 2,(hl)" };
|
||||
case 0x57: return { "bit 2,a" };
|
||||
case 0x58: return { "bit 3,b" };
|
||||
case 0x59: return { "bit 3,c" };
|
||||
case 0x5a: return { "bit 3,d" };
|
||||
case 0x5b: return { "bit 3,e" };
|
||||
case 0x5c: return { "bit 3,h" };
|
||||
case 0x5d: return { "bit 3,l" };
|
||||
case 0x5e: return { "bit 3,(hl)" };
|
||||
case 0x5f: return { "bit 3,a" };
|
||||
case 0x60: return { "bit 4,b" };
|
||||
case 0x61: return { "bit 4,c" };
|
||||
case 0x62: return { "bit 4,d" };
|
||||
case 0x63: return { "bit 4,e" };
|
||||
case 0x64: return { "bit 4,h" };
|
||||
case 0x65: return { "bit 4,l" };
|
||||
case 0x66: return { "bit 4,(hl)" };
|
||||
case 0x67: return { "bit 4,a" };
|
||||
case 0x68: return { "bit 5,b" };
|
||||
case 0x69: return { "bit 5,c" };
|
||||
case 0x6a: return { "bit 5,d" };
|
||||
case 0x6b: return { "bit 5,e" };
|
||||
case 0x6c: return { "bit 5,h" };
|
||||
case 0x6d: return { "bit 5,l" };
|
||||
case 0x6e: return { "bit 5,(hl)" };
|
||||
case 0x6f: return { "bit 5,a" };
|
||||
case 0x70: return { "bit 6,b" };
|
||||
case 0x71: return { "bit 6,c" };
|
||||
case 0x72: return { "bit 6,d" };
|
||||
case 0x73: return { "bit 6,e" };
|
||||
case 0x74: return { "bit 6,h" };
|
||||
case 0x75: return { "bit 6,l" };
|
||||
case 0x76: return { "bit 6,(hl)" };
|
||||
case 0x77: return { "bit 6,a" };
|
||||
case 0x78: return { "bit 7,b" };
|
||||
case 0x79: return { "bit 7,c" };
|
||||
case 0x7a: return { "bit 7,d" };
|
||||
case 0x7b: return { "bit 7,e" };
|
||||
case 0x7c: return { "bit 7,h" };
|
||||
case 0x7d: return { "bit 7,l" };
|
||||
case 0x7e: return { "bit 7,(hl)" };
|
||||
case 0x7f: return { "bit 7,a" };
|
||||
case 0x80: return { "res 0,b" };
|
||||
case 0x81: return { "res 0,c" };
|
||||
case 0x82: return { "res 0,d" };
|
||||
case 0x83: return { "res 0,e" };
|
||||
case 0x84: return { "res 0,h" };
|
||||
case 0x85: return { "res 0,l" };
|
||||
case 0x86: return { "res 0,(hl)" };
|
||||
case 0x87: return { "res 0,a" };
|
||||
case 0x88: return { "res 1,b" };
|
||||
case 0x89: return { "res 1,c" };
|
||||
case 0x8a: return { "res 1,d" };
|
||||
case 0x8b: return { "res 1,e" };
|
||||
case 0x8c: return { "res 1,h" };
|
||||
case 0x8d: return { "res 1,l" };
|
||||
case 0x8e: return { "res 1,(hl)" };
|
||||
case 0x8f: return { "res 1,a" };
|
||||
case 0x90: return { "res 2,b" };
|
||||
case 0x91: return { "res 2,c" };
|
||||
case 0x92: return { "res 2,d" };
|
||||
case 0x93: return { "res 2,e" };
|
||||
case 0x94: return { "res 2,h" };
|
||||
case 0x95: return { "res 2,l" };
|
||||
case 0x96: return { "res 2,(hl)" };
|
||||
case 0x97: return { "res 2,a" };
|
||||
case 0x98: return { "res 3,b" };
|
||||
case 0x99: return { "res 3,c" };
|
||||
case 0x9a: return { "res 3,d" };
|
||||
case 0x9b: return { "res 3,e" };
|
||||
case 0x9c: return { "res 3,h" };
|
||||
case 0x9d: return { "res 3,l" };
|
||||
case 0x9e: return { "res 3,(hl)" };
|
||||
case 0x9f: return { "res 3,a" };
|
||||
case 0xa0: return { "res 4,b" };
|
||||
case 0xa1: return { "res 4,c" };
|
||||
case 0xa2: return { "res 4,d" };
|
||||
case 0xa3: return { "res 4,e" };
|
||||
case 0xa4: return { "res 4,h" };
|
||||
case 0xa5: return { "res 4,l" };
|
||||
case 0xa6: return { "res 4,(hl)" };
|
||||
case 0xa7: return { "res 4,a" };
|
||||
case 0xa8: return { "res 5,b" };
|
||||
case 0xa9: return { "res 5,c" };
|
||||
case 0xaa: return { "res 5,d" };
|
||||
case 0xab: return { "res 5,e" };
|
||||
case 0xac: return { "res 5,h" };
|
||||
case 0xad: return { "res 5,l" };
|
||||
case 0xae: return { "res 5,(hl)" };
|
||||
case 0xaf: return { "res 5,a" };
|
||||
case 0xb0: return { "res 6,b" };
|
||||
case 0xb1: return { "res 6,c" };
|
||||
case 0xb2: return { "res 6,d" };
|
||||
case 0xb3: return { "res 6,e" };
|
||||
case 0xb4: return { "res 6,h" };
|
||||
case 0xb5: return { "res 6,l" };
|
||||
case 0xb6: return { "res 6,(hl)" };
|
||||
case 0xb7: return { "res 6,a" };
|
||||
case 0xb8: return { "res 7,b" };
|
||||
case 0xb9: return { "res 7,c" };
|
||||
case 0xba: return { "res 7,d" };
|
||||
case 0xbb: return { "res 7,e" };
|
||||
case 0xbc: return { "res 7,h" };
|
||||
case 0xbd: return { "res 7,l" };
|
||||
case 0xbe: return { "res 7,(hl)" };
|
||||
case 0xbf: return { "res 7,a" };
|
||||
case 0xc0: return { "set 0,b" };
|
||||
case 0xc1: return { "set 0,c" };
|
||||
case 0xc2: return { "set 0,d" };
|
||||
case 0xc3: return { "set 0,e" };
|
||||
case 0xc4: return { "set 0,h" };
|
||||
case 0xc5: return { "set 0,l" };
|
||||
case 0xc6: return { "set 0,(hl)" };
|
||||
case 0xc7: return { "set 0,a" };
|
||||
case 0xc8: return { "set 1,b" };
|
||||
case 0xc9: return { "set 1,c" };
|
||||
case 0xca: return { "set 1,d" };
|
||||
case 0xcb: return { "set 1,e" };
|
||||
case 0xcc: return { "set 1,h" };
|
||||
case 0xcd: return { "set 1,l" };
|
||||
case 0xce: return { "set 1,(hl)" };
|
||||
case 0xcf: return { "set 1,a" };
|
||||
case 0xd0: return { "set 2,b" };
|
||||
case 0xd1: return { "set 2,c" };
|
||||
case 0xd2: return { "set 2,d" };
|
||||
case 0xd3: return { "set 2,e" };
|
||||
case 0xd4: return { "set 2,h" };
|
||||
case 0xd5: return { "set 2,l" };
|
||||
case 0xd6: return { "set 2,(hl)" };
|
||||
case 0xd7: return { "set 2,a" };
|
||||
case 0xd8: return { "set 3,b" };
|
||||
case 0xd9: return { "set 3,c" };
|
||||
case 0xda: return { "set 3,d" };
|
||||
case 0xdb: return { "set 3,e" };
|
||||
case 0xdc: return { "set 3,h" };
|
||||
case 0xdd: return { "set 3,l" };
|
||||
case 0xde: return { "set 3,(hl)" };
|
||||
case 0xdf: return { "set 3,a" };
|
||||
case 0xe0: return { "set 4,b" };
|
||||
case 0xe1: return { "set 4,c" };
|
||||
case 0xe2: return { "set 4,d" };
|
||||
case 0xe3: return { "set 4,e" };
|
||||
case 0xe4: return { "set 4,h" };
|
||||
case 0xe5: return { "set 4,l" };
|
||||
case 0xe6: return { "set 4,(hl)" };
|
||||
case 0xe7: return { "set 4,a" };
|
||||
case 0xe8: return { "set 5,b" };
|
||||
case 0xe9: return { "set 5,c" };
|
||||
case 0xea: return { "set 5,d" };
|
||||
case 0xeb: return { "set 5,e" };
|
||||
case 0xec: return { "set 5,h" };
|
||||
case 0xed: return { "set 5,l" };
|
||||
case 0xee: return { "set 5,(hl)" };
|
||||
case 0xef: return { "set 5,a" };
|
||||
case 0xf0: return { "set 6,b" };
|
||||
case 0xf1: return { "set 6,c" };
|
||||
case 0xf2: return { "set 6,d" };
|
||||
case 0xf3: return { "set 6,e" };
|
||||
case 0xf4: return { "set 6,h" };
|
||||
case 0xf5: return { "set 6,l" };
|
||||
case 0xf6: return { "set 6,(hl)" };
|
||||
case 0xf7: return { "set 6,a" };
|
||||
case 0xf8: return { "set 7,b" };
|
||||
case 0xf9: return { "set 7,c" };
|
||||
case 0xfa: return { "set 7,d" };
|
||||
case 0xfb: return { "set 7,e" };
|
||||
case 0xfc: return { "set 7,h" };
|
||||
case 0xfd: return { "set 7,l" };
|
||||
case 0xfe: return { "set 7,(hl)" };
|
||||
case 0xff: return { "set 7,a" };
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,101 +0,0 @@
|
||||
enum {
|
||||
A, F, AF,
|
||||
B, C, BC,
|
||||
D, E, DE,
|
||||
H, L, HL,
|
||||
SP, PC,
|
||||
};
|
||||
|
||||
enum {
|
||||
ZF, NF, HF, CF,
|
||||
};
|
||||
|
||||
//register base class
|
||||
//the idea here is to have all registers derive from a single base class.
|
||||
//this allows construction of opcodes that can take any register as input or output,
|
||||
//despite the fact that behind-the-scenes, special handling is done for eg: F, AF, HL, etc.
|
||||
//registers can also be chained together: eg af = 0x0000 writes both a and f.
|
||||
struct Register {
|
||||
virtual operator unsigned() const = 0;
|
||||
virtual unsigned operator=(unsigned x) = 0;
|
||||
Register& operator=(const Register &x) { operator=((unsigned)x); return *this; }
|
||||
|
||||
unsigned operator++(int) { unsigned r = *this; operator=(*this + 1); return r; }
|
||||
unsigned operator--(int) { unsigned r = *this; operator=(*this - 1); return r; }
|
||||
unsigned operator++() { return operator=(*this + 1); }
|
||||
unsigned operator--() { return operator=(*this - 1); }
|
||||
|
||||
unsigned operator |=(unsigned x) { return operator=(*this | x); }
|
||||
unsigned operator ^=(unsigned x) { return operator=(*this ^ x); }
|
||||
unsigned operator &=(unsigned x) { return operator=(*this & x); }
|
||||
|
||||
unsigned operator<<=(unsigned x) { return operator=(*this << x); }
|
||||
unsigned operator>>=(unsigned x) { return operator=(*this >> x); }
|
||||
|
||||
unsigned operator +=(unsigned x) { return operator=(*this + x); }
|
||||
unsigned operator -=(unsigned x) { return operator=(*this - x); }
|
||||
unsigned operator *=(unsigned x) { return operator=(*this * x); }
|
||||
unsigned operator /=(unsigned x) { return operator=(*this / x); }
|
||||
unsigned operator %=(unsigned x) { return operator=(*this % x); }
|
||||
};
|
||||
|
||||
struct Register8 : Register {
|
||||
uint8 data;
|
||||
operator unsigned() const { return data; }
|
||||
unsigned operator=(unsigned x) { return data = x; }
|
||||
};
|
||||
|
||||
struct RegisterF : Register {
|
||||
bool z, n, h, c;
|
||||
operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); }
|
||||
unsigned operator=(unsigned x) { z = x & 0x80; n = x & 0x40; h = x & 0x20; c = x & 0x10; return *this; }
|
||||
bool& operator[](unsigned r) {
|
||||
static bool* table[] = { &z, &n, &h, &c };
|
||||
return *table[r];
|
||||
}
|
||||
};
|
||||
|
||||
struct Register16 : Register {
|
||||
uint16 data;
|
||||
operator unsigned() const { return data; }
|
||||
unsigned operator=(unsigned x) { return data = x; }
|
||||
};
|
||||
|
||||
struct RegisterAF : Register {
|
||||
Register8 &hi;
|
||||
RegisterF &lo;
|
||||
operator unsigned() const { return (hi << 8) | (lo << 0); }
|
||||
unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
|
||||
RegisterAF(Register8 &hi, RegisterF &lo) : hi(hi), lo(lo) {}
|
||||
};
|
||||
|
||||
struct RegisterW : Register {
|
||||
Register8 &hi, &lo;
|
||||
operator unsigned() const { return (hi << 8) | (lo << 0); }
|
||||
unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
|
||||
RegisterW(Register8 &hi, Register8 &lo) : hi(hi), lo(lo) {}
|
||||
};
|
||||
|
||||
struct Registers {
|
||||
Register8 a;
|
||||
RegisterF f;
|
||||
RegisterAF af;
|
||||
Register8 b;
|
||||
Register8 c;
|
||||
RegisterW bc;
|
||||
Register8 d;
|
||||
Register8 e;
|
||||
RegisterW de;
|
||||
Register8 h;
|
||||
Register8 l;
|
||||
RegisterW hl;
|
||||
Register16 sp;
|
||||
Register16 pc;
|
||||
|
||||
Register& operator[](unsigned r) {
|
||||
static Register* table[] = { &a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc };
|
||||
return *table[r];
|
||||
}
|
||||
|
||||
Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {}
|
||||
} r;
|
@@ -1,519 +0,0 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
void CPU::initialize_opcode_table() {
|
||||
opcode_table[0x00] = &CPU::op_nop;
|
||||
opcode_table[0x01] = &CPU::op_ld_rr_nn<BC>;
|
||||
opcode_table[0x02] = &CPU::op_ld_rr_a<BC>;
|
||||
opcode_table[0x03] = &CPU::op_inc_rr<BC>;
|
||||
opcode_table[0x04] = &CPU::op_inc_r<B>;
|
||||
opcode_table[0x05] = &CPU::op_dec_r<B>;
|
||||
opcode_table[0x06] = &CPU::op_ld_r_n<B>;
|
||||
opcode_table[0x07] = &CPU::op_rlca;
|
||||
opcode_table[0x08] = &CPU::op_ld_nn_sp;
|
||||
opcode_table[0x09] = &CPU::op_add_hl_rr<BC>;
|
||||
opcode_table[0x0a] = &CPU::op_ld_a_rr<BC>;
|
||||
opcode_table[0x0b] = &CPU::op_dec_rr<BC>;
|
||||
opcode_table[0x0c] = &CPU::op_inc_r<C>;
|
||||
opcode_table[0x0d] = &CPU::op_dec_r<C>;
|
||||
opcode_table[0x0e] = &CPU::op_ld_r_n<C>;
|
||||
opcode_table[0x0f] = &CPU::op_rrca;
|
||||
opcode_table[0x10] = &CPU::op_stop;
|
||||
opcode_table[0x11] = &CPU::op_ld_rr_nn<DE>;
|
||||
opcode_table[0x12] = &CPU::op_ld_rr_a<DE>;
|
||||
opcode_table[0x13] = &CPU::op_inc_rr<DE>;
|
||||
opcode_table[0x14] = &CPU::op_inc_r<D>;
|
||||
opcode_table[0x15] = &CPU::op_dec_r<D>;
|
||||
opcode_table[0x16] = &CPU::op_ld_r_n<D>;
|
||||
opcode_table[0x17] = &CPU::op_rla;
|
||||
opcode_table[0x18] = &CPU::op_jr_n;
|
||||
opcode_table[0x19] = &CPU::op_add_hl_rr<DE>;
|
||||
opcode_table[0x1a] = &CPU::op_ld_a_rr<DE>;
|
||||
opcode_table[0x1b] = &CPU::op_dec_rr<DE>;
|
||||
opcode_table[0x1c] = &CPU::op_inc_r<E>;
|
||||
opcode_table[0x1d] = &CPU::op_dec_r<E>;
|
||||
opcode_table[0x1e] = &CPU::op_ld_r_n<E>;
|
||||
opcode_table[0x1f] = &CPU::op_rra;
|
||||
opcode_table[0x20] = &CPU::op_jr_f_n<ZF, 0>;
|
||||
opcode_table[0x21] = &CPU::op_ld_rr_nn<HL>;
|
||||
opcode_table[0x22] = &CPU::op_ldi_hl_a;
|
||||
opcode_table[0x23] = &CPU::op_inc_rr<HL>;
|
||||
opcode_table[0x24] = &CPU::op_inc_r<H>;
|
||||
opcode_table[0x25] = &CPU::op_dec_r<H>;
|
||||
opcode_table[0x26] = &CPU::op_ld_r_n<H>;
|
||||
opcode_table[0x27] = &CPU::op_daa;
|
||||
opcode_table[0x28] = &CPU::op_jr_f_n<ZF, 1>;
|
||||
opcode_table[0x29] = &CPU::op_add_hl_rr<HL>;
|
||||
opcode_table[0x2a] = &CPU::op_ldi_a_hl;
|
||||
opcode_table[0x2b] = &CPU::op_dec_rr<HL>;
|
||||
opcode_table[0x2c] = &CPU::op_inc_r<L>;
|
||||
opcode_table[0x2d] = &CPU::op_dec_r<L>;
|
||||
opcode_table[0x2e] = &CPU::op_ld_r_n<L>;
|
||||
opcode_table[0x2f] = &CPU::op_cpl;
|
||||
opcode_table[0x30] = &CPU::op_jr_f_n<CF, 0>;
|
||||
opcode_table[0x31] = &CPU::op_ld_rr_nn<SP>;
|
||||
opcode_table[0x32] = &CPU::op_ldd_hl_a;
|
||||
opcode_table[0x33] = &CPU::op_inc_rr<SP>;
|
||||
opcode_table[0x34] = &CPU::op_inc_hl;
|
||||
opcode_table[0x35] = &CPU::op_dec_hl;
|
||||
opcode_table[0x36] = &CPU::op_ld_hl_n;
|
||||
opcode_table[0x37] = &CPU::op_scf;
|
||||
opcode_table[0x38] = &CPU::op_jr_f_n<CF, 1>;
|
||||
opcode_table[0x39] = &CPU::op_add_hl_rr<SP>;
|
||||
opcode_table[0x3a] = &CPU::op_ldd_a_hl;
|
||||
opcode_table[0x3b] = &CPU::op_dec_rr<SP>;
|
||||
opcode_table[0x3c] = &CPU::op_inc_r<A>;
|
||||
opcode_table[0x3d] = &CPU::op_dec_r<A>;
|
||||
opcode_table[0x3e] = &CPU::op_ld_r_n<A>;
|
||||
opcode_table[0x3f] = &CPU::op_ccf;
|
||||
opcode_table[0x40] = &CPU::op_ld_r_r<B, B>;
|
||||
opcode_table[0x41] = &CPU::op_ld_r_r<B, C>;
|
||||
opcode_table[0x42] = &CPU::op_ld_r_r<B, D>;
|
||||
opcode_table[0x43] = &CPU::op_ld_r_r<B, E>;
|
||||
opcode_table[0x44] = &CPU::op_ld_r_r<B, H>;
|
||||
opcode_table[0x45] = &CPU::op_ld_r_r<B, L>;
|
||||
opcode_table[0x46] = &CPU::op_ld_r_hl<B>;
|
||||
opcode_table[0x47] = &CPU::op_ld_r_r<B, A>;
|
||||
opcode_table[0x48] = &CPU::op_ld_r_r<C, B>;
|
||||
opcode_table[0x49] = &CPU::op_ld_r_r<C, C>;
|
||||
opcode_table[0x4a] = &CPU::op_ld_r_r<C, D>;
|
||||
opcode_table[0x4b] = &CPU::op_ld_r_r<C, E>;
|
||||
opcode_table[0x4c] = &CPU::op_ld_r_r<C, H>;
|
||||
opcode_table[0x4d] = &CPU::op_ld_r_r<C, L>;
|
||||
opcode_table[0x4e] = &CPU::op_ld_r_hl<C>;
|
||||
opcode_table[0x4f] = &CPU::op_ld_r_r<C, A>;
|
||||
opcode_table[0x50] = &CPU::op_ld_r_r<D, B>;
|
||||
opcode_table[0x51] = &CPU::op_ld_r_r<D, C>;
|
||||
opcode_table[0x52] = &CPU::op_ld_r_r<D, D>;
|
||||
opcode_table[0x53] = &CPU::op_ld_r_r<D, E>;
|
||||
opcode_table[0x54] = &CPU::op_ld_r_r<D, H>;
|
||||
opcode_table[0x55] = &CPU::op_ld_r_r<D, L>;
|
||||
opcode_table[0x56] = &CPU::op_ld_r_hl<D>;
|
||||
opcode_table[0x57] = &CPU::op_ld_r_r<D, A>;
|
||||
opcode_table[0x58] = &CPU::op_ld_r_r<E, B>;
|
||||
opcode_table[0x59] = &CPU::op_ld_r_r<E, C>;
|
||||
opcode_table[0x5a] = &CPU::op_ld_r_r<E, D>;
|
||||
opcode_table[0x5b] = &CPU::op_ld_r_r<E, E>;
|
||||
opcode_table[0x5c] = &CPU::op_ld_r_r<E, H>;
|
||||
opcode_table[0x5d] = &CPU::op_ld_r_r<E, L>;
|
||||
opcode_table[0x5e] = &CPU::op_ld_r_hl<E>;
|
||||
opcode_table[0x5f] = &CPU::op_ld_r_r<E, A>;
|
||||
opcode_table[0x60] = &CPU::op_ld_r_r<H, B>;
|
||||
opcode_table[0x61] = &CPU::op_ld_r_r<H, C>;
|
||||
opcode_table[0x62] = &CPU::op_ld_r_r<H, D>;
|
||||
opcode_table[0x63] = &CPU::op_ld_r_r<H, E>;
|
||||
opcode_table[0x64] = &CPU::op_ld_r_r<H, H>;
|
||||
opcode_table[0x65] = &CPU::op_ld_r_r<H, L>;
|
||||
opcode_table[0x66] = &CPU::op_ld_r_hl<H>;
|
||||
opcode_table[0x67] = &CPU::op_ld_r_r<H, A>;
|
||||
opcode_table[0x68] = &CPU::op_ld_r_r<L, B>;
|
||||
opcode_table[0x69] = &CPU::op_ld_r_r<L, C>;
|
||||
opcode_table[0x6a] = &CPU::op_ld_r_r<L, D>;
|
||||
opcode_table[0x6b] = &CPU::op_ld_r_r<L, E>;
|
||||
opcode_table[0x6c] = &CPU::op_ld_r_r<L, H>;
|
||||
opcode_table[0x6d] = &CPU::op_ld_r_r<L, L>;
|
||||
opcode_table[0x6e] = &CPU::op_ld_r_hl<L>;
|
||||
opcode_table[0x6f] = &CPU::op_ld_r_r<L, A>;
|
||||
opcode_table[0x70] = &CPU::op_ld_hl_r<B>;
|
||||
opcode_table[0x71] = &CPU::op_ld_hl_r<C>;
|
||||
opcode_table[0x72] = &CPU::op_ld_hl_r<D>;
|
||||
opcode_table[0x73] = &CPU::op_ld_hl_r<E>;
|
||||
opcode_table[0x74] = &CPU::op_ld_hl_r<H>;
|
||||
opcode_table[0x75] = &CPU::op_ld_hl_r<L>;
|
||||
opcode_table[0x76] = &CPU::op_halt;
|
||||
opcode_table[0x77] = &CPU::op_ld_hl_r<A>;
|
||||
opcode_table[0x78] = &CPU::op_ld_r_r<A, B>;
|
||||
opcode_table[0x79] = &CPU::op_ld_r_r<A, C>;
|
||||
opcode_table[0x7a] = &CPU::op_ld_r_r<A, D>;
|
||||
opcode_table[0x7b] = &CPU::op_ld_r_r<A, E>;
|
||||
opcode_table[0x7c] = &CPU::op_ld_r_r<A, H>;
|
||||
opcode_table[0x7d] = &CPU::op_ld_r_r<A, L>;
|
||||
opcode_table[0x7e] = &CPU::op_ld_r_hl<A>;
|
||||
opcode_table[0x7f] = &CPU::op_ld_r_r<A, A>;
|
||||
opcode_table[0x80] = &CPU::op_add_a_r<B>;
|
||||
opcode_table[0x81] = &CPU::op_add_a_r<C>;
|
||||
opcode_table[0x82] = &CPU::op_add_a_r<D>;
|
||||
opcode_table[0x83] = &CPU::op_add_a_r<E>;
|
||||
opcode_table[0x84] = &CPU::op_add_a_r<H>;
|
||||
opcode_table[0x85] = &CPU::op_add_a_r<L>;
|
||||
opcode_table[0x86] = &CPU::op_add_a_hl;
|
||||
opcode_table[0x87] = &CPU::op_add_a_r<A>;
|
||||
opcode_table[0x88] = &CPU::op_adc_a_r<B>;
|
||||
opcode_table[0x89] = &CPU::op_adc_a_r<C>;
|
||||
opcode_table[0x8a] = &CPU::op_adc_a_r<D>;
|
||||
opcode_table[0x8b] = &CPU::op_adc_a_r<E>;
|
||||
opcode_table[0x8c] = &CPU::op_adc_a_r<H>;
|
||||
opcode_table[0x8d] = &CPU::op_adc_a_r<L>;
|
||||
opcode_table[0x8e] = &CPU::op_adc_a_hl;
|
||||
opcode_table[0x8f] = &CPU::op_adc_a_r<A>;
|
||||
opcode_table[0x90] = &CPU::op_sub_a_r<B>;
|
||||
opcode_table[0x91] = &CPU::op_sub_a_r<C>;
|
||||
opcode_table[0x92] = &CPU::op_sub_a_r<D>;
|
||||
opcode_table[0x93] = &CPU::op_sub_a_r<E>;
|
||||
opcode_table[0x94] = &CPU::op_sub_a_r<H>;
|
||||
opcode_table[0x95] = &CPU::op_sub_a_r<L>;
|
||||
opcode_table[0x96] = &CPU::op_sub_a_hl;
|
||||
opcode_table[0x97] = &CPU::op_sub_a_r<A>;
|
||||
opcode_table[0x98] = &CPU::op_sbc_a_r<B>;
|
||||
opcode_table[0x99] = &CPU::op_sbc_a_r<C>;
|
||||
opcode_table[0x9a] = &CPU::op_sbc_a_r<D>;
|
||||
opcode_table[0x9b] = &CPU::op_sbc_a_r<E>;
|
||||
opcode_table[0x9c] = &CPU::op_sbc_a_r<H>;
|
||||
opcode_table[0x9d] = &CPU::op_sbc_a_r<L>;
|
||||
opcode_table[0x9e] = &CPU::op_sbc_a_hl;
|
||||
opcode_table[0x9f] = &CPU::op_sbc_a_r<A>;
|
||||
opcode_table[0xa0] = &CPU::op_and_a_r<B>;
|
||||
opcode_table[0xa1] = &CPU::op_and_a_r<C>;
|
||||
opcode_table[0xa2] = &CPU::op_and_a_r<D>;
|
||||
opcode_table[0xa3] = &CPU::op_and_a_r<E>;
|
||||
opcode_table[0xa4] = &CPU::op_and_a_r<H>;
|
||||
opcode_table[0xa5] = &CPU::op_and_a_r<L>;
|
||||
opcode_table[0xa6] = &CPU::op_and_a_hl;
|
||||
opcode_table[0xa7] = &CPU::op_and_a_r<A>;
|
||||
opcode_table[0xa8] = &CPU::op_xor_a_r<B>;
|
||||
opcode_table[0xa9] = &CPU::op_xor_a_r<C>;
|
||||
opcode_table[0xaa] = &CPU::op_xor_a_r<D>;
|
||||
opcode_table[0xab] = &CPU::op_xor_a_r<E>;
|
||||
opcode_table[0xac] = &CPU::op_xor_a_r<H>;
|
||||
opcode_table[0xad] = &CPU::op_xor_a_r<L>;
|
||||
opcode_table[0xae] = &CPU::op_xor_a_hl;
|
||||
opcode_table[0xaf] = &CPU::op_xor_a_r<A>;
|
||||
opcode_table[0xb0] = &CPU::op_or_a_r<B>;
|
||||
opcode_table[0xb1] = &CPU::op_or_a_r<C>;
|
||||
opcode_table[0xb2] = &CPU::op_or_a_r<D>;
|
||||
opcode_table[0xb3] = &CPU::op_or_a_r<E>;
|
||||
opcode_table[0xb4] = &CPU::op_or_a_r<H>;
|
||||
opcode_table[0xb5] = &CPU::op_or_a_r<L>;
|
||||
opcode_table[0xb6] = &CPU::op_or_a_hl;
|
||||
opcode_table[0xb7] = &CPU::op_or_a_r<A>;
|
||||
opcode_table[0xb8] = &CPU::op_cp_a_r<B>;
|
||||
opcode_table[0xb9] = &CPU::op_cp_a_r<C>;
|
||||
opcode_table[0xba] = &CPU::op_cp_a_r<D>;
|
||||
opcode_table[0xbb] = &CPU::op_cp_a_r<E>;
|
||||
opcode_table[0xbc] = &CPU::op_cp_a_r<H>;
|
||||
opcode_table[0xbd] = &CPU::op_cp_a_r<L>;
|
||||
opcode_table[0xbe] = &CPU::op_cp_a_hl;
|
||||
opcode_table[0xbf] = &CPU::op_cp_a_r<A>;
|
||||
opcode_table[0xc0] = &CPU::op_ret_f<ZF, 0>;
|
||||
opcode_table[0xc1] = &CPU::op_pop_rr<BC>;
|
||||
opcode_table[0xc2] = &CPU::op_jp_f_nn<ZF, 0>;
|
||||
opcode_table[0xc3] = &CPU::op_jp_nn;
|
||||
opcode_table[0xc4] = &CPU::op_call_f_nn<ZF, 0>;
|
||||
opcode_table[0xc5] = &CPU::op_push_rr<BC>;
|
||||
opcode_table[0xc6] = &CPU::op_add_a_n;
|
||||
opcode_table[0xc7] = &CPU::op_rst_n<0x00>;
|
||||
opcode_table[0xc8] = &CPU::op_ret_f<ZF, 1>;
|
||||
opcode_table[0xc9] = &CPU::op_ret;
|
||||
opcode_table[0xca] = &CPU::op_jp_f_nn<ZF, 1>;
|
||||
opcode_table[0xcb] = &CPU::op_cb;
|
||||
opcode_table[0xcc] = &CPU::op_call_f_nn<ZF, 1>;
|
||||
opcode_table[0xcd] = &CPU::op_call_nn;
|
||||
opcode_table[0xce] = &CPU::op_adc_a_n;
|
||||
opcode_table[0xcf] = &CPU::op_rst_n<0x08>;
|
||||
opcode_table[0xd0] = &CPU::op_ret_f<CF, 0>;
|
||||
opcode_table[0xd1] = &CPU::op_pop_rr<DE>;
|
||||
opcode_table[0xd2] = &CPU::op_jp_f_nn<CF, 0>;
|
||||
opcode_table[0xd3] = &CPU::op_xx;
|
||||
opcode_table[0xd4] = &CPU::op_call_f_nn<CF, 0>;
|
||||
opcode_table[0xd5] = &CPU::op_push_rr<DE>;
|
||||
opcode_table[0xd6] = &CPU::op_sub_a_n;
|
||||
opcode_table[0xd7] = &CPU::op_rst_n<0x10>;
|
||||
opcode_table[0xd8] = &CPU::op_ret_f<CF, 1>;
|
||||
opcode_table[0xd9] = &CPU::op_reti;
|
||||
opcode_table[0xda] = &CPU::op_jp_f_nn<CF, 1>;
|
||||
opcode_table[0xdb] = &CPU::op_xx;
|
||||
opcode_table[0xdc] = &CPU::op_call_f_nn<CF, 1>;
|
||||
opcode_table[0xdd] = &CPU::op_xx;
|
||||
opcode_table[0xde] = &CPU::op_sbc_a_n;
|
||||
opcode_table[0xdf] = &CPU::op_rst_n<0x18>;
|
||||
opcode_table[0xe0] = &CPU::op_ld_ffn_a;
|
||||
opcode_table[0xe1] = &CPU::op_pop_rr<HL>;
|
||||
opcode_table[0xe2] = &CPU::op_ld_ffc_a;
|
||||
opcode_table[0xe3] = &CPU::op_xx;
|
||||
opcode_table[0xe4] = &CPU::op_xx;
|
||||
opcode_table[0xe5] = &CPU::op_push_rr<HL>;
|
||||
opcode_table[0xe6] = &CPU::op_and_a_n;
|
||||
opcode_table[0xe7] = &CPU::op_rst_n<0x20>;
|
||||
opcode_table[0xe8] = &CPU::op_add_sp_n;
|
||||
opcode_table[0xe9] = &CPU::op_jp_hl;
|
||||
opcode_table[0xea] = &CPU::op_ld_nn_a;
|
||||
opcode_table[0xeb] = &CPU::op_xx;
|
||||
opcode_table[0xec] = &CPU::op_xx;
|
||||
opcode_table[0xed] = &CPU::op_xx;
|
||||
opcode_table[0xee] = &CPU::op_xor_a_n;
|
||||
opcode_table[0xef] = &CPU::op_rst_n<0x28>;
|
||||
opcode_table[0xf0] = &CPU::op_ld_a_ffn;
|
||||
opcode_table[0xf1] = &CPU::op_pop_rr<AF>;
|
||||
opcode_table[0xf2] = &CPU::op_ld_a_ffc;
|
||||
opcode_table[0xf3] = &CPU::op_di;
|
||||
opcode_table[0xf4] = &CPU::op_xx;
|
||||
opcode_table[0xf5] = &CPU::op_push_rr<AF>;
|
||||
opcode_table[0xf6] = &CPU::op_or_a_n;
|
||||
opcode_table[0xf7] = &CPU::op_rst_n<0x30>;
|
||||
opcode_table[0xf8] = &CPU::op_ld_hl_sp_n;
|
||||
opcode_table[0xf9] = &CPU::op_ld_sp_hl;
|
||||
opcode_table[0xfa] = &CPU::op_ld_a_nn;
|
||||
opcode_table[0xfb] = &CPU::op_ei;
|
||||
opcode_table[0xfc] = &CPU::op_xx;
|
||||
opcode_table[0xfd] = &CPU::op_xx;
|
||||
opcode_table[0xfe] = &CPU::op_cp_a_n;
|
||||
opcode_table[0xff] = &CPU::op_rst_n<0x38>;
|
||||
|
||||
opcode_table_cb[0x00] = &CPU::op_rlc_r<B>;
|
||||
opcode_table_cb[0x01] = &CPU::op_rlc_r<C>;
|
||||
opcode_table_cb[0x02] = &CPU::op_rlc_r<D>;
|
||||
opcode_table_cb[0x03] = &CPU::op_rlc_r<E>;
|
||||
opcode_table_cb[0x04] = &CPU::op_rlc_r<H>;
|
||||
opcode_table_cb[0x05] = &CPU::op_rlc_r<L>;
|
||||
opcode_table_cb[0x06] = &CPU::op_rlc_hl;
|
||||
opcode_table_cb[0x07] = &CPU::op_rlc_r<A>;
|
||||
opcode_table_cb[0x08] = &CPU::op_rrc_r<B>;
|
||||
opcode_table_cb[0x09] = &CPU::op_rrc_r<C>;
|
||||
opcode_table_cb[0x0a] = &CPU::op_rrc_r<D>;
|
||||
opcode_table_cb[0x0b] = &CPU::op_rrc_r<E>;
|
||||
opcode_table_cb[0x0c] = &CPU::op_rrc_r<H>;
|
||||
opcode_table_cb[0x0d] = &CPU::op_rrc_r<L>;
|
||||
opcode_table_cb[0x0e] = &CPU::op_rrc_hl;
|
||||
opcode_table_cb[0x0f] = &CPU::op_rrc_r<A>;
|
||||
opcode_table_cb[0x10] = &CPU::op_rl_r<B>;
|
||||
opcode_table_cb[0x11] = &CPU::op_rl_r<C>;
|
||||
opcode_table_cb[0x12] = &CPU::op_rl_r<D>;
|
||||
opcode_table_cb[0x13] = &CPU::op_rl_r<E>;
|
||||
opcode_table_cb[0x14] = &CPU::op_rl_r<H>;
|
||||
opcode_table_cb[0x15] = &CPU::op_rl_r<L>;
|
||||
opcode_table_cb[0x16] = &CPU::op_rl_hl;
|
||||
opcode_table_cb[0x17] = &CPU::op_rl_r<A>;
|
||||
opcode_table_cb[0x18] = &CPU::op_rr_r<B>;
|
||||
opcode_table_cb[0x19] = &CPU::op_rr_r<C>;
|
||||
opcode_table_cb[0x1a] = &CPU::op_rr_r<D>;
|
||||
opcode_table_cb[0x1b] = &CPU::op_rr_r<E>;
|
||||
opcode_table_cb[0x1c] = &CPU::op_rr_r<H>;
|
||||
opcode_table_cb[0x1d] = &CPU::op_rr_r<L>;
|
||||
opcode_table_cb[0x1e] = &CPU::op_rr_hl;
|
||||
opcode_table_cb[0x1f] = &CPU::op_rr_r<A>;
|
||||
opcode_table_cb[0x20] = &CPU::op_sla_r<B>;
|
||||
opcode_table_cb[0x21] = &CPU::op_sla_r<C>;
|
||||
opcode_table_cb[0x22] = &CPU::op_sla_r<D>;
|
||||
opcode_table_cb[0x23] = &CPU::op_sla_r<E>;
|
||||
opcode_table_cb[0x24] = &CPU::op_sla_r<H>;
|
||||
opcode_table_cb[0x25] = &CPU::op_sla_r<L>;
|
||||
opcode_table_cb[0x26] = &CPU::op_sla_hl;
|
||||
opcode_table_cb[0x27] = &CPU::op_sla_r<A>;
|
||||
opcode_table_cb[0x28] = &CPU::op_sra_r<B>;
|
||||
opcode_table_cb[0x29] = &CPU::op_sra_r<C>;
|
||||
opcode_table_cb[0x2a] = &CPU::op_sra_r<D>;
|
||||
opcode_table_cb[0x2b] = &CPU::op_sra_r<E>;
|
||||
opcode_table_cb[0x2c] = &CPU::op_sra_r<H>;
|
||||
opcode_table_cb[0x2d] = &CPU::op_sra_r<L>;
|
||||
opcode_table_cb[0x2e] = &CPU::op_sra_hl;
|
||||
opcode_table_cb[0x2f] = &CPU::op_sra_r<A>;
|
||||
opcode_table_cb[0x30] = &CPU::op_swap_r<B>;
|
||||
opcode_table_cb[0x31] = &CPU::op_swap_r<C>;
|
||||
opcode_table_cb[0x32] = &CPU::op_swap_r<D>;
|
||||
opcode_table_cb[0x33] = &CPU::op_swap_r<E>;
|
||||
opcode_table_cb[0x34] = &CPU::op_swap_r<H>;
|
||||
opcode_table_cb[0x35] = &CPU::op_swap_r<L>;
|
||||
opcode_table_cb[0x36] = &CPU::op_swap_hl;
|
||||
opcode_table_cb[0x37] = &CPU::op_swap_r<A>;
|
||||
opcode_table_cb[0x38] = &CPU::op_srl_r<B>;
|
||||
opcode_table_cb[0x39] = &CPU::op_srl_r<C>;
|
||||
opcode_table_cb[0x3a] = &CPU::op_srl_r<D>;
|
||||
opcode_table_cb[0x3b] = &CPU::op_srl_r<E>;
|
||||
opcode_table_cb[0x3c] = &CPU::op_srl_r<H>;
|
||||
opcode_table_cb[0x3d] = &CPU::op_srl_r<L>;
|
||||
opcode_table_cb[0x3e] = &CPU::op_srl_hl;
|
||||
opcode_table_cb[0x3f] = &CPU::op_srl_r<A>;
|
||||
opcode_table_cb[0x40] = &CPU::op_bit_n_r<0, B>;
|
||||
opcode_table_cb[0x41] = &CPU::op_bit_n_r<0, C>;
|
||||
opcode_table_cb[0x42] = &CPU::op_bit_n_r<0, D>;
|
||||
opcode_table_cb[0x43] = &CPU::op_bit_n_r<0, E>;
|
||||
opcode_table_cb[0x44] = &CPU::op_bit_n_r<0, H>;
|
||||
opcode_table_cb[0x45] = &CPU::op_bit_n_r<0, L>;
|
||||
opcode_table_cb[0x46] = &CPU::op_bit_n_hl<0>;
|
||||
opcode_table_cb[0x47] = &CPU::op_bit_n_r<0, A>;
|
||||
opcode_table_cb[0x48] = &CPU::op_bit_n_r<1, B>;
|
||||
opcode_table_cb[0x49] = &CPU::op_bit_n_r<1, C>;
|
||||
opcode_table_cb[0x4a] = &CPU::op_bit_n_r<1, D>;
|
||||
opcode_table_cb[0x4b] = &CPU::op_bit_n_r<1, E>;
|
||||
opcode_table_cb[0x4c] = &CPU::op_bit_n_r<1, H>;
|
||||
opcode_table_cb[0x4d] = &CPU::op_bit_n_r<1, L>;
|
||||
opcode_table_cb[0x4e] = &CPU::op_bit_n_hl<1>;
|
||||
opcode_table_cb[0x4f] = &CPU::op_bit_n_r<1, A>;
|
||||
opcode_table_cb[0x50] = &CPU::op_bit_n_r<2, B>;
|
||||
opcode_table_cb[0x51] = &CPU::op_bit_n_r<2, C>;
|
||||
opcode_table_cb[0x52] = &CPU::op_bit_n_r<2, D>;
|
||||
opcode_table_cb[0x53] = &CPU::op_bit_n_r<2, E>;
|
||||
opcode_table_cb[0x54] = &CPU::op_bit_n_r<2, H>;
|
||||
opcode_table_cb[0x55] = &CPU::op_bit_n_r<2, L>;
|
||||
opcode_table_cb[0x56] = &CPU::op_bit_n_hl<2>;
|
||||
opcode_table_cb[0x57] = &CPU::op_bit_n_r<2, A>;
|
||||
opcode_table_cb[0x58] = &CPU::op_bit_n_r<3, B>;
|
||||
opcode_table_cb[0x59] = &CPU::op_bit_n_r<3, C>;
|
||||
opcode_table_cb[0x5a] = &CPU::op_bit_n_r<3, D>;
|
||||
opcode_table_cb[0x5b] = &CPU::op_bit_n_r<3, E>;
|
||||
opcode_table_cb[0x5c] = &CPU::op_bit_n_r<3, H>;
|
||||
opcode_table_cb[0x5d] = &CPU::op_bit_n_r<3, L>;
|
||||
opcode_table_cb[0x5e] = &CPU::op_bit_n_hl<3>;
|
||||
opcode_table_cb[0x5f] = &CPU::op_bit_n_r<3, A>;
|
||||
opcode_table_cb[0x60] = &CPU::op_bit_n_r<4, B>;
|
||||
opcode_table_cb[0x61] = &CPU::op_bit_n_r<4, C>;
|
||||
opcode_table_cb[0x62] = &CPU::op_bit_n_r<4, D>;
|
||||
opcode_table_cb[0x63] = &CPU::op_bit_n_r<4, E>;
|
||||
opcode_table_cb[0x64] = &CPU::op_bit_n_r<4, H>;
|
||||
opcode_table_cb[0x65] = &CPU::op_bit_n_r<4, L>;
|
||||
opcode_table_cb[0x66] = &CPU::op_bit_n_hl<4>;
|
||||
opcode_table_cb[0x67] = &CPU::op_bit_n_r<4, A>;
|
||||
opcode_table_cb[0x68] = &CPU::op_bit_n_r<5, B>;
|
||||
opcode_table_cb[0x69] = &CPU::op_bit_n_r<5, C>;
|
||||
opcode_table_cb[0x6a] = &CPU::op_bit_n_r<5, D>;
|
||||
opcode_table_cb[0x6b] = &CPU::op_bit_n_r<5, E>;
|
||||
opcode_table_cb[0x6c] = &CPU::op_bit_n_r<5, H>;
|
||||
opcode_table_cb[0x6d] = &CPU::op_bit_n_r<5, L>;
|
||||
opcode_table_cb[0x6e] = &CPU::op_bit_n_hl<5>;
|
||||
opcode_table_cb[0x6f] = &CPU::op_bit_n_r<5, A>;
|
||||
opcode_table_cb[0x70] = &CPU::op_bit_n_r<6, B>;
|
||||
opcode_table_cb[0x71] = &CPU::op_bit_n_r<6, C>;
|
||||
opcode_table_cb[0x72] = &CPU::op_bit_n_r<6, D>;
|
||||
opcode_table_cb[0x73] = &CPU::op_bit_n_r<6, E>;
|
||||
opcode_table_cb[0x74] = &CPU::op_bit_n_r<6, H>;
|
||||
opcode_table_cb[0x75] = &CPU::op_bit_n_r<6, L>;
|
||||
opcode_table_cb[0x76] = &CPU::op_bit_n_hl<6>;
|
||||
opcode_table_cb[0x77] = &CPU::op_bit_n_r<6, A>;
|
||||
opcode_table_cb[0x78] = &CPU::op_bit_n_r<7, B>;
|
||||
opcode_table_cb[0x79] = &CPU::op_bit_n_r<7, C>;
|
||||
opcode_table_cb[0x7a] = &CPU::op_bit_n_r<7, D>;
|
||||
opcode_table_cb[0x7b] = &CPU::op_bit_n_r<7, E>;
|
||||
opcode_table_cb[0x7c] = &CPU::op_bit_n_r<7, H>;
|
||||
opcode_table_cb[0x7d] = &CPU::op_bit_n_r<7, L>;
|
||||
opcode_table_cb[0x7e] = &CPU::op_bit_n_hl<7>;
|
||||
opcode_table_cb[0x7f] = &CPU::op_bit_n_r<7, A>;
|
||||
opcode_table_cb[0x80] = &CPU::op_res_n_r<0, B>;
|
||||
opcode_table_cb[0x81] = &CPU::op_res_n_r<0, C>;
|
||||
opcode_table_cb[0x82] = &CPU::op_res_n_r<0, D>;
|
||||
opcode_table_cb[0x83] = &CPU::op_res_n_r<0, E>;
|
||||
opcode_table_cb[0x84] = &CPU::op_res_n_r<0, H>;
|
||||
opcode_table_cb[0x85] = &CPU::op_res_n_r<0, L>;
|
||||
opcode_table_cb[0x86] = &CPU::op_res_n_hl<0>;
|
||||
opcode_table_cb[0x87] = &CPU::op_res_n_r<0, A>;
|
||||
opcode_table_cb[0x88] = &CPU::op_res_n_r<1, B>;
|
||||
opcode_table_cb[0x89] = &CPU::op_res_n_r<1, C>;
|
||||
opcode_table_cb[0x8a] = &CPU::op_res_n_r<1, D>;
|
||||
opcode_table_cb[0x8b] = &CPU::op_res_n_r<1, E>;
|
||||
opcode_table_cb[0x8c] = &CPU::op_res_n_r<1, H>;
|
||||
opcode_table_cb[0x8d] = &CPU::op_res_n_r<1, L>;
|
||||
opcode_table_cb[0x8e] = &CPU::op_res_n_hl<1>;
|
||||
opcode_table_cb[0x8f] = &CPU::op_res_n_r<1, A>;
|
||||
opcode_table_cb[0x90] = &CPU::op_res_n_r<2, B>;
|
||||
opcode_table_cb[0x91] = &CPU::op_res_n_r<2, C>;
|
||||
opcode_table_cb[0x92] = &CPU::op_res_n_r<2, D>;
|
||||
opcode_table_cb[0x93] = &CPU::op_res_n_r<2, E>;
|
||||
opcode_table_cb[0x94] = &CPU::op_res_n_r<2, H>;
|
||||
opcode_table_cb[0x95] = &CPU::op_res_n_r<2, L>;
|
||||
opcode_table_cb[0x96] = &CPU::op_res_n_hl<2>;
|
||||
opcode_table_cb[0x97] = &CPU::op_res_n_r<2, A>;
|
||||
opcode_table_cb[0x98] = &CPU::op_res_n_r<3, B>;
|
||||
opcode_table_cb[0x99] = &CPU::op_res_n_r<3, C>;
|
||||
opcode_table_cb[0x9a] = &CPU::op_res_n_r<3, D>;
|
||||
opcode_table_cb[0x9b] = &CPU::op_res_n_r<3, E>;
|
||||
opcode_table_cb[0x9c] = &CPU::op_res_n_r<3, H>;
|
||||
opcode_table_cb[0x9d] = &CPU::op_res_n_r<3, L>;
|
||||
opcode_table_cb[0x9e] = &CPU::op_res_n_hl<3>;
|
||||
opcode_table_cb[0x9f] = &CPU::op_res_n_r<3, A>;
|
||||
opcode_table_cb[0xa0] = &CPU::op_res_n_r<4, B>;
|
||||
opcode_table_cb[0xa1] = &CPU::op_res_n_r<4, C>;
|
||||
opcode_table_cb[0xa2] = &CPU::op_res_n_r<4, D>;
|
||||
opcode_table_cb[0xa3] = &CPU::op_res_n_r<4, E>;
|
||||
opcode_table_cb[0xa4] = &CPU::op_res_n_r<4, H>;
|
||||
opcode_table_cb[0xa5] = &CPU::op_res_n_r<4, L>;
|
||||
opcode_table_cb[0xa6] = &CPU::op_res_n_hl<4>;
|
||||
opcode_table_cb[0xa7] = &CPU::op_res_n_r<4, A>;
|
||||
opcode_table_cb[0xa8] = &CPU::op_res_n_r<5, B>;
|
||||
opcode_table_cb[0xa9] = &CPU::op_res_n_r<5, C>;
|
||||
opcode_table_cb[0xaa] = &CPU::op_res_n_r<5, D>;
|
||||
opcode_table_cb[0xab] = &CPU::op_res_n_r<5, E>;
|
||||
opcode_table_cb[0xac] = &CPU::op_res_n_r<5, H>;
|
||||
opcode_table_cb[0xad] = &CPU::op_res_n_r<5, L>;
|
||||
opcode_table_cb[0xae] = &CPU::op_res_n_hl<5>;
|
||||
opcode_table_cb[0xaf] = &CPU::op_res_n_r<5, A>;
|
||||
opcode_table_cb[0xb0] = &CPU::op_res_n_r<6, B>;
|
||||
opcode_table_cb[0xb1] = &CPU::op_res_n_r<6, C>;
|
||||
opcode_table_cb[0xb2] = &CPU::op_res_n_r<6, D>;
|
||||
opcode_table_cb[0xb3] = &CPU::op_res_n_r<6, E>;
|
||||
opcode_table_cb[0xb4] = &CPU::op_res_n_r<6, H>;
|
||||
opcode_table_cb[0xb5] = &CPU::op_res_n_r<6, L>;
|
||||
opcode_table_cb[0xb6] = &CPU::op_res_n_hl<6>;
|
||||
opcode_table_cb[0xb7] = &CPU::op_res_n_r<6, A>;
|
||||
opcode_table_cb[0xb8] = &CPU::op_res_n_r<7, B>;
|
||||
opcode_table_cb[0xb9] = &CPU::op_res_n_r<7, C>;
|
||||
opcode_table_cb[0xba] = &CPU::op_res_n_r<7, D>;
|
||||
opcode_table_cb[0xbb] = &CPU::op_res_n_r<7, E>;
|
||||
opcode_table_cb[0xbc] = &CPU::op_res_n_r<7, H>;
|
||||
opcode_table_cb[0xbd] = &CPU::op_res_n_r<7, L>;
|
||||
opcode_table_cb[0xbe] = &CPU::op_res_n_hl<7>;
|
||||
opcode_table_cb[0xbf] = &CPU::op_res_n_r<7, A>;
|
||||
opcode_table_cb[0xc0] = &CPU::op_set_n_r<0, B>;
|
||||
opcode_table_cb[0xc1] = &CPU::op_set_n_r<0, C>;
|
||||
opcode_table_cb[0xc2] = &CPU::op_set_n_r<0, D>;
|
||||
opcode_table_cb[0xc3] = &CPU::op_set_n_r<0, E>;
|
||||
opcode_table_cb[0xc4] = &CPU::op_set_n_r<0, H>;
|
||||
opcode_table_cb[0xc5] = &CPU::op_set_n_r<0, L>;
|
||||
opcode_table_cb[0xc6] = &CPU::op_set_n_hl<0>;
|
||||
opcode_table_cb[0xc7] = &CPU::op_set_n_r<0, A>;
|
||||
opcode_table_cb[0xc8] = &CPU::op_set_n_r<1, B>;
|
||||
opcode_table_cb[0xc9] = &CPU::op_set_n_r<1, C>;
|
||||
opcode_table_cb[0xca] = &CPU::op_set_n_r<1, D>;
|
||||
opcode_table_cb[0xcb] = &CPU::op_set_n_r<1, E>;
|
||||
opcode_table_cb[0xcc] = &CPU::op_set_n_r<1, H>;
|
||||
opcode_table_cb[0xcd] = &CPU::op_set_n_r<1, L>;
|
||||
opcode_table_cb[0xce] = &CPU::op_set_n_hl<1>;
|
||||
opcode_table_cb[0xcf] = &CPU::op_set_n_r<1, A>;
|
||||
opcode_table_cb[0xd0] = &CPU::op_set_n_r<2, B>;
|
||||
opcode_table_cb[0xd1] = &CPU::op_set_n_r<2, C>;
|
||||
opcode_table_cb[0xd2] = &CPU::op_set_n_r<2, D>;
|
||||
opcode_table_cb[0xd3] = &CPU::op_set_n_r<2, E>;
|
||||
opcode_table_cb[0xd4] = &CPU::op_set_n_r<2, H>;
|
||||
opcode_table_cb[0xd5] = &CPU::op_set_n_r<2, L>;
|
||||
opcode_table_cb[0xd6] = &CPU::op_set_n_hl<2>;
|
||||
opcode_table_cb[0xd7] = &CPU::op_set_n_r<2, A>;
|
||||
opcode_table_cb[0xd8] = &CPU::op_set_n_r<3, B>;
|
||||
opcode_table_cb[0xd9] = &CPU::op_set_n_r<3, C>;
|
||||
opcode_table_cb[0xda] = &CPU::op_set_n_r<3, D>;
|
||||
opcode_table_cb[0xdb] = &CPU::op_set_n_r<3, E>;
|
||||
opcode_table_cb[0xdc] = &CPU::op_set_n_r<3, H>;
|
||||
opcode_table_cb[0xdd] = &CPU::op_set_n_r<3, L>;
|
||||
opcode_table_cb[0xde] = &CPU::op_set_n_hl<3>;
|
||||
opcode_table_cb[0xdf] = &CPU::op_set_n_r<3, A>;
|
||||
opcode_table_cb[0xe0] = &CPU::op_set_n_r<4, B>;
|
||||
opcode_table_cb[0xe1] = &CPU::op_set_n_r<4, C>;
|
||||
opcode_table_cb[0xe2] = &CPU::op_set_n_r<4, D>;
|
||||
opcode_table_cb[0xe3] = &CPU::op_set_n_r<4, E>;
|
||||
opcode_table_cb[0xe4] = &CPU::op_set_n_r<4, H>;
|
||||
opcode_table_cb[0xe5] = &CPU::op_set_n_r<4, L>;
|
||||
opcode_table_cb[0xe6] = &CPU::op_set_n_hl<4>;
|
||||
opcode_table_cb[0xe7] = &CPU::op_set_n_r<4, A>;
|
||||
opcode_table_cb[0xe8] = &CPU::op_set_n_r<5, B>;
|
||||
opcode_table_cb[0xe9] = &CPU::op_set_n_r<5, C>;
|
||||
opcode_table_cb[0xea] = &CPU::op_set_n_r<5, D>;
|
||||
opcode_table_cb[0xeb] = &CPU::op_set_n_r<5, E>;
|
||||
opcode_table_cb[0xec] = &CPU::op_set_n_r<5, H>;
|
||||
opcode_table_cb[0xed] = &CPU::op_set_n_r<5, L>;
|
||||
opcode_table_cb[0xee] = &CPU::op_set_n_hl<5>;
|
||||
opcode_table_cb[0xef] = &CPU::op_set_n_r<5, A>;
|
||||
opcode_table_cb[0xf0] = &CPU::op_set_n_r<6, B>;
|
||||
opcode_table_cb[0xf1] = &CPU::op_set_n_r<6, C>;
|
||||
opcode_table_cb[0xf2] = &CPU::op_set_n_r<6, D>;
|
||||
opcode_table_cb[0xf3] = &CPU::op_set_n_r<6, E>;
|
||||
opcode_table_cb[0xf4] = &CPU::op_set_n_r<6, H>;
|
||||
opcode_table_cb[0xf5] = &CPU::op_set_n_r<6, L>;
|
||||
opcode_table_cb[0xf6] = &CPU::op_set_n_hl<6>;
|
||||
opcode_table_cb[0xf7] = &CPU::op_set_n_r<6, A>;
|
||||
opcode_table_cb[0xf8] = &CPU::op_set_n_r<7, B>;
|
||||
opcode_table_cb[0xf9] = &CPU::op_set_n_r<7, C>;
|
||||
opcode_table_cb[0xfa] = &CPU::op_set_n_r<7, D>;
|
||||
opcode_table_cb[0xfb] = &CPU::op_set_n_r<7, E>;
|
||||
opcode_table_cb[0xfc] = &CPU::op_set_n_r<7, H>;
|
||||
opcode_table_cb[0xfd] = &CPU::op_set_n_r<7, L>;
|
||||
opcode_table_cb[0xfe] = &CPU::op_set_n_hl<7>;
|
||||
opcode_table_cb[0xff] = &CPU::op_set_n_r<7, A>;
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,202 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define CPU_CPP
|
||||
namespace GB {
|
||||
|
||||
#include "core/core.cpp"
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "timing/timing.cpp"
|
||||
#include "serialization.cpp"
|
||||
CPU cpu;
|
||||
|
||||
void CPU::Main() {
|
||||
cpu.main();
|
||||
}
|
||||
|
||||
void CPU::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(trace) print(disassemble(r[PC]), "\n");
|
||||
interrupt_test();
|
||||
uint8 opcode = op_read(r[PC]++);
|
||||
(this->*opcode_table[opcode])();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::interrupt_raise(CPU::Interrupt id) {
|
||||
if(id == Interrupt::Vblank) {
|
||||
status.interrupt_request_vblank = 1;
|
||||
if(status.interrupt_enable_vblank) status.halt = false;
|
||||
}
|
||||
|
||||
if(id == Interrupt::Stat) {
|
||||
status.interrupt_request_stat = 1;
|
||||
if(status.interrupt_enable_stat) status.halt = false;
|
||||
}
|
||||
|
||||
if(id == Interrupt::Timer) {
|
||||
status.interrupt_request_timer = 1;
|
||||
if(status.interrupt_enable_timer) status.halt = false;
|
||||
}
|
||||
|
||||
if(id == Interrupt::Serial) {
|
||||
status.interrupt_request_serial = 1;
|
||||
if(status.interrupt_enable_serial) status.halt = false;
|
||||
}
|
||||
|
||||
if(id == Interrupt::Joypad) {
|
||||
status.interrupt_request_joypad = 1;
|
||||
if(status.interrupt_enable_joypad) status.halt = status.stop = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::interrupt_test() {
|
||||
if(status.ime) {
|
||||
if(status.interrupt_request_vblank && status.interrupt_enable_vblank) {
|
||||
status.interrupt_request_vblank = 0;
|
||||
return interrupt_exec(0x0040);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_stat && status.interrupt_enable_stat) {
|
||||
status.interrupt_request_stat = 0;
|
||||
return interrupt_exec(0x0048);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_timer && status.interrupt_enable_timer) {
|
||||
status.interrupt_request_timer = 0;
|
||||
return interrupt_exec(0x0050);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_serial && status.interrupt_enable_serial) {
|
||||
status.interrupt_request_serial = 0;
|
||||
return interrupt_exec(0x0058);
|
||||
}
|
||||
|
||||
if(status.interrupt_request_joypad && status.interrupt_enable_joypad) {
|
||||
status.interrupt_request_joypad = 0;
|
||||
return interrupt_exec(0x0060);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::interrupt_exec(uint16 pc) {
|
||||
status.ime = 0;
|
||||
op_write(--r[SP], r[PC] >> 8);
|
||||
op_write(--r[SP], r[PC] >> 0);
|
||||
r[PC] = pc;
|
||||
op_io();
|
||||
op_io();
|
||||
op_io();
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
create(Main, 4 * 1024 * 1024);
|
||||
|
||||
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 = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
|
||||
|
||||
bus.mmio[0xff00] = this; //JOYP
|
||||
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[SP] = 0x0000;
|
||||
r[AF] = 0x0000;
|
||||
r[BC] = 0x0000;
|
||||
r[DE] = 0x0000;
|
||||
r[HL] = 0x0000;
|
||||
|
||||
status.clock = 0;
|
||||
status.halt = false;
|
||||
status.stop = false;
|
||||
status.ei = false;
|
||||
status.ime = 0;
|
||||
|
||||
status.p15 = 0;
|
||||
status.p14 = 0;
|
||||
status.joyp = 0;
|
||||
status.mlt_req = 0;
|
||||
|
||||
status.serial_data = 0;
|
||||
status.serial_bits = 0;
|
||||
|
||||
status.serial_transfer = 0;
|
||||
status.serial_clock = 0;
|
||||
|
||||
status.div = 0;
|
||||
|
||||
status.tima = 0;
|
||||
|
||||
status.tma = 0;
|
||||
|
||||
status.timer_enable = 0;
|
||||
status.timer_clock = 0;
|
||||
|
||||
status.interrupt_request_joypad = 0;
|
||||
status.interrupt_request_serial = 0;
|
||||
status.interrupt_request_timer = 0;
|
||||
status.interrupt_request_stat = 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_serial = 0;
|
||||
status.interrupt_enable_timer = 0;
|
||||
status.interrupt_enable_stat = 0;
|
||||
status.interrupt_enable_vblank = 0;
|
||||
}
|
||||
|
||||
CPU::CPU() : trace(false) {
|
||||
initialize_opcode_table();
|
||||
}
|
||||
|
||||
}
|
@@ -1,105 +0,0 @@
|
||||
struct CPU : Thread, MMIO {
|
||||
#include "core/core.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "timing/timing.hpp"
|
||||
|
||||
bool trace;
|
||||
|
||||
enum class Interrupt : unsigned {
|
||||
Vblank,
|
||||
Stat,
|
||||
Timer,
|
||||
Serial,
|
||||
Joypad,
|
||||
};
|
||||
|
||||
struct Status {
|
||||
unsigned clock;
|
||||
bool halt;
|
||||
bool stop;
|
||||
bool ei;
|
||||
bool ime;
|
||||
|
||||
//$ff00 JOYP
|
||||
bool p15;
|
||||
bool p14;
|
||||
uint8 joyp;
|
||||
uint8 mlt_req;
|
||||
|
||||
//$ff01 SB
|
||||
uint8 serial_data;
|
||||
unsigned serial_bits;
|
||||
|
||||
//$ff02 SC
|
||||
bool serial_transfer;
|
||||
bool serial_clock;
|
||||
|
||||
//$ff04 DIV
|
||||
uint8 div;
|
||||
|
||||
//$ff05 TIMA
|
||||
uint8 tima;
|
||||
|
||||
//$ff06 TMA
|
||||
uint8 tma;
|
||||
|
||||
//$ff07 TAC
|
||||
bool timer_enable;
|
||||
unsigned timer_clock;
|
||||
|
||||
//$ff0f IF
|
||||
bool interrupt_request_joypad;
|
||||
bool interrupt_request_serial;
|
||||
bool interrupt_request_timer;
|
||||
bool interrupt_request_stat;
|
||||
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
|
||||
bool interrupt_enable_joypad;
|
||||
bool interrupt_enable_serial;
|
||||
bool interrupt_enable_timer;
|
||||
bool interrupt_enable_stat;
|
||||
bool interrupt_enable_vblank;
|
||||
} status;
|
||||
|
||||
uint8 wram[32768]; //GB=8192, GBC=32768
|
||||
uint8 hram[128];
|
||||
|
||||
static void Main();
|
||||
void main();
|
||||
void interrupt_raise(Interrupt id);
|
||||
void interrupt_test();
|
||||
void interrupt_exec(uint16 pc);
|
||||
void power();
|
||||
|
||||
void serialize(serializer&);
|
||||
CPU();
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
@@ -1,271 +0,0 @@
|
||||
#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,4 +0,0 @@
|
||||
unsigned wram_addr(uint16 addr) const;
|
||||
void mmio_joyp_poll();
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
@@ -1,76 +0,0 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
void CPU::serialize(serializer &s) {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(wram);
|
||||
s.array(hram);
|
||||
|
||||
s.integer(r.a.data);
|
||||
s.integer(r.f.z);
|
||||
s.integer(r.f.n);
|
||||
s.integer(r.f.h);
|
||||
s.integer(r.f.c);
|
||||
s.integer(r.b.data);
|
||||
s.integer(r.c.data);
|
||||
s.integer(r.d.data);
|
||||
s.integer(r.e.data);
|
||||
s.integer(r.h.data);
|
||||
s.integer(r.l.data);
|
||||
s.integer(r.sp.data);
|
||||
s.integer(r.pc.data);
|
||||
|
||||
s.integer(status.clock);
|
||||
s.integer(status.halt);
|
||||
s.integer(status.stop);
|
||||
s.integer(status.ei);
|
||||
s.integer(status.ime);
|
||||
|
||||
s.integer(status.p15);
|
||||
s.integer(status.p14);
|
||||
s.integer(status.joyp);
|
||||
s.integer(status.mlt_req);
|
||||
|
||||
s.integer(status.serial_data);
|
||||
s.integer(status.serial_bits);
|
||||
|
||||
s.integer(status.serial_transfer);
|
||||
s.integer(status.serial_clock);
|
||||
|
||||
s.integer(status.div);
|
||||
s.integer(status.tima);
|
||||
s.integer(status.tma);
|
||||
s.integer(status.timer_enable);
|
||||
s.integer(status.timer_clock);
|
||||
|
||||
s.integer(status.interrupt_request_joypad);
|
||||
s.integer(status.interrupt_request_serial);
|
||||
s.integer(status.interrupt_request_timer);
|
||||
s.integer(status.interrupt_request_stat);
|
||||
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_serial);
|
||||
s.integer(status.interrupt_enable_timer);
|
||||
s.integer(status.interrupt_enable_stat);
|
||||
s.integer(status.interrupt_enable_vblank);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,28 +0,0 @@
|
||||
#ifdef CPU_CPP
|
||||
|
||||
void CPU::op_io() {
|
||||
cycle_edge();
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
uint8 CPU::op_read(uint16 addr) {
|
||||
cycle_edge();
|
||||
uint8 r = bus.read(addr);
|
||||
add_clocks(4);
|
||||
return r;
|
||||
}
|
||||
|
||||
void CPU::op_write(uint16 addr, uint8 data) {
|
||||
cycle_edge();
|
||||
bus.write(addr, data);
|
||||
add_clocks(4);
|
||||
}
|
||||
|
||||
void CPU::cycle_edge() {
|
||||
if(status.ei) {
|
||||
status.ei = false;
|
||||
status.ime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,90 +0,0 @@
|
||||
//70224 clocks/frame
|
||||
// 456 clocks/scanline
|
||||
// 154 scanlines/frame
|
||||
|
||||
#ifdef CPU_CPP
|
||||
|
||||
#include "opcode.cpp"
|
||||
|
||||
void CPU::add_clocks(unsigned clocks) {
|
||||
system.clocks_executed += clocks;
|
||||
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
|
||||
|
||||
status.clock += clocks;
|
||||
if(status.clock >= 4 * 1024 * 1024) {
|
||||
status.clock -= 4 * 1024 * 1024;
|
||||
cartridge.mbc3.second();
|
||||
}
|
||||
|
||||
//4MHz / N(hz) - 1 = mask
|
||||
if((status.clock & 15) == 0) timer_262144hz();
|
||||
if((status.clock & 63) == 0) timer_65536hz();
|
||||
if((status.clock & 255) == 0) timer_16384hz();
|
||||
if((status.clock & 511) == 0) timer_8192hz();
|
||||
if((status.clock & 1023) == 0) timer_4096hz();
|
||||
|
||||
lcd.clock -= clocks * lcd.frequency;
|
||||
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
|
||||
|
||||
apu.clock -= clocks * apu.frequency;
|
||||
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
|
||||
}
|
||||
|
||||
void CPU::timer_262144hz() {
|
||||
if(status.timer_enable && status.timer_clock == 1) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
interrupt_raise(Interrupt::Timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::timer_65536hz() {
|
||||
if(status.timer_enable && status.timer_clock == 2) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
interrupt_raise(Interrupt::Timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::timer_16384hz() {
|
||||
if(status.timer_enable && status.timer_clock == 3) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
interrupt_raise(Interrupt::Timer);
|
||||
}
|
||||
}
|
||||
|
||||
status.div++;
|
||||
}
|
||||
|
||||
void CPU::timer_8192hz() {
|
||||
if(status.serial_transfer && status.serial_clock) {
|
||||
if(--status.serial_bits == 0) {
|
||||
status.serial_transfer = 0;
|
||||
interrupt_raise(Interrupt::Serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::timer_4096hz() {
|
||||
if(status.timer_enable && status.timer_clock == 0) {
|
||||
if(++status.tima == 0) {
|
||||
status.tima = status.tma;
|
||||
interrupt_raise(Interrupt::Timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
@@ -1,13 +0,0 @@
|
||||
void add_clocks(unsigned clocks);
|
||||
void timer_262144hz();
|
||||
void timer_65536hz();
|
||||
void timer_16384hz();
|
||||
void timer_8192hz();
|
||||
void timer_4096hz();
|
||||
void hblank();
|
||||
|
||||
//opcode.cpp
|
||||
void op_io();
|
||||
uint8 op_read(uint16 addr);
|
||||
void op_write(uint16 addr, uint8 data);
|
||||
void cycle_edge();
|
@@ -1,59 +0,0 @@
|
||||
#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
|
@@ -1,27 +0,0 @@
|
||||
#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");
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
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;
|
@@ -1,185 +0,0 @@
|
||||
#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
|
@@ -1,145 +0,0 @@
|
||||
#ifdef LCD_CPP
|
||||
|
||||
void LCD::dmg_render() {
|
||||
for(unsigned n = 0; n < 160; n++) {
|
||||
line[n] = 0x00;
|
||||
origin[n] = Origin::None;
|
||||
}
|
||||
|
||||
if(status.display_enable) {
|
||||
if(status.bg_enable) dmg_render_bg();
|
||||
if(status.window_display_enable) dmg_render_window();
|
||||
if(status.ob_enable) dmg_render_ob();
|
||||
}
|
||||
|
||||
uint16 *output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
uint16 LCD::dmg_read_tile(bool select, unsigned x, unsigned y) {
|
||||
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
|
||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
if(status.bg_tiledata_select == 0) {
|
||||
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
|
||||
} else {
|
||||
tdaddr = 0x0000 + (vram[tmaddr] << 4);
|
||||
}
|
||||
tdaddr += (y & 7) << 1;
|
||||
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
|
||||
}
|
||||
|
||||
void LCD::dmg_render_bg() {
|
||||
unsigned iy = (status.ly + status.scy) & 255;
|
||||
unsigned ix = status.scx, tx = ix & 7;
|
||||
unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
line[ox] = bgp[palette];
|
||||
origin[ox] = Origin::BG;
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
||||
if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||
}
|
||||
}
|
||||
|
||||
void LCD::dmg_render_window() {
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned iy = status.wyc++;
|
||||
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||
unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(ox - (status.wx - 7) < 160u) {
|
||||
line[ox] = bgp[palette];
|
||||
origin[ox] = Origin::BG;
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
||||
if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||
}
|
||||
}
|
||||
|
||||
//Attributes:
|
||||
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x10: palette#
|
||||
void LCD::dmg_render_ob() {
|
||||
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
|
||||
unsigned sprite[10], sprites = 0;
|
||||
|
||||
//find first ten sprites on this scanline
|
||||
for(unsigned s = 0; s < 40; s++) {
|
||||
unsigned sy = oam[(s << 2) + 0] - 16;
|
||||
unsigned sx = oam[(s << 2) + 1] - 8;
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
|
||||
sprite[sprites++] = s;
|
||||
if(sprites == 10) break;
|
||||
}
|
||||
|
||||
//sort by X-coordinate, when equal, lower address comes first
|
||||
for(unsigned x = 0; x < sprites; x++) {
|
||||
for(unsigned y = x + 1; y < sprites; y++) {
|
||||
signed sx = oam[(sprite[x] << 2) + 1] - 8;
|
||||
signed sy = oam[(sprite[y] << 2) + 1] - 8;
|
||||
if(sy < sx) {
|
||||
sprite[x] ^= sprite[y];
|
||||
sprite[y] ^= sprite[x];
|
||||
sprite[x] ^= sprite[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//render backwards, so that first sprite has highest priority
|
||||
for(signed s = sprites - 1; s >= 0; s--) {
|
||||
unsigned n = sprite[s] << 2;
|
||||
unsigned sy = oam[n + 0] - 16;
|
||||
unsigned sx = oam[n + 1] - 8;
|
||||
unsigned tile = oam[n + 2] & ~status.ob_size;
|
||||
unsigned attr = oam[n + 3];
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
if(attr & 0x40) sy ^= (Height - 1);
|
||||
|
||||
unsigned tdaddr = (tile << 4) + (sy << 1), data = 0;
|
||||
data |= vram[tdaddr++] << 0;
|
||||
data |= vram[tdaddr++] << 8;
|
||||
if(attr & 0x20) data = hflip(data);
|
||||
|
||||
for(unsigned tx = 0; tx < 8; tx++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(palette == 0) continue;
|
||||
|
||||
palette = obp[(bool)(attr & 0x10)][palette];
|
||||
unsigned ox = sx + tx;
|
||||
|
||||
if(ox < 160) {
|
||||
if(attr & 0x80) {
|
||||
if(origin[ox] == Origin::BG) {
|
||||
if(line[ox] > 0) continue;
|
||||
}
|
||||
}
|
||||
line[ox] = palette;
|
||||
origin[ox] = Origin::OB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,158 +0,0 @@
|
||||
#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() {
|
||||
}
|
||||
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
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,203 +0,0 @@
|
||||
#ifdef LCD_CPP
|
||||
|
||||
unsigned LCD::vram_addr(uint16 addr) const {
|
||||
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
|
||||
}
|
||||
|
||||
uint8 LCD::mmio_read(uint16 addr) {
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)];
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
|
||||
|
||||
if(addr == 0xff40) { //LCDC
|
||||
return (status.display_enable << 7)
|
||||
| (status.window_tilemap_select << 6)
|
||||
| (status.window_display_enable << 5)
|
||||
| (status.bg_tiledata_select << 4)
|
||||
| (status.bg_tilemap_select << 3)
|
||||
| (status.ob_size << 2)
|
||||
| (status.ob_enable << 1)
|
||||
| (status.bg_enable << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
unsigned mode;
|
||||
if(status.ly >= 144) mode = 1; //Vblank
|
||||
else if(status.lx < 80) mode = 2; //OAM
|
||||
else if(status.lx < 252) mode = 3; //LCD
|
||||
else mode = 0; //Hblank
|
||||
|
||||
return (status.interrupt_lyc << 6)
|
||||
| (status.interrupt_oam << 5)
|
||||
| (status.interrupt_vblank << 4)
|
||||
| (status.interrupt_hblank << 3)
|
||||
| ((status.ly == status.lyc) << 2)
|
||||
| (mode << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff42) { //SCY
|
||||
return status.scy;
|
||||
}
|
||||
|
||||
if(addr == 0xff43) { //SCX
|
||||
return status.scx;
|
||||
}
|
||||
|
||||
if(addr == 0xff44) { //LY
|
||||
return status.ly;
|
||||
}
|
||||
|
||||
if(addr == 0xff45) { //LYC
|
||||
return status.lyc;
|
||||
}
|
||||
|
||||
if(addr == 0xff47) { //BGP
|
||||
return (bgp[3] << 6)
|
||||
| (bgp[2] << 4)
|
||||
| (bgp[1] << 2)
|
||||
| (bgp[0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff48) { //OBP0
|
||||
return (obp[0][3] << 6)
|
||||
| (obp[0][2] << 4)
|
||||
| (obp[0][1] << 2)
|
||||
| (obp[0][0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff49) { //OBP1
|
||||
return (obp[1][3] << 6)
|
||||
| (obp[1][2] << 4)
|
||||
| (obp[1][1] << 2)
|
||||
| (obp[1][0] << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff4a) { //WY
|
||||
return status.wy;
|
||||
}
|
||||
|
||||
if(addr == 0xff4b) { //WX
|
||||
return status.wx;
|
||||
}
|
||||
|
||||
if(addr == 0xff69) { //BGPD
|
||||
return bgpd[status.bgpi];
|
||||
}
|
||||
|
||||
if(addr == 0xff6b) { //OBPD
|
||||
return obpd[status.obpi];
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void LCD::mmio_write(uint16 addr, uint8 data) {
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; }
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
|
||||
|
||||
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.window_tilemap_select = data & 0x40;
|
||||
status.window_display_enable = data & 0x20;
|
||||
status.bg_tiledata_select = data & 0x10;
|
||||
status.bg_tilemap_select = data & 0x08;
|
||||
status.ob_size = data & 0x04;
|
||||
status.ob_enable = data & 0x02;
|
||||
status.bg_enable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
status.interrupt_lyc = data & 0x40;
|
||||
status.interrupt_oam = data & 0x20;
|
||||
status.interrupt_vblank = data & 0x10;
|
||||
status.interrupt_hblank = data & 0x08;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff42) { //SCY
|
||||
status.scy = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff43) { //SCX
|
||||
status.scx = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff44) { //LY
|
||||
status.ly = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff45) { //LYC
|
||||
status.lyc = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff47) { //BGP
|
||||
bgp[3] = (data >> 6) & 3;
|
||||
bgp[2] = (data >> 4) & 3;
|
||||
bgp[1] = (data >> 2) & 3;
|
||||
bgp[0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff48) { //OBP0
|
||||
obp[0][3] = (data >> 6) & 3;
|
||||
obp[0][2] = (data >> 4) & 3;
|
||||
obp[0][1] = (data >> 2) & 3;
|
||||
obp[0][0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff49) { //OBP1
|
||||
obp[1][3] = (data >> 6) & 3;
|
||||
obp[1][2] = (data >> 4) & 3;
|
||||
obp[1][1] = (data >> 2) & 3;
|
||||
obp[1][0] = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff4a) { //WY
|
||||
status.wy = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff4b) { //WX
|
||||
status.wx = data;
|
||||
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
|
@@ -1,3 +0,0 @@
|
||||
unsigned vram_addr(uint16 addr) const;
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
@@ -1,53 +0,0 @@
|
||||
#ifdef LCD_CPP
|
||||
|
||||
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.wyc);
|
||||
|
||||
s.integer(status.display_enable);
|
||||
s.integer(status.window_tilemap_select);
|
||||
s.integer(status.window_display_enable);
|
||||
s.integer(status.bg_tiledata_select);
|
||||
s.integer(status.bg_tilemap_select);
|
||||
s.integer(status.ob_size);
|
||||
s.integer(status.ob_enable);
|
||||
s.integer(status.bg_enable);
|
||||
|
||||
s.integer(status.interrupt_lyc);
|
||||
s.integer(status.interrupt_oam);
|
||||
s.integer(status.interrupt_vblank);
|
||||
s.integer(status.interrupt_hblank);
|
||||
|
||||
s.integer(status.scy);
|
||||
s.integer(status.scx);
|
||||
|
||||
s.integer(status.ly);
|
||||
s.integer(status.lyc);
|
||||
|
||||
s.integer(status.wy);
|
||||
s.integer(status.wx);
|
||||
|
||||
s.integer(status.vram_bank);
|
||||
|
||||
s.integer(status.bgpi_increment);
|
||||
s.integer(status.bgpi);
|
||||
|
||||
s.integer(status.obpi_increment);
|
||||
s.integer(status.obpi);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,69 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define MEMORY_CPP
|
||||
namespace GB {
|
||||
|
||||
Unmapped unmapped;
|
||||
Bus bus;
|
||||
|
||||
uint8_t& Memory::operator[](unsigned addr) {
|
||||
return data[addr];
|
||||
}
|
||||
|
||||
void Memory::allocate(unsigned size_) {
|
||||
free();
|
||||
size = size_;
|
||||
data = new uint8_t[size]();
|
||||
}
|
||||
|
||||
void Memory::copy(const uint8_t *data_, unsigned size_) {
|
||||
free();
|
||||
size = size_;
|
||||
data = new uint8_t[size];
|
||||
memcpy(data, data_, size);
|
||||
}
|
||||
|
||||
void Memory::free() {
|
||||
if(data) {
|
||||
delete[] data;
|
||||
data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Memory::Memory() {
|
||||
data = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
Memory::~Memory() {
|
||||
free();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
uint8 Bus::read(uint16 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) {
|
||||
mmio[addr]->mmio_write(addr, data);
|
||||
}
|
||||
|
||||
void Bus::power() {
|
||||
for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped;
|
||||
}
|
||||
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
struct Memory {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
uint8_t& operator[](unsigned addr);
|
||||
void allocate(unsigned size);
|
||||
void copy(const uint8_t *data, unsigned size);
|
||||
void free();
|
||||
Memory();
|
||||
~Memory();
|
||||
};
|
||||
|
||||
struct MMIO {
|
||||
virtual uint8 mmio_read(uint16 addr) = 0;
|
||||
virtual void mmio_write(uint16 addr, uint8 data) = 0;
|
||||
};
|
||||
|
||||
struct Unmapped : MMIO {
|
||||
uint8 mmio_read(uint16) { return 0x00; }
|
||||
void mmio_write(uint16, uint8) {}
|
||||
};
|
||||
|
||||
struct Bus {
|
||||
MMIO *mmio[65536];
|
||||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 data);
|
||||
|
||||
void power();
|
||||
};
|
||||
|
||||
extern Unmapped unmapped;
|
||||
extern Bus bus;
|
@@ -1,30 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define SCHEDULER_CPP
|
||||
namespace GB {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
void Scheduler::enter() {
|
||||
host_thread = co_active();
|
||||
co_switch(active_thread);
|
||||
}
|
||||
|
||||
void Scheduler::exit(ExitReason reason) {
|
||||
exit_reason = reason;
|
||||
active_thread = co_active();
|
||||
co_switch(host_thread);
|
||||
}
|
||||
|
||||
void Scheduler::init() {
|
||||
host_thread = co_active();
|
||||
active_thread = cpu.thread;
|
||||
}
|
||||
|
||||
Scheduler::Scheduler() {
|
||||
exit_reason = ExitReason::UnknownEvent;
|
||||
host_thread = 0;
|
||||
active_thread = 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
struct Scheduler : property<Scheduler> {
|
||||
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
||||
enum class ExitReason : unsigned { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent };
|
||||
readonly<ExitReason> exit_reason;
|
||||
|
||||
cothread_t host_thread;
|
||||
cothread_t active_thread;
|
||||
|
||||
void enter();
|
||||
void exit(ExitReason);
|
||||
|
||||
void init();
|
||||
Scheduler();
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -1,64 +0,0 @@
|
||||
#ifdef SYSTEM_CPP
|
||||
|
||||
serializer System::serialize() {
|
||||
serializer s(serialize_size);
|
||||
|
||||
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0;
|
||||
char description[512];
|
||||
memset(&description, 0, sizeof description);
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
s.array(description);
|
||||
|
||||
serialize_all(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool System::unserialize(serializer &s) {
|
||||
unsigned signature, version, crc32;
|
||||
char description[512];
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
s.array(description);
|
||||
|
||||
if(signature != 0x31545342) return false;
|
||||
if(version != Info::SerializerVersion) return false;
|
||||
//if(crc32 != 0) return false;
|
||||
|
||||
power();
|
||||
serialize_all(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void System::serialize(serializer &s) {
|
||||
s.integer(clocks_executed);
|
||||
}
|
||||
|
||||
void System::serialize_all(serializer &s) {
|
||||
cartridge.serialize(s);
|
||||
system.serialize(s);
|
||||
cpu.serialize(s);
|
||||
apu.serialize(s);
|
||||
lcd.serialize(s);
|
||||
}
|
||||
|
||||
void System::serialize_init() {
|
||||
serializer s;
|
||||
|
||||
unsigned signature = 0, version = 0, crc32 = 0;
|
||||
char description[512];
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
s.array(description);
|
||||
|
||||
serialize_all(s);
|
||||
serialize_size = s.size();
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,72 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
#define SYSTEM_CPP
|
||||
namespace GB {
|
||||
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
|
||||
void System::run() {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
void System::runtosave() {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active_thread = lcd.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.active_thread = apu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
void System::runthreadtosave() {
|
||||
while(true) {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::init() {
|
||||
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(Revision revision) {
|
||||
this->revision = revision;
|
||||
serialize_init();
|
||||
}
|
||||
|
||||
void System::power() {
|
||||
bus.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
apu.power();
|
||||
lcd.power();
|
||||
scheduler.init();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
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;
|
@@ -1,82 +0,0 @@
|
||||
#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 },
|
||||
};
|
||||
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
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;
|
@@ -1,14 +0,0 @@
|
||||
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)
|
@@ -1,98 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
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;
|
@@ -1,28 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,209 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
@@ -1,93 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
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();
|
||||
}
|
@@ -1,158 +0,0 @@
|
||||
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];
|
@@ -1,107 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,130 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GBA {
|
||||
|
||||
#include "eeprom.cpp"
|
||||
#include "flashrom.cpp"
|
||||
#include "serialization.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
||||
information.markup = markup;
|
||||
XML::Document document(markup);
|
||||
|
||||
for(unsigned addr = 0; addr < rom.size; addr++) {
|
||||
rom.data[addr] = data[Bus::mirror(addr, size)];
|
||||
}
|
||||
|
||||
has_sram = false;
|
||||
has_eeprom = false;
|
||||
has_flashrom = false;
|
||||
|
||||
if(document["cartridge"]["ram"].exists()) {
|
||||
auto &info = document["cartridge"]["ram"];
|
||||
|
||||
if(info["type"].data == "SRAM" || info["type"].data == "FRAM") {
|
||||
has_sram = true;
|
||||
ram.size = numeral(info["size"].data);
|
||||
ram.mask = ram.size - 1;
|
||||
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
|
||||
}
|
||||
|
||||
if(info["type"].data == "EEPROM") {
|
||||
has_eeprom = true;
|
||||
eeprom.size = numeral(info["size"].data);
|
||||
eeprom.bits = eeprom.size <= 512 ? 6 : 14;
|
||||
if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size
|
||||
eeprom.mask = size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
|
||||
eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
|
||||
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
|
||||
}
|
||||
|
||||
if(info["type"].data == "FlashROM") {
|
||||
has_flashrom = true;
|
||||
flashrom.id = numeral(info["id"].data);
|
||||
flashrom.size = numeral(info["size"].data);
|
||||
for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
sha256 = nall::sha256(rom.data, rom.size);
|
||||
|
||||
system.load();
|
||||
return loaded = true;
|
||||
}
|
||||
|
||||
void Cartridge::unload() {
|
||||
if(loaded == false) return;
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
void Cartridge::power() {
|
||||
eeprom.power();
|
||||
flashrom.power();
|
||||
}
|
||||
|
||||
uint8* Cartridge::ram_data() {
|
||||
if(has_sram) return ram.data;
|
||||
if(has_eeprom) return eeprom.data;
|
||||
if(has_flashrom) return flashrom.data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned Cartridge::ram_size() {
|
||||
if(has_sram) return ram.size;
|
||||
if(has_eeprom) return eeprom.size;
|
||||
if(has_flashrom) return flashrom.size;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint32 Cartridge::read(uint8 *data, uint32 addr, uint32 size) {
|
||||
if(size == Word) addr &= ~3;
|
||||
if(size == Half) addr &= ~1;
|
||||
data += addr;
|
||||
if(size == Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
|
||||
if(size == Half) return data[0] << 0 | data[1] << 8;
|
||||
return data[0];
|
||||
}
|
||||
|
||||
void Cartridge::write(uint8 *data, uint32 addr, uint32 size, uint32 word) {
|
||||
if(size == Word) addr &= ~3;
|
||||
if(size == Half) addr &= ~1;
|
||||
data += addr;
|
||||
switch(size) {
|
||||
case Word: data[3] = word >> 24;
|
||||
data[2] = word >> 16;
|
||||
case Half: data[1] = word >> 8;
|
||||
case Byte: data[0] = word >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Cartridge::read(uint32 addr, uint32 size) {
|
||||
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return read(ram.data, addr & ram.mask, size);
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
|
||||
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.read(addr);
|
||||
if(addr < 0x0e000000) return read(rom.data, addr & 0x01ffffff, size);
|
||||
return cpu.pipeline.fetch.instruction;
|
||||
}
|
||||
|
||||
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
||||
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return write(ram.data, addr & ram.mask, size, word);
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
|
||||
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.write(addr, word);
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
loaded = false;
|
||||
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
|
||||
ram.data = new uint8[ram.size = 32 * 1024];
|
||||
eeprom.data = new uint8[eeprom.size = 8 * 1024];
|
||||
flashrom.data = new uint8[flashrom.size = 128 * 1024];
|
||||
}
|
||||
|
||||
Cartridge::~Cartridge() {
|
||||
delete[] rom.data;
|
||||
delete[] ram.data;
|
||||
delete[] eeprom.data;
|
||||
delete[] flashrom.data;
|
||||
}
|
||||
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
struct Cartridge : property<Cartridge> {
|
||||
#include "memory.hpp"
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
readonly<bool> has_sram;
|
||||
readonly<bool> has_eeprom;
|
||||
readonly<bool> has_flashrom;
|
||||
|
||||
struct Information {
|
||||
string markup;
|
||||
} information;
|
||||
|
||||
bool load(const string &markup, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
void power();
|
||||
|
||||
uint8* ram_data();
|
||||
unsigned ram_size();
|
||||
|
||||
uint32 read(uint8 *data, uint32 addr, uint32 size);
|
||||
void write(uint8 *data, uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
uint32 read(uint32 addr, uint32 size);
|
||||
void write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
@@ -1,100 +0,0 @@
|
||||
//Dev.ID Size Blocks Manufacturer
|
||||
//====== ===== ======== ============
|
||||
//0xd4bf 64KB 16x4096 SST
|
||||
//0x1cc2 64KB 16x4096 Macronix
|
||||
//0x1b32 64KB 16x4096 Panasonic
|
||||
//0x3d1f 64KB 512x 128 Atmel
|
||||
//0x1362 128KB 32x4096 Sanyo
|
||||
//0x09c2 128KB 32x4096 Macronix
|
||||
|
||||
uint8 Cartridge::FlashROM::read(uint16 addr) {
|
||||
if(idmode) {
|
||||
if(addr == 0x0000) return id >> 0;
|
||||
if(addr == 0x0001) return id >> 8;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
return data[bank << 16 | addr];
|
||||
}
|
||||
|
||||
void Cartridge::FlashROM::write(uint16 addr, uint8 byte) {
|
||||
if(bankselect) {
|
||||
bankselect = false;
|
||||
//bank select is only applicable on 128KB chips
|
||||
if(addr == 0x0000) bank = byte & (size > 64 * 1024);
|
||||
return;
|
||||
}
|
||||
|
||||
if(writeselect) {
|
||||
//Atmel writes 128 bytes per command; all others write 1 byte per command
|
||||
if(id != 0x3d1f || (addr & 0x007f) == 0x007f) writeselect = false;
|
||||
data[bank << 16 | addr] = byte;
|
||||
return;
|
||||
}
|
||||
|
||||
if(byte == 0xaa && addr == 0x5555) { unlockhi = true; return; }
|
||||
if(byte == 0x55 && addr == 0x2aaa) { unlocklo = true; return; }
|
||||
|
||||
if(unlockhi && unlocklo) {
|
||||
unlockhi = false;
|
||||
unlocklo = false;
|
||||
|
||||
if(byte == 0x10 && addr == 0x5555) {
|
||||
if(erasemode) {
|
||||
erasemode = false;
|
||||
for(unsigned n = 0; n < size; n++) data[n] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte == 0x30 && (addr & 0x0fff) == 0x0000) {
|
||||
//command only valid for non-Atmel chips
|
||||
if(erasemode && id != 0x3d1f) {
|
||||
erasemode = false;
|
||||
unsigned offset = bank << 16 | (addr & ~4095);
|
||||
for(unsigned n = 0; n < 4096; n++) data[offset++] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte == 0x80 && addr == 0x5555) {
|
||||
erasemode = true;
|
||||
}
|
||||
|
||||
if(byte == 0x90 && addr == 0x5555) {
|
||||
idmode = true;
|
||||
}
|
||||
|
||||
if(byte == 0xa0 && addr == 0x5555) {
|
||||
writeselect = true;
|
||||
}
|
||||
|
||||
if(byte == 0xb0 && addr == 0x5555) {
|
||||
bankselect = true;
|
||||
}
|
||||
|
||||
if(byte == 0xf0 && addr == 0x5555) {
|
||||
idmode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::FlashROM::power() {
|
||||
unlockhi = false;
|
||||
unlocklo = false;
|
||||
idmode = false;
|
||||
bankselect = false;
|
||||
writeselect = false;
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
void Cartridge::FlashROM::serialize(serializer &s) {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(id);
|
||||
s.integer(unlockhi);
|
||||
s.integer(unlocklo);
|
||||
s.integer(idmode);
|
||||
s.integer(erasemode);
|
||||
s.integer(bankselect);
|
||||
s.integer(writeselect);
|
||||
s.integer(bank);
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
struct Memory {
|
||||
uint8 *data;
|
||||
unsigned size;
|
||||
unsigned mask;
|
||||
} rom, ram;
|
||||
|
||||
struct EEPROM {
|
||||
uint8 *data;
|
||||
unsigned size;
|
||||
unsigned mask;
|
||||
unsigned test;
|
||||
unsigned bits;
|
||||
|
||||
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
|
||||
unsigned offset;
|
||||
unsigned address;
|
||||
unsigned addressbits;
|
||||
|
||||
bool read(unsigned addr);
|
||||
void write(unsigned addr, bool bit);
|
||||
|
||||
bool read();
|
||||
void write(bool bit);
|
||||
void power();
|
||||
void serialize(serializer&);
|
||||
} eeprom;
|
||||
|
||||
struct FlashROM {
|
||||
uint8 *data;
|
||||
unsigned size;
|
||||
uint16 id;
|
||||
|
||||
bool unlockhi;
|
||||
bool unlocklo;
|
||||
bool idmode;
|
||||
bool erasemode;
|
||||
bool bankselect;
|
||||
bool writeselect;
|
||||
bool bank;
|
||||
|
||||
uint8 read(uint16 addr);
|
||||
void write(uint16 addr, uint8 byte);
|
||||
void power();
|
||||
void serialize(serializer&);
|
||||
} flashrom;
|
@@ -1,5 +0,0 @@
|
||||
void Cartridge::serialize(serializer &s) {
|
||||
if(has_sram) s.array(ram.data, ram.size);
|
||||
if(has_eeprom) eeprom.serialize(s);
|
||||
if(has_flashrom) flashrom.serialize(s);
|
||||
}
|
@@ -1,173 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GBA {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "timer.cpp"
|
||||
#include "serialization.cpp"
|
||||
CPU cpu;
|
||||
|
||||
void CPU::Enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::main() {
|
||||
#if defined(DEBUG)
|
||||
if(crash) {
|
||||
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
|
||||
: disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||
print(disassemble_registers(), "\n");
|
||||
print("Executed: ", instructions, "\n");
|
||||
while(true) step(frequency);
|
||||
}
|
||||
#endif
|
||||
|
||||
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
|
||||
|
||||
if(regs.mode == Registers::Mode::Stop) {
|
||||
if((regs.irq.enable.keypad & regs.irq.flag.keypad) == 0) {
|
||||
sync_step(16); //STOP does not advance timers
|
||||
} else {
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dma_run();
|
||||
|
||||
if(regs.mode == Registers::Mode::Halt) {
|
||||
if((regs.irq.enable & regs.irq.flag) == 0) {
|
||||
step(16);
|
||||
} else {
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void CPU::step(unsigned clocks) {
|
||||
timer_step(clocks);
|
||||
sync_step(clocks);
|
||||
}
|
||||
|
||||
void CPU::sync_step(unsigned clocks) {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
}
|
||||
|
||||
void CPU::bus_idle(uint32 addr) {
|
||||
step(1);
|
||||
return bus.idle(addr);
|
||||
}
|
||||
|
||||
uint32 CPU::bus_read(uint32 addr, uint32 size) {
|
||||
step(bus.speed(addr, size));
|
||||
return bus.read(addr, size);
|
||||
}
|
||||
|
||||
void CPU::bus_write(uint32 addr, uint32 size, uint32 word) {
|
||||
step(bus.speed(addr, size));
|
||||
return bus.write(addr, size, word);
|
||||
}
|
||||
|
||||
void CPU::keypad_run() {
|
||||
if(regs.keypad.control.enable == false) return;
|
||||
|
||||
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
|
||||
for(unsigned n = 0; n < 10; n++) {
|
||||
if(regs.keypad.control.flag[n] == false) continue;
|
||||
bool input = interface->inputPoll(n);
|
||||
if(regs.keypad.control.condition == 0) test |= input;
|
||||
if(regs.keypad.control.condition == 1) test &= input;
|
||||
}
|
||||
if(test) regs.irq.flag.keypad = true;
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
create(CPU::Enter, 16777216);
|
||||
|
||||
ARM::power();
|
||||
for(unsigned n = 0; n < 32 * 1024; n++) iwram[n] = 0;
|
||||
for(unsigned n = 0; n < 256 * 1024; n++) ewram[n] = 0;
|
||||
|
||||
for(auto &dma : regs.dma) {
|
||||
dma.source = 0;
|
||||
dma.target = 0;
|
||||
dma.length = 0;
|
||||
dma.control = 0;
|
||||
dma.pending = 0;
|
||||
dma.run.target = 0;
|
||||
dma.run.source = 0;
|
||||
dma.run.length = 0;
|
||||
}
|
||||
for(auto &timer : regs.timer) {
|
||||
timer.period = 0;
|
||||
timer.reload = 0;
|
||||
timer.control = 0;
|
||||
}
|
||||
regs.keypad.control = 0;
|
||||
regs.ime = 0;
|
||||
regs.irq.enable = 0;
|
||||
regs.irq.flag = 0;
|
||||
regs.wait.control = 0;
|
||||
regs.postboot = 0;
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
regs.clock = 0;
|
||||
regs.memory.control = 0x0d000020;
|
||||
|
||||
pending.dma.vblank = 0;
|
||||
pending.dma.hblank = 0;
|
||||
pending.dma.hdma = 0;
|
||||
|
||||
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
||||
for(unsigned n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||
for(unsigned n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
||||
for(unsigned n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
|
||||
for(unsigned n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
|
||||
for(unsigned n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
|
||||
for(unsigned n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
|
||||
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
|
||||
}
|
||||
|
||||
CPU::CPU() {
|
||||
iwram = new uint8[ 32 * 1024];
|
||||
ewram = new uint8[256 * 1024];
|
||||
|
||||
regs.dma[0].source.bits(27); regs.dma[0].run.source.bits(27);
|
||||
regs.dma[0].target.bits(27); regs.dma[0].run.target.bits(27);
|
||||
regs.dma[0].length.bits(14); regs.dma[0].run.length.bits(14);
|
||||
|
||||
regs.dma[1].source.bits(28); regs.dma[1].run.source.bits(28);
|
||||
regs.dma[1].target.bits(27); regs.dma[1].run.target.bits(27);
|
||||
regs.dma[1].length.bits(14); regs.dma[1].run.length.bits(14);
|
||||
|
||||
regs.dma[2].source.bits(28); regs.dma[2].run.source.bits(28);
|
||||
regs.dma[2].target.bits(27); regs.dma[2].run.target.bits(27);
|
||||
regs.dma[2].length.bits(14); regs.dma[2].run.length.bits(14);
|
||||
|
||||
regs.dma[3].source.bits(28); regs.dma[3].run.source.bits(28);
|
||||
regs.dma[3].target.bits(28); regs.dma[3].run.target.bits(28);
|
||||
regs.dma[3].length.bits(16); regs.dma[3].run.length.bits(16);
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
delete[] iwram;
|
||||
delete[] ewram;
|
||||
}
|
||||
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
struct CPU : Processor::ARM, Thread, MMIO {
|
||||
uint8 *iwram;
|
||||
uint8 *ewram;
|
||||
#include "registers.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
void step(unsigned clocks);
|
||||
void sync_step(unsigned clocks);
|
||||
|
||||
void bus_idle(uint32 addr);
|
||||
uint32 bus_read(uint32 addr, uint32 size);
|
||||
void bus_write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
void keypad_run();
|
||||
void power();
|
||||
|
||||
uint8 read(uint32 addr);
|
||||
void write(uint32 addr, uint8 byte);
|
||||
|
||||
uint32 iwram_read(uint32 addr, uint32 size);
|
||||
void iwram_write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
uint32 ewram_read(uint32 addr, uint32 size);
|
||||
void ewram_write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
void dma_run();
|
||||
void dma_transfer(Registers::DMA &dma);
|
||||
void dma_vblank();
|
||||
void dma_hblank();
|
||||
void dma_hdma();
|
||||
|
||||
void timer_step(unsigned clocks);
|
||||
void timer_increment(unsigned n);
|
||||
void timer_fifo_run(unsigned n);
|
||||
|
||||
void serialize(serializer&);
|
||||
CPU();
|
||||
~CPU();
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
@@ -1,60 +0,0 @@
|
||||
void CPU::dma_run() {
|
||||
for(unsigned n = 0; n < 4; n++) {
|
||||
auto &dma = regs.dma[n];
|
||||
if(dma.pending) {
|
||||
dma.pending = false;
|
||||
dma_transfer(dma);
|
||||
if(dma.control.irq) regs.irq.flag.dma[n] = 1;
|
||||
if(dma.control.drq && n == 3) regs.irq.flag.cartridge = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::dma_transfer(Registers::DMA &dma) {
|
||||
unsigned size = dma.control.size ? Word : Half;
|
||||
unsigned seek = dma.control.size ? 4 : 2;
|
||||
|
||||
sequential() = false;
|
||||
do {
|
||||
step(bus.speed(dma.run.source, size));
|
||||
uint32 word = bus.read(dma.run.source, size);
|
||||
|
||||
step(bus.speed(dma.run.target, size));
|
||||
bus.write(dma.run.target, size, word);
|
||||
|
||||
sequential() = true;
|
||||
|
||||
switch(dma.control.sourcemode) {
|
||||
case 0: dma.run.source += seek; break;
|
||||
case 1: dma.run.source -= seek; break;
|
||||
}
|
||||
|
||||
switch(dma.control.targetmode) {
|
||||
case 0: dma.run.target += seek; break;
|
||||
case 1: dma.run.target -= seek; break;
|
||||
case 3: dma.run.target += seek; break;
|
||||
}
|
||||
} while(--dma.run.length);
|
||||
sequential() = false;
|
||||
|
||||
if(dma.control.targetmode == 3) dma.run.target = dma.target;
|
||||
if(dma.control.repeat == 1) dma.run.length = dma.length;
|
||||
if(dma.control.repeat == 0) dma.control.enable = false;
|
||||
}
|
||||
|
||||
void CPU::dma_vblank() {
|
||||
for(auto &dma : regs.dma) {
|
||||
if(dma.control.enable && dma.control.timingmode == 1) dma.pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::dma_hblank() {
|
||||
for(auto &dma : regs.dma) {
|
||||
if(dma.control.enable && dma.control.timingmode == 2) dma.pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::dma_hdma() {
|
||||
auto &dma = regs.dma[3];
|
||||
if(dma.control.enable && dma.control.timingmode == 3) dma.pending = true;
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
uint32 CPU::iwram_read(uint32 addr, uint32 size) {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
|
||||
if(size == Word) return iwram_read(addr &~ 2, Half) << 0 | iwram_read(addr | 2, Half) << 16;
|
||||
if(size == Half) return iwram_read(addr &~ 1, Byte) << 0 | iwram_read(addr | 1, Byte) << 8;
|
||||
|
||||
return iwram[addr & 0x7fff];
|
||||
}
|
||||
|
||||
void CPU::iwram_write(uint32 addr, uint32 size, uint32 word) {
|
||||
if(regs.memory.control.disable) return;
|
||||
|
||||
if(size == Word) {
|
||||
iwram_write(addr &~2, Half, word >> 0);
|
||||
iwram_write(addr | 2, Half, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Half) {
|
||||
iwram_write(addr &~1, Byte, word >> 0);
|
||||
iwram_write(addr | 1, Byte, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
iwram[addr & 0x7fff] = word;
|
||||
}
|
||||
|
||||
uint32 CPU::ewram_read(uint32 addr, uint32 size) {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
if(regs.memory.control.ewram == false) return iwram_read(addr, size);
|
||||
|
||||
if(size == Word) return ewram_read(addr &~ 2, Half) << 0 | ewram_read(addr | 2, Half) << 16;
|
||||
if(size == Half) return ewram_read(addr &~ 1, Byte) << 0 | ewram_read(addr | 1, Byte) << 8;
|
||||
|
||||
return ewram[addr & 0x3ffff];
|
||||
}
|
||||
|
||||
void CPU::ewram_write(uint32 addr, uint32 size, uint32 word) {
|
||||
if(regs.memory.control.disable) return;
|
||||
if(regs.memory.control.ewram == false) return iwram_write(addr, size, word);
|
||||
|
||||
if(size == Word) {
|
||||
ewram_write(addr &~2, Half, word >> 0);
|
||||
ewram_write(addr | 2, Half, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Half) {
|
||||
ewram_write(addr &~1, Byte, word >> 0);
|
||||
ewram_write(addr | 1, Byte, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
ewram[addr & 0x3ffff] = word;
|
||||
}
|
@@ -1,316 +0,0 @@
|
||||
uint8 CPU::read(uint32 addr) {
|
||||
uint8 result = 0;
|
||||
|
||||
switch(addr) {
|
||||
|
||||
//DMA0CNT_H
|
||||
//DMA1CNT_H
|
||||
//DMA2CNT_H
|
||||
//DMA3CNT_H
|
||||
case 0x040000ba: case 0x040000bb:
|
||||
case 0x040000c6: case 0x040000c7:
|
||||
case 0x040000d2: case 0x040000d3:
|
||||
case 0x040000de: case 0x040000df: {
|
||||
auto &dma = regs.dma[(addr - 0x040000ba) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return dma.control >> shift;
|
||||
}
|
||||
|
||||
//TM0CNT_L
|
||||
//TM1CNT_L
|
||||
//TM2CNT_L
|
||||
//TM3CNT_L
|
||||
case 0x04000100: case 0x04000101:
|
||||
case 0x04000104: case 0x04000105:
|
||||
case 0x04000108: case 0x04000109:
|
||||
case 0x0400010c: case 0x0400010d: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.period >> shift;
|
||||
}
|
||||
|
||||
//TIM0CNT_H
|
||||
case 0x04000102: case 0x04000103:
|
||||
case 0x04000106: case 0x04000107:
|
||||
case 0x0400010a: case 0x0400010b:
|
||||
case 0x0400010e: case 0x0400010f: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.control >> shift;
|
||||
}
|
||||
|
||||
//SIOMULTI0 (SIODATA32_L)
|
||||
//SIOMULTI1 (SIODATA32_H)
|
||||
//SIOMULTI2
|
||||
//SIOMULTI3
|
||||
case 0x04000120: case 0x04000121:
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
auto &data = regs.serial.data[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return data >> shift;
|
||||
}
|
||||
|
||||
//SIOCNT
|
||||
case 0x04000128: return regs.serial.control >> 0;
|
||||
case 0x04000129: return regs.serial.control >> 8;
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
case 0x0400012a: return regs.serial.data8;
|
||||
case 0x0400012b: return 0u;
|
||||
|
||||
//KEYINPUT
|
||||
case 0x04000130:
|
||||
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(n) << n;
|
||||
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
|
||||
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
|
||||
return result ^ 0xff;
|
||||
case 0x04000131:
|
||||
result |= interface->inputPoll(8) << 0;
|
||||
result |= interface->inputPoll(9) << 1;
|
||||
return result ^ 0x03;
|
||||
|
||||
//KEYCNT
|
||||
case 0x04000132: return regs.keypad.control >> 0;
|
||||
case 0x04000133: return regs.keypad.control >> 8;
|
||||
|
||||
//RCNT
|
||||
case 0x04000134: return regs.joybus.settings >> 0;
|
||||
case 0x04000135: return regs.joybus.settings >> 8;
|
||||
|
||||
//JOYCNT
|
||||
case 0x04000140: return regs.joybus.control >> 0;
|
||||
case 0x04000141: return regs.joybus.control >> 8;
|
||||
|
||||
//JOY_RECV_L
|
||||
//JOY_RECV_H
|
||||
case 0x04000150: return regs.joybus.receive >> 0;
|
||||
case 0x04000151: return regs.joybus.receive >> 8;
|
||||
case 0x04000152: return regs.joybus.receive >> 16;
|
||||
case 0x04000153: return regs.joybus.receive >> 24;
|
||||
|
||||
//JOY_TRANS_L
|
||||
//JOY_TRANS_H
|
||||
case 0x04000154: return regs.joybus.transmit >> 0;
|
||||
case 0x04000155: return regs.joybus.transmit >> 8;
|
||||
case 0x04000156: return regs.joybus.transmit >> 16;
|
||||
case 0x04000157: return regs.joybus.transmit >> 24;
|
||||
|
||||
//JOYSTAT
|
||||
case 0x04000158: return regs.joybus.status >> 0;
|
||||
case 0x04000159: return regs.joybus.status >> 8;
|
||||
|
||||
//IE
|
||||
case 0x04000200: return regs.irq.enable >> 0;
|
||||
case 0x04000201: return regs.irq.enable >> 8;
|
||||
|
||||
//IF
|
||||
case 0x04000202: return regs.irq.flag >> 0;
|
||||
case 0x04000203: return regs.irq.flag >> 8;
|
||||
|
||||
//WAITCNT
|
||||
case 0x04000204: return regs.wait.control >> 0;
|
||||
case 0x04000205: return regs.wait.control >> 8;
|
||||
|
||||
//IME
|
||||
case 0x04000208: return regs.ime;
|
||||
case 0x04000209: return 0u;
|
||||
|
||||
//POSTFLG + HALTCNT
|
||||
case 0x04000300: return regs.postboot;
|
||||
case 0x04000301: return 0u;
|
||||
|
||||
//MEMCNT_L
|
||||
case 0x04000800: return regs.memory.control >> 0;
|
||||
case 0x04000801: return regs.memory.control >> 8;
|
||||
|
||||
//MEMCNT_H
|
||||
case 0x04000802: return regs.memory.control >> 16;
|
||||
case 0x04000803: return regs.memory.control >> 24;
|
||||
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void CPU::write(uint32 addr, uint8 byte) {
|
||||
switch(addr) {
|
||||
|
||||
//DMA0SAD
|
||||
//DMA1SAD
|
||||
//DMA2SAD
|
||||
//DMA3SAD
|
||||
case 0x040000b0: case 0x040000b1: case 0x040000b2: case 0x040000b3:
|
||||
case 0x040000bc: case 0x040000bd: case 0x040000be: case 0x040000bf:
|
||||
case 0x040000c8: case 0x040000c9: case 0x040000ca: case 0x040000cb:
|
||||
case 0x040000d4: case 0x040000d5: case 0x040000d6: case 0x040000d7: {
|
||||
auto &dma = regs.dma[(addr - 0x040000b0) / 12];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
dma.source = (dma.source & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0DAD
|
||||
//DMA1DAD
|
||||
//DMA2DAD
|
||||
//DMA3DAD
|
||||
case 0x040000b4: case 0x040000b5: case 0x040000b6: case 0x040000b7:
|
||||
case 0x040000c0: case 0x040000c1: case 0x040000c2: case 0x040000c3:
|
||||
case 0x040000cc: case 0x040000cd: case 0x040000ce: case 0x040000cf:
|
||||
case 0x040000d8: case 0x040000d9: case 0x040000da: case 0x040000db: {
|
||||
auto &dma = regs.dma[(addr - 0x040000b4) / 12];
|
||||
unsigned shift = (addr & 3) * 8;
|
||||
dma.target = (dma.target & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0CNT_L
|
||||
//DMA1CNT_L
|
||||
//DMA2CNT_L
|
||||
//DMA3CNT_L
|
||||
case 0x040000b8: case 0x040000b9:
|
||||
case 0x040000c4: case 0x040000c5:
|
||||
case 0x040000d0: case 0x040000d1:
|
||||
case 0x040000dc: case 0x040000dd: {
|
||||
auto &dma = regs.dma[(addr - 0x040000b8) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
dma.length = (dma.length & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//DMA0CNT_H
|
||||
//DMA1CNT_H
|
||||
//DMA2CNT_H
|
||||
//DMA3CNT_H
|
||||
case 0x040000ba: case 0x040000bb:
|
||||
case 0x040000c6: case 0x040000c7:
|
||||
case 0x040000d2: case 0x040000d3:
|
||||
case 0x040000de: case 0x040000df: {
|
||||
auto &dma = regs.dma[(addr - 0x040000ba) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
bool enable = dma.control.enable;
|
||||
dma.control = (dma.control & ~(255 << shift)) | (byte << shift);
|
||||
if(enable == 0 && dma.control.enable) {
|
||||
if(dma.control.timingmode == 0) dma.pending = true; //immediate transfer mode
|
||||
dma.run.target = dma.target;
|
||||
dma.run.source = dma.source;
|
||||
dma.run.length = dma.length;
|
||||
} else if(dma.control.enable == 0) {
|
||||
dma.pending = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//TM0CNT_L
|
||||
//TM1CNT_L
|
||||
//TM2CNT_L
|
||||
//TM3CNT_L
|
||||
case 0x04000100: case 0x04000101:
|
||||
case 0x04000104: case 0x04000105:
|
||||
case 0x04000108: case 0x04000109:
|
||||
case 0x0400010c: case 0x0400010d: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
timer.reload = (timer.reload & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//TM0CNT_H
|
||||
//TM1CNT_H
|
||||
//TM2CNT_H
|
||||
//TM3CNT_H
|
||||
case 0x04000102:
|
||||
case 0x04000106:
|
||||
case 0x0400010a:
|
||||
case 0x0400010e: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
bool enable = timer.control.enable;
|
||||
timer.control = byte;
|
||||
if(enable == 0 && timer.control.enable == 1) {
|
||||
timer.period = timer.reload;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//SIOMULTI0 (SIODATA32_L)
|
||||
//SIOMULTI1 (SIODATA32_H)
|
||||
//SIOMULTI2
|
||||
//SIOMULTI3
|
||||
case 0x04000120: case 0x04000121:
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
auto &data = regs.serial.data[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
data = (data & ~(255 << shift)) | (byte << shift);
|
||||
return;
|
||||
}
|
||||
|
||||
//SIOCNT
|
||||
case 0x04000128: regs.serial.control = (regs.serial.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000129: regs.serial.control = (regs.serial.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
case 0x0400012a: regs.serial.data8 = byte; return;
|
||||
case 0x0400012b: return;
|
||||
|
||||
//KEYCNT
|
||||
case 0x04000132: regs.keypad.control = (regs.keypad.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000133: regs.keypad.control = (regs.keypad.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//RCNT
|
||||
case 0x04000134: regs.joybus.settings = (regs.joybus.settings & 0xff00) | (byte << 0); return;
|
||||
case 0x04000135: regs.joybus.settings = (regs.joybus.settings & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//JOYCNT
|
||||
case 0x04000140: regs.joybus.control = (regs.joybus.control & 0xff00) | (byte << 0); return;
|
||||
case 0x04000141: regs.joybus.control = (regs.joybus.control & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//JOY_RECV_L
|
||||
//JOY_RECV_H
|
||||
case 0x04000150: regs.joybus.receive = (regs.joybus.receive & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000151: regs.joybus.receive = (regs.joybus.receive & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000152: regs.joybus.receive = (regs.joybus.receive & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000153: regs.joybus.receive = (regs.joybus.receive & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
//JOY_TRANS_L
|
||||
//JOY_TRANS_H
|
||||
case 0x04000154: regs.joybus.transmit = (regs.joybus.transmit & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000155: regs.joybus.transmit = (regs.joybus.transmit & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000156: regs.joybus.transmit = (regs.joybus.transmit & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000157: regs.joybus.transmit = (regs.joybus.transmit & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
//JOYSTAT
|
||||
case 0x04000158: regs.joybus.status = (regs.joybus.status & 0xff00) | (byte << 0); return;
|
||||
case 0x04000159: regs.joybus.status = (regs.joybus.status & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//IE
|
||||
case 0x04000200: regs.irq.enable = (regs.irq.enable & 0xff00) | (byte << 0); return;
|
||||
case 0x04000201: regs.irq.enable = (regs.irq.enable & 0x00ff) | (byte << 8); return;
|
||||
|
||||
//IF
|
||||
case 0x04000202: regs.irq.flag = regs.irq.flag & ~(byte << 0); return;
|
||||
case 0x04000203: regs.irq.flag = regs.irq.flag & ~(byte << 8); return;
|
||||
|
||||
//WAITCNT
|
||||
case 0x04000204: regs.wait.control = (regs.wait.control & 0xff00) | ((byte & 0xff) << 0); return;
|
||||
case 0x04000205: regs.wait.control = (regs.wait.control & 0x00ff) | ((byte & 0x7f) << 8); return;
|
||||
|
||||
//IME
|
||||
case 0x04000208: regs.ime = byte >> 0; return;
|
||||
case 0x04000209: return;
|
||||
|
||||
//POSTFLG, HALTCNT
|
||||
case 0x04000300: regs.postboot |= byte >> 0; return;
|
||||
case 0x04000301: regs.mode = byte & 0x80 ? Registers::Mode::Stop : Registers::Mode::Halt; return;
|
||||
|
||||
//MEMCNT_L
|
||||
//MEMCNT_H
|
||||
case 0x04000800: regs.memory.control = (regs.memory.control & 0xffffff00) | (byte << 0); return;
|
||||
case 0x04000801: regs.memory.control = (regs.memory.control & 0xffff00ff) | (byte << 8); return;
|
||||
case 0x04000802: regs.memory.control = (regs.memory.control & 0xff00ffff) | (byte << 16); return;
|
||||
case 0x04000803: regs.memory.control = (regs.memory.control & 0x00ffffff) | (byte << 24); return;
|
||||
|
||||
}
|
||||
}
|
@@ -1,244 +0,0 @@
|
||||
CPU::Registers::DMAControl::operator uint16() const {
|
||||
return (
|
||||
(targetmode << 5)
|
||||
| (sourcemode << 7)
|
||||
| (repeat << 9)
|
||||
| (size << 10)
|
||||
| (drq << 11)
|
||||
| (timingmode << 12)
|
||||
| (irq << 14)
|
||||
| (enable << 15)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::DMAControl::operator=(uint16 source) {
|
||||
targetmode = source >> 5;
|
||||
sourcemode = source >> 7;
|
||||
repeat = source >> 9;
|
||||
size = source >> 10;
|
||||
drq = source >> 11;
|
||||
timingmode = source >> 12;
|
||||
irq = source >> 14;
|
||||
enable = source >> 15;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::TimerControl::operator uint16() const {
|
||||
return (
|
||||
(frequency << 0)
|
||||
| (cascade << 2)
|
||||
| (irq << 6)
|
||||
| (enable << 7)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::TimerControl::operator=(uint16 source) {
|
||||
frequency = source >> 0;
|
||||
cascade = source >> 2;
|
||||
irq = source >> 6;
|
||||
enable = source >> 7;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::SerialControl::operator uint16() const {
|
||||
return (
|
||||
(shiftclockselect << 0)
|
||||
| (shiftclockfrequency << 1)
|
||||
| (transferenablereceive << 2)
|
||||
| (transferenablesend << 3)
|
||||
| (startbit << 7)
|
||||
| (transferlength << 12)
|
||||
| (irqenable << 14)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::SerialControl::operator=(uint16 source) {
|
||||
shiftclockselect = source >> 0;
|
||||
shiftclockfrequency = source >> 1;
|
||||
transferenablereceive = source >> 2;
|
||||
transferenablesend = source >> 3;
|
||||
startbit = source >> 7;
|
||||
transferlength = source >> 12;
|
||||
irqenable = source >> 14;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::KeypadControl::operator uint16() const {
|
||||
return (
|
||||
(flag[0] << 0)
|
||||
| (flag[1] << 1)
|
||||
| (flag[2] << 2)
|
||||
| (flag[3] << 3)
|
||||
| (flag[4] << 4)
|
||||
| (flag[5] << 5)
|
||||
| (flag[6] << 6)
|
||||
| (flag[7] << 7)
|
||||
| (flag[8] << 8)
|
||||
| (flag[9] << 9)
|
||||
| (enable << 14)
|
||||
| (condition << 15)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::KeypadControl::operator=(uint16 source) {
|
||||
flag[0] = source >> 0;
|
||||
flag[1] = source >> 1;
|
||||
flag[2] = source >> 2;
|
||||
flag[3] = source >> 3;
|
||||
flag[4] = source >> 4;
|
||||
flag[5] = source >> 5;
|
||||
flag[6] = source >> 6;
|
||||
flag[7] = source >> 7;
|
||||
flag[8] = source >> 8;
|
||||
flag[9] = source >> 9;
|
||||
enable = source >> 14;
|
||||
condition = source >> 15;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusSettings::operator uint16() const {
|
||||
return (
|
||||
(sc << 0)
|
||||
| (sd << 1)
|
||||
| (si << 2)
|
||||
| (so << 3)
|
||||
| (scmode << 4)
|
||||
| (sdmode << 5)
|
||||
| (simode << 6)
|
||||
| (somode << 7)
|
||||
| (irqenable << 8)
|
||||
| (mode << 14)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::JoybusSettings::operator=(uint16 source) {
|
||||
sc = source >> 0;
|
||||
sd = source >> 1;
|
||||
si = source >> 2;
|
||||
so = source >> 3;
|
||||
scmode = source >> 4;
|
||||
sdmode = source >> 5;
|
||||
simode = source >> 6;
|
||||
somode = source >> 7;
|
||||
irqenable = source >> 8;
|
||||
mode = source >> 14;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusControl::operator uint16() const {
|
||||
return (
|
||||
(resetsignal << 0)
|
||||
| (receivecomplete << 1)
|
||||
| (sendcomplete << 2)
|
||||
| (irqenable << 6)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::JoybusControl::operator=(uint16 source) {
|
||||
resetsignal = source >> 0;
|
||||
receivecomplete = source >> 1;
|
||||
sendcomplete = source >> 2;
|
||||
irqenable = source >> 6;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::JoybusStatus::operator uint16() const {
|
||||
return (
|
||||
(receiveflag << 1)
|
||||
| (sendflag << 3)
|
||||
| (generalflag << 4)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::JoybusStatus::operator=(uint16 source) {
|
||||
receiveflag = source >> 1;
|
||||
sendflag = source >> 3;
|
||||
generalflag = source >> 4;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::Interrupt::operator uint16() const {
|
||||
return (
|
||||
(vblank << 0)
|
||||
| (hblank << 1)
|
||||
| (vcoincidence << 2)
|
||||
| (timer[0] << 3)
|
||||
| (timer[1] << 4)
|
||||
| (timer[2] << 5)
|
||||
| (timer[3] << 6)
|
||||
| (serial << 7)
|
||||
| (dma[0] << 8)
|
||||
| (dma[1] << 9)
|
||||
| (dma[2] << 10)
|
||||
| (dma[3] << 11)
|
||||
| (keypad << 12)
|
||||
| (cartridge << 13)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::Interrupt::operator=(uint16 source) {
|
||||
vblank = source >> 0;
|
||||
hblank = source >> 1;
|
||||
vcoincidence = source >> 2;
|
||||
timer[0] = source >> 3;
|
||||
timer[1] = source >> 4;
|
||||
timer[2] = source >> 5;
|
||||
timer[3] = source >> 6;
|
||||
serial = source >> 7;
|
||||
dma[0] = source >> 8;
|
||||
dma[1] = source >> 9;
|
||||
dma[2] = source >> 10;
|
||||
dma[3] = source >> 11;
|
||||
keypad = source >> 12;
|
||||
cartridge = source >> 13;
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::WaitControl::operator uint16() const {
|
||||
return (
|
||||
(nwait[3] << 0)
|
||||
| (nwait[0] << 2)
|
||||
| (swait[0] << 4)
|
||||
| (nwait[1] << 5)
|
||||
| (swait[1] << 7)
|
||||
| (nwait[2] << 8)
|
||||
| (swait[2] << 10)
|
||||
| (phi << 11)
|
||||
| (prefetch << 14)
|
||||
| (gametype << 15)
|
||||
);
|
||||
}
|
||||
|
||||
uint16 CPU::Registers::WaitControl::operator=(uint16 source) {
|
||||
nwait[3] = (source >> 0) & 3;
|
||||
nwait[0] = (source >> 2) & 3;
|
||||
swait[0] = (source >> 4) & 1;
|
||||
nwait[1] = (source >> 5) & 3;
|
||||
swait[1] = (source >> 7) & 1;
|
||||
nwait[2] = (source >> 8) & 3;
|
||||
swait[2] = (source >> 10) & 1;
|
||||
phi = (source >> 11) & 3;
|
||||
prefetch = (source >> 14) & 1;
|
||||
gametype = (source >> 15) & 1;
|
||||
swait[3] = nwait[3];
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
CPU::Registers::MemoryControl::operator uint32() const {
|
||||
return (
|
||||
(disable << 0)
|
||||
| (unknown1 << 1)
|
||||
| (ewram << 5)
|
||||
| (ewramwait << 24)
|
||||
| (unknown2 << 28)
|
||||
);
|
||||
}
|
||||
|
||||
uint32 CPU::Registers::MemoryControl::operator=(uint32 source) {
|
||||
disable = source >> 0;
|
||||
unknown1 = source >> 1;
|
||||
ewram = source >> 5;
|
||||
ewramwait = source >> 24;
|
||||
unknown2 = source >> 28;
|
||||
return operator uint32();
|
||||
}
|
@@ -1,186 +0,0 @@
|
||||
struct Registers {
|
||||
struct DMAControl {
|
||||
uint2 targetmode;
|
||||
uint2 sourcemode;
|
||||
uint1 repeat;
|
||||
uint1 size;
|
||||
uint1 drq;
|
||||
uint2 timingmode;
|
||||
uint1 irq;
|
||||
uint1 enable;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
DMAControl& operator=(const DMAControl&) = delete;
|
||||
};
|
||||
|
||||
struct DMA {
|
||||
varuint source;
|
||||
varuint target;
|
||||
varuint length;
|
||||
DMAControl control;
|
||||
|
||||
//internal
|
||||
bool pending;
|
||||
struct Run {
|
||||
varuint target;
|
||||
varuint source;
|
||||
varuint length;
|
||||
} run;
|
||||
} dma[4];
|
||||
|
||||
struct TimerControl {
|
||||
uint2 frequency;
|
||||
uint1 cascade;
|
||||
uint1 irq;
|
||||
uint1 enable;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
TimerControl& operator=(const TimerControl&) = delete;
|
||||
};
|
||||
|
||||
struct Timer {
|
||||
uint16 period;
|
||||
uint16 reload;
|
||||
TimerControl control;
|
||||
} timer[4];
|
||||
|
||||
struct SerialControl {
|
||||
uint1 shiftclockselect;
|
||||
uint1 shiftclockfrequency;
|
||||
uint1 transferenablereceive;
|
||||
uint1 transferenablesend;
|
||||
uint1 startbit;
|
||||
uint1 transferlength;
|
||||
uint1 irqenable;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
SerialControl& operator=(const SerialControl&) = delete;
|
||||
};
|
||||
|
||||
struct Serial {
|
||||
uint16 data[4];
|
||||
SerialControl control;
|
||||
uint8 data8;
|
||||
} serial;
|
||||
|
||||
struct KeypadControl {
|
||||
uint1 flag[10];
|
||||
uint1 enable;
|
||||
uint1 condition;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
KeypadControl& operator=(const KeypadControl&) = delete;
|
||||
};
|
||||
|
||||
struct Keypad {
|
||||
KeypadControl control;
|
||||
} keypad;
|
||||
|
||||
struct JoybusSettings {
|
||||
uint1 sc;
|
||||
uint1 sd;
|
||||
uint1 si;
|
||||
uint1 so;
|
||||
uint1 scmode;
|
||||
uint1 sdmode;
|
||||
uint1 simode;
|
||||
uint1 somode;
|
||||
uint1 irqenable;
|
||||
uint2 mode;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
JoybusSettings& operator=(const JoybusSettings&) = delete;
|
||||
};
|
||||
|
||||
struct JoybusControl {
|
||||
uint1 resetsignal;
|
||||
uint1 receivecomplete;
|
||||
uint1 sendcomplete;
|
||||
uint1 irqenable;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
JoybusControl& operator=(const JoybusControl&) = delete;
|
||||
};
|
||||
|
||||
struct JoybusStatus {
|
||||
uint1 receiveflag;
|
||||
uint1 sendflag;
|
||||
uint2 generalflag;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
JoybusStatus& operator=(const JoybusStatus&) = delete;
|
||||
};
|
||||
|
||||
struct Joybus {
|
||||
JoybusSettings settings;
|
||||
JoybusControl control;
|
||||
uint32 receive;
|
||||
uint32 transmit;
|
||||
JoybusStatus status;
|
||||
} joybus;
|
||||
|
||||
uint1 ime;
|
||||
|
||||
struct Interrupt {
|
||||
uint1 vblank;
|
||||
uint1 hblank;
|
||||
uint1 vcoincidence;
|
||||
uint1 timer[4];
|
||||
uint1 serial;
|
||||
uint1 dma[4];
|
||||
uint1 keypad;
|
||||
uint1 cartridge;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
Interrupt& operator=(const Interrupt&) = delete;
|
||||
};
|
||||
|
||||
struct IRQ {
|
||||
Interrupt enable;
|
||||
Interrupt flag;
|
||||
} irq;
|
||||
|
||||
struct WaitControl {
|
||||
uint2 nwait[4];
|
||||
uint2 swait[4];
|
||||
uint2 phi;
|
||||
uint1 prefetch;
|
||||
uint1 gametype;
|
||||
|
||||
operator uint16() const;
|
||||
uint16 operator=(uint16 source);
|
||||
WaitControl& operator=(const WaitControl&) = delete;
|
||||
};
|
||||
|
||||
struct Wait {
|
||||
WaitControl control;
|
||||
} wait;
|
||||
|
||||
struct MemoryControl {
|
||||
uint1 disable;
|
||||
uint3 unknown1;
|
||||
uint1 ewram;
|
||||
uint4 ewramwait;
|
||||
uint4 unknown2;
|
||||
|
||||
operator uint32() const;
|
||||
uint32 operator=(uint32 source);
|
||||
MemoryControl& operator=(const MemoryControl&) = delete;
|
||||
};
|
||||
|
||||
struct Memory {
|
||||
MemoryControl control;
|
||||
} memory;
|
||||
|
||||
uint1 postboot;
|
||||
enum class Mode : unsigned { Normal, Halt, Stop } mode;
|
||||
unsigned clock;
|
||||
} regs;
|
@@ -1,110 +0,0 @@
|
||||
void CPU::serialize(serializer &s) {
|
||||
ARM::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(iwram, 32 * 1024);
|
||||
s.array(ewram, 256 * 1024);
|
||||
|
||||
for(auto &dma : regs.dma) {
|
||||
s.integer(dma.source);
|
||||
s.integer(dma.target);
|
||||
s.integer(dma.length);
|
||||
s.integer(dma.control.targetmode);
|
||||
s.integer(dma.control.sourcemode);
|
||||
s.integer(dma.control.repeat);
|
||||
s.integer(dma.control.size);
|
||||
s.integer(dma.control.drq);
|
||||
s.integer(dma.control.timingmode);
|
||||
s.integer(dma.control.irq);
|
||||
s.integer(dma.control.enable);
|
||||
s.integer(dma.run.target);
|
||||
s.integer(dma.run.source);
|
||||
s.integer(dma.run.length);
|
||||
}
|
||||
|
||||
for(auto &timer : regs.timer) {
|
||||
s.integer(timer.period);
|
||||
s.integer(timer.reload);
|
||||
s.integer(timer.control.frequency);
|
||||
s.integer(timer.control.cascade);
|
||||
s.integer(timer.control.irq);
|
||||
s.integer(timer.control.enable);
|
||||
}
|
||||
|
||||
for(auto &value : regs.serial.data) s.integer(value);
|
||||
s.integer(regs.serial.control.shiftclockselect);
|
||||
s.integer(regs.serial.control.shiftclockfrequency);
|
||||
s.integer(regs.serial.control.transferenablereceive);
|
||||
s.integer(regs.serial.control.transferenablesend);
|
||||
s.integer(regs.serial.control.startbit);
|
||||
s.integer(regs.serial.control.transferlength);
|
||||
s.integer(regs.serial.control.irqenable);
|
||||
s.integer(regs.serial.data8);
|
||||
|
||||
for(auto &flag : regs.keypad.control.flag) s.integer(flag);
|
||||
s.integer(regs.keypad.control.enable);
|
||||
s.integer(regs.keypad.control.condition);
|
||||
|
||||
s.integer(regs.joybus.settings.sc);
|
||||
s.integer(regs.joybus.settings.sd);
|
||||
s.integer(regs.joybus.settings.si);
|
||||
s.integer(regs.joybus.settings.so);
|
||||
s.integer(regs.joybus.settings.scmode);
|
||||
s.integer(regs.joybus.settings.sdmode);
|
||||
s.integer(regs.joybus.settings.simode);
|
||||
s.integer(regs.joybus.settings.somode);
|
||||
s.integer(regs.joybus.settings.irqenable);
|
||||
s.integer(regs.joybus.settings.mode);
|
||||
|
||||
s.integer(regs.joybus.control.resetsignal);
|
||||
s.integer(regs.joybus.control.receivecomplete);
|
||||
s.integer(regs.joybus.control.sendcomplete);
|
||||
s.integer(regs.joybus.control.irqenable);
|
||||
|
||||
s.integer(regs.joybus.receive);
|
||||
s.integer(regs.joybus.transmit);
|
||||
|
||||
s.integer(regs.joybus.status.receiveflag);
|
||||
s.integer(regs.joybus.status.sendflag);
|
||||
s.integer(regs.joybus.status.generalflag);
|
||||
|
||||
s.integer(regs.ime);
|
||||
|
||||
s.integer(regs.irq.enable.vblank);
|
||||
s.integer(regs.irq.enable.hblank);
|
||||
s.integer(regs.irq.enable.vcoincidence);
|
||||
for(auto &flag : regs.irq.enable.timer) s.integer(flag);
|
||||
s.integer(regs.irq.enable.serial);
|
||||
for(auto &flag : regs.irq.enable.dma) s.integer(flag);
|
||||
s.integer(regs.irq.enable.keypad);
|
||||
s.integer(regs.irq.enable.cartridge);
|
||||
|
||||
s.integer(regs.irq.flag.vblank);
|
||||
s.integer(regs.irq.flag.hblank);
|
||||
s.integer(regs.irq.flag.vcoincidence);
|
||||
for(auto &flag : regs.irq.flag.timer) s.integer(flag);
|
||||
s.integer(regs.irq.flag.serial);
|
||||
for(auto &flag : regs.irq.flag.dma) s.integer(flag);
|
||||
s.integer(regs.irq.flag.keypad);
|
||||
s.integer(regs.irq.flag.cartridge);
|
||||
|
||||
for(auto &flag : regs.wait.control.nwait) s.integer(flag);
|
||||
for(auto &flag : regs.wait.control.swait) s.integer(flag);
|
||||
s.integer(regs.wait.control.phi);
|
||||
s.integer(regs.wait.control.prefetch);
|
||||
s.integer(regs.wait.control.gametype);
|
||||
|
||||
s.integer(regs.memory.control.disable);
|
||||
s.integer(regs.memory.control.unknown1);
|
||||
s.integer(regs.memory.control.ewram);
|
||||
s.integer(regs.memory.control.ewramwait);
|
||||
s.integer(regs.memory.control.unknown2);
|
||||
|
||||
s.integer(regs.postboot);
|
||||
s.integer((unsigned&)regs.mode);
|
||||
s.integer(regs.clock);
|
||||
|
||||
s.integer(pending.dma.vblank);
|
||||
s.integer(pending.dma.hblank);
|
||||
s.integer(pending.dma.hdma);
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
struct Pending {
|
||||
struct DMA {
|
||||
bool vblank;
|
||||
bool hblank;
|
||||
bool hdma;
|
||||
} dma;
|
||||
} pending;
|
@@ -1,44 +0,0 @@
|
||||
void CPU::timer_step(unsigned clocks) {
|
||||
for(unsigned c = 0; c < clocks; c++) {
|
||||
for(unsigned n = 0; n < 4; n++) {
|
||||
auto &timer = regs.timer[n];
|
||||
if(timer.control.enable == false || timer.control.cascade == true) continue;
|
||||
|
||||
static unsigned mask[] = { 0, 63, 255, 1023 };
|
||||
if((regs.clock & mask[timer.control.frequency]) == 0) {
|
||||
timer_increment(n);
|
||||
}
|
||||
}
|
||||
|
||||
regs.clock++;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::timer_increment(unsigned n) {
|
||||
auto &timer = regs.timer[n];
|
||||
if(++timer.period == 0) {
|
||||
timer.period = timer.reload;
|
||||
|
||||
if(timer.control.irq) regs.irq.flag.timer[n] = 1;
|
||||
|
||||
if(apu.fifo[0].timer == n) timer_fifo_run(0);
|
||||
if(apu.fifo[1].timer == n) timer_fifo_run(1);
|
||||
|
||||
if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) {
|
||||
timer_increment(n + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::timer_fifo_run(unsigned n) {
|
||||
apu.fifo[n].read();
|
||||
if(apu.fifo[n].size > 16) return;
|
||||
|
||||
auto &dma = regs.dma[1 + n];
|
||||
if(dma.control.enable && dma.control.timingmode == 3) {
|
||||
dma.pending = true;
|
||||
dma.control.targetmode = 2;
|
||||
dma.control.size = 1;
|
||||
dma.run.length = 4;
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
#ifndef GBA_HPP
|
||||
#define GBA_HPP
|
||||
|
||||
#include <base/base.hpp>
|
||||
#include <processor/arm/arm.hpp>
|
||||
|
||||
namespace GBA {
|
||||
namespace Info {
|
||||
static const char Name[] = "bgba";
|
||||
static const unsigned SerializerVersion = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bgba - Game Boy Advance emulator
|
||||
authors: byuu, Cydrak
|
||||
license: GPLv3
|
||||
project started: 2012-03-19
|
||||
*/
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
namespace GBA {
|
||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
||||
|
||||
struct Thread {
|
||||
cothread_t thread;
|
||||
unsigned frequency;
|
||||
signed 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 <gba/memory/memory.hpp>
|
||||
#include <gba/interface/interface.hpp>
|
||||
#include <gba/scheduler/scheduler.hpp>
|
||||
#include <gba/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
#include <gba/cpu/cpu.hpp>
|
||||
#include <gba/ppu/ppu.hpp>
|
||||
#include <gba/apu/apu.hpp>
|
||||
#include <gba/video/video.hpp>
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,21 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GBA {
|
||||
|
||||
Interface *interface = nullptr;
|
||||
|
||||
void Interface::videoRefresh(const uint16_t *data) {
|
||||
}
|
||||
|
||||
void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||
}
|
||||
|
||||
bool Interface::inputPoll(unsigned id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Interface::message(const string &text) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
struct Interface {
|
||||
virtual void videoRefresh(const uint16_t *data);
|
||||
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||
virtual bool inputPoll(unsigned id);
|
||||
|
||||
virtual void message(const string &text);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user